【问题标题】:Change pipe delimited file to comma delimited in Powershell在Powershell中将管道分隔文件更改为逗号分隔
【发布时间】:2020-02-02 04:23:36
【问题描述】:

我有一个以竖线分隔的 .TXT 文件。我需要将分隔符更改为逗号,但仍将文件扩展名保留为 .TXT。该文件如下所示:

第 1 列 |第 2 列

13|2019-09-30

96|2019-09-26

173|2019-09-25

我的脚本使用的是 Windows Powershell 5.1 版本。

我正在使用以下代码:

$file = New-Object System.IO.StreamReader -Arg "c:\file.txt"
$outstream = [System.IO.StreamWriter] "c:\out.txt"
while ($line = $file.ReadLine()) {
$s = $line -replace '|', ','
$outstream.WriteLine($s)
}
$file.close()
$outstream.close()

输出文件不只是用逗号替换管道,而是如下所示:

C,o,l,u,m,n, 1 , |,C,o,l,u,m,n, 2

1,3,|,2,0,1,9,-,0,9,-,3,0

9,6,|2,0,1,9,-,0,9,-,2,6

1,7,3,|,2,0,1,9,-,0,9,-,2,5

【问题讨论】:

    标签: powershell


    【解决方案1】:

    您会发现-split-join 运算符很有趣。

    Get-Content -Path "C:\File.TXT" | ForEach-Object { ($_ -split "\|") -join "," } | Set-Content -Path "C:\Out.TXT"
    

    【讨论】:

    • 投了反对票,因为@Jacob 之前提供的答案更有效
    • @StanislavCastek - 如果您尝试 Jacob 的解决方案按书面形式,将会出现问题 - 至少,在文件顶部生成了一条虚假行。虽然在结构上,查询者发布的内容似乎符合沼泽标准 CSV,但我从不假设发布的样本数据与现实完全一致;如果文件中的其他行与两列 CSV 模型不匹配,Import-CSV/Export-CSV 模型将失败 - 而我的模型不会。 如果原始文件完全符合 CSV 标准,如示例所示,我承认 Jacob 的解决方案更快。
    • @StanislavCastek - 如果询问者明确声明原始文件是 CSV(无论文件名如何),我会提出 Jacob 的解决方案并进行必要的更正。
    • 同理 (+1):即使您使用 -NoTypeInformation 开关,也存在另一个潜在的复杂情况(如果输入文件格式正确并且所有这些都无关紧要)是输出是否是有效的 CSV 文件):Export-Csv 将始终双引号所有字段值。
    【解决方案2】:

    您可以通过指定-Delimiter 来使用Import-CsvExport-Csv

    Import-Csv -Delimiter '|' -Path "c:\file.txt" | Export-Csv -Delimiter ',' -Path "c:\file.txt" -NoTypeInformation
    

    【讨论】:

    • Export-CSV 将在文件顶部生成一个虚假行,除非您包含 -NoTypeInformation 开关。但也请参阅我对@StanislavCastek 的评论以及我的回答。
    • 谢谢! (我避免这种方式,因为它是一个巨大的文件,这需要一段时间才能完成。)
    • @JeffZeitlin,你是对的,我总是忘记包含 -NoTypeInformation。我已经更新了答案以包含它。 @Arijita,这是一个公平的观察,AFAIK Import-CsvExport-Csv 将其转换回 CSV 之前将其解析为内存中的对象。所以认为这是一种简单的方法,但绝对不是最高效的方法。
    【解决方案3】:

    您回答的唯一问题在于您如何尝试替换输入中的 | 字符:

    $s = $line -replace '|', ',' # WRONG

    PowerShell 的 -replace 运算符期望 regex(正则表达式) 作为其第一个 RHS 操作数,而 | 是一个正则表达式元字符(具有特殊含义)[1];要将其用作文字字符,您必须\-escape它:

    # '\'-escape regex metacharacter '|' to treat it literally.
    $s = $line -replace '\|', ','
    

    虽然PowerShell's -replace operator 非常灵活,在这种简单的情况下,您可以选择使用[string] 类型的.Replace() 方法,它执行文字字符串替换,因此不需要转义(它也比-replace 快):

    # Use literal string replacement.
    # Note: .Replace() is case-*sensitive*, unlike -replace
    $s = $line.Replace('|', ',')
    

    [1] | 表示正则表达式中的 alternation,这意味着两边的子表达式都与输入字符串匹配,其中一个匹配就足够了;如果您的完整正则表达式只是|,它有效地匹配输入中每个字符前后的空字符串,这解释了您的症状;例如,'foo' -replace '|', '@' 产生 @f@o@o@

    【讨论】:

    • 这是一个很好的解释,在我的回答中也适用于-split
    • 谢谢,@JeffZeitlin;是的,你的-split / -join 组合肯定也有效,关于-replace.Replace() 的要点类似地适用于-split.Split()
    • 谢谢!逃避是有道理的,而且效果很好。我也尝试了 .Replace(),它比 -replace 快了几秒钟。
    • 我很高兴听到这个消息,@Arijita。请允许我在下一条评论中给你标准的建议给新人:
    • 如果某个答案解决了您的问题,请通过单击旁边的大复选标记 (✓) 接受它,并可选择对其进行投票(投票至少需要 15 个声望点)。如果您发现其他答案有帮助,请给他们投票。接受(您将获得 2 个声望点)和投票可以帮助未来的读者。有关更多信息,请参阅this article。如果您的问题尚未得到完全解答,请提供反馈或self-answer
    猜你喜欢
    • 2019-05-21
    • 2015-04-21
    • 2022-11-17
    • 2016-09-05
    • 2020-07-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多