【问题标题】:Powershell script to remove double quotes from CSV unless comma exists inside double quotes用于从 CSV 中删除双引号的 Powershell 脚本,除非双引号中存在逗号
【发布时间】:2015-07-26 15:21:58
【问题描述】:

我有一个以下文件格式的 .csv:

In: "bob","1234 Main St, New York, NY","cool guy"

我希望删除内部没有逗号的双引号:

Out: bob,"1234 Main St, New York, Ny",cool guy

有没有办法在 Powershell 中做到这一点?

我已经检查过:

  1. How to remove double quotes on specific column from CSV file using Powershell script
  2. http://blogs.technet.com/b/heyscriptingguy/archive/2011/11/02/remove-unwanted-quotation-marks-from-csv-files-by-using-powershell.aspx
  3. https://social.technet.microsoft.com/Forums/windowsserver/en-US/f6b610b6-bfb2-4140-9529-e61ad30b8927/how-to-export-csv-without-doublequote?forum=winserverpowershell

【问题讨论】:

    标签: regex powershell csv


    【解决方案1】:

    改编自"How to remove double quotes on specific column from CSV file using Powershell script"的代码:

    $csv = 'C:\path\to\your.csv'
    (Get-Content $csv) -replace '(?m)"([^,]*?)"(?=,|$)', '$1' |
        Set-Content $csv
    

    正则表达式 (?m)"([^,]*?)"(?=,|$) 匹配任何 " + 0 or more non-commas + " before 逗号或行尾(通过正向预读和强制 $ 匹配的多行选项 (?m) 实现换行符,而不仅仅是字符串的结尾)。

    见regex demo

    【讨论】:

    • 感谢您的所有帮助,Wiktor - 我已经清理了我以前的 cmets。最后的想法:如果您使用Get-Content -Raw(PSv3+)将整个文件读入单个字符串,您可以充分利用(?m),这也将加快速度。但是,为了避免额外的尾随换行符,必须使用Set-Content -NoNewline 写入输出 - 这是 PSv5+:(Get-Content -Raw $csv) -replace '(?m)"([^,]*?)"(?=,|$)', '$1' | Set-Content -NoNewline $csv
    【解决方案2】:

    我不知道你脚本的其余部分到底是什么样子。尝试一下这些方面的东西

    (("bob","1234 Main St, New York, NY","cool guy") -split '"' | 
      ForEach-Object {IF ($_ -match ",") {'"' + $_ + '"' } ELSE {$_}}) -join ","
    

    【讨论】:

      【解决方案3】:

      更新:在 PowerShell [Core] v7+ 中,可以使用更简单的解决方案:请参阅 this answer。


      现有答案适用于示例输入:

      • Wiktor Stribiżew's helpful answer 使用正则表达式标识不包含 , 的双引号字段,它首先将整个输入文件加载到内存中,这样就可以在单个管道中用结果替换输入文件。
        虽然这比逐行处理更方便且更快,但需要注意的是它可能不适用于大型输入文件。
      • markg's helpful answer,通过" 字符将行拆分为字段,是大型输入文件的替代方案,因为它使用管道逐一处理输入行。
        (因此,输入文件不能直接用结果更新。)

      如果我们概括 OP 的要求也处理带有嵌入" 字符的字段。,我们需要一种不同的方法:

      以下字段必须保留其封闭的双引号:

      • (必须)嵌入, 字符的双引号字段。例如,
        "1234 Main St, New York, NY"
      • (必须)嵌入" 字符的双引号字段,根据RFC 4180,必须将其转义为"",即加倍;例如,
        "Nat ""King"" Cole"

      注意:

      • 我们不处理可能包含嵌入换行符的字段,因为这需要一种根本不同的方法,因为独立的逐行处理是那就不可能了。

      • 向Wiktor Stribiżew 致敬,他提出了正则表达式来稳健地匹配双引号字段和任意数量的嵌入式双引号,转义为"":"([^"]*(?:""[^"]*)*)"

        创建包含双引号字段的示例 CSV 文件

        只是 ',',只是嵌入双引号 ('""'),两者都有。

        @' bob,"1234 Main St, New York, NY","酷哥" nat,"Nat""King""Cole Lane","酷歌手" nat2,"Nat ""King"" Cole Lane, NY","酷歌手" '@ |设置内容./test.csv

        获取内容 ./test.csv | ForEach-对象 { # 匹配行上所有双引号的字段,并替换那些 # 既不包含逗号也不包含仅包含其内容的嵌入双引号, # 即去掉封闭的双引号。 ([regex] '"([^"](?:""[^"])*)"').Replace($_, { param($match) $fieldContent = $match.Groups[1] if ($fieldContent -match '[,"]') { $match } else { $fieldContent } }) }

      这会产生:

      bob,"1234 Main St, New York, NY",cool guy
      nat,"Nat ""King"" Cole Lane",cool singer
      nat2,"Nat ""King"" Cole Lane, NY",cool singer
      

      更新输入文件:

      与markg的回答一样,由于逐行处理,您无法直接使用同一管道中的输出更新输入文件。
      要稍后更新 iput 文件,请使用临时输出文件,然后用它替换输入文件(... 表示上面的 Get-Content 管道,仅使用 $csvFile 而不是 ./test.csv):

      $csvfile = 'c:\path\to\some.csv'
      $tmpFile = $env:TEMP\tmp.$PID.csv
      ... | Set-Content $tmpFile
      if ($?) { Move-Item -Force $tmpFile $csvFile }
      

      请注意,Set-Content 默认使用系统的单字节扩展 ASCII 字符编码(即使 help topic falsely states ASCII 也是如此)。

      使用-Encoding 参数允许您指定不同的编码,但请注意,UTF-16LE 是Out-File / > 的默认值,它会导致 Excel 无法正确识别 CSV 文件,例如.

      【讨论】:

        【解决方案4】:

        在 PowerShell [Core] v7+ 中,您现在可以将 -UseQuotes AsNeeded 与 ConvertTo-Csv 和 Export-Csv 一起使用,这大大简化了方法:

        @'
        Name,Address,Comment
        "bob","1234 Main St, New York, NY","cool guy"
        '@ | ConvertFrom-Csv | 
               ConvertTo-Csv -UseQuotes AsNeeded # use Export-Csv to save to a file.
        

        上面的结果如下,表明只有嵌入了 , 的字段被双引号:

        Name,Address,Comment
        bob,"1234 Main St, New York, NY",cool guy
        

        见Export-Csv v7+ docs:

        • -UseQuotes 接受 AsNeeded、Always 和 Newer

        • 另外,还有-QuoteFields,它接受一个列(属性)名称数组,应该(选择性地应用)引用。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-03-18
          • 1970-01-01
          • 2017-02-13
          • 1970-01-01
          • 2016-01-18
          • 2018-07-27
          • 1970-01-01
          • 2016-09-26
          相关资源
          最近更新 更多