【问题标题】:How to preserve spaces after a CSV import using a powershell script?如何在使用 powershell 脚本导入 CSV 后保留空格?
【发布时间】:2021-04-11 21:23:46
【问题描述】:

我目前正在开发一个小型 PowerShell 脚本,该脚本应该将带有翻译的 CSV 文件拆分为相应语言的单个文件。为此,我使用Import-Csv cmdlet 导入源文件,其格式大致如下:

ResourceId;GermanTranslation;EnglishTranslation;Check
0;Deutscher Text;English text;OK
1;  mit Leerzeichen  ;  with spaces  ;OK

目标是以ResourceId|EnglishTranslation| 格式逐行表示翻译。为此,我构建了以下脚本:

Set-Variable SOURCE_FILE -Option Constant -Value ".\sourceFile.csv"
Set-Variable RESULT_FILE -Option Constant -Value ".\resultFile.csv"

foreach ($row in (Import-Csv -Path $SOURCE_FILE -Delimiter ";")) {
    Add-Content -Path $RESULT_FILE -Value ($row.RessourceId + "|" + $row.EnglishTranslation + "|")
}

基本上,一切都按预期工作,但是当我检查结果时,我注意到结果中不再存在某些元素开头的空格:

0|English text|
1|with spaces  |

很遗憾,我在the MS documentation 中没有找到解决此问题的参数,所以一开始我并不确定。 之后,我查看了RFC 4180,它或多或少准确地描述了 CSV 文件格式。它指出空格应该被视为字段的一部分,而不是被忽略。 应该意味着不是必须,所以很可能真的没有选择。

是否有可能保留空格而无需我自己解析整个文件?

【问题讨论】:

  • 您可以尝试 PowerShell 版本 7 中的 Import-Csv,因为它有更多选项。另一种选择可能是在使用输入 csv 将单元格内容括在引号中以根据需要保留空格之前处理您的输入 csv。 ;-)
  • 如果您可以控制上游文件(显然您可能没有:-S),那么您最好使用带引号的字符串生成它 - 例如1;" mit Leerzeichen ";" with spaces ";OK 确实Import-Csv一起保存。
  • 即使在 PS 7 中也没有参数来控制Import-Csv 的空白行为。您可以尝试使用 .NET 类,请参阅 stackoverflow.com/a/33796861/7571258

标签: powershell


【解决方案1】:

这是一个比替换 CSV 文件中的字符更健壮(并且可能更快)的解决方案。

它使用来自 Microsoft.VisualBasic 程序集的 .NET TextFieldParser 类。它有一个TrimWhiteSpace 属性,当设置为$false 时,保留每个字段的任何前导和尾随空格,即使该字段没有用双引号括起来。

我已将 .NET 代码封装在一个名为 Import-CustomCsv 的函数中。它通过各种参数支持TextFieldParser的一些附加选项。

Function Import-CustomCsv {
   [CmdletBinding()]
   param (
      [Parameter(Mandatory, ValueFromPipeline)] [String] $Path,
      [String[]] $Delimiter = ',',
      [String[]] $CommentTokens = '#',
      [switch] $TrimWhiteSpace,
      [switch] $HasFieldsEnclosedInQuotes
   )

   # Load Visual Basic assembly if necessary. Seems to be required only for PS 5.
   if (-not ([System.Management.Automation.PSTypeName]'Microsoft.VisualBasic.FileIO.TextFieldParser').Type) {
      Add-Type -AssemblyName 'Microsoft.VisualBasic, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
   }   

   # Create a CSV parser
   $csvParser = New-Object Microsoft.VisualBasic.FileIO.TextFieldParser $Path

   try {
      # Set CSV parser options
      $csvParser.SetDelimiters( $Delimiter )
      $csvParser.CommentTokens = $CommentTokens
      $csvParser.TrimWhiteSpace = $TrimWhiteSpace
      $csvParser.HasFieldsEnclosedInQuotes = $HasFieldsEnclosedInQuotes

      # Read the header
      $header = $csvParser.ReadFields()

      while( -not $csvParser.EndOfData ) {
         # Read current line fields, pointer moves to the next line.
         $fields = $csvParser.ReadFields()

         # Associate each field with its name from the header by storing it in an
         # ordered hashtable.
         $namedFields = [ordered]@{}
         for( $i = 0; $i -lt $header.Count; $i++ ) {
            $namedFields[ $header[ $i ] ] = $fields[ $i ]
         }

         # Finally convert fields to PSCustomObject and output (implicitly)
         [PSCustomObject] $namedFields
      }
   }
   finally {
      # Make sure to close the file even in case of an exception.
      $csvParser.Close()
   }
}

使用示例:解析 CSV,保留空格:

Import-CustomCsv test.csv -Delimiter ';'

输出:

ResourceId GermanTranslation   EnglishTranslation Check
---------- -----------------   ------------------ -----
0          Deutscher Text      English text       OK
1            mit Leerzeichen     with spaces      OK

用法示例:解析 CSV,修剪空白(如 Import-Csv):

Import-CustomCsv test.csv -Delimiter ';' -TrimWhiteSpace

输出:

ResourceId GermanTranslation EnglishTranslation Check
---------- ----------------- ------------------ -----
0          Deutscher Text    English text       OK   
1          mit Leerzeichen   with spaces        OK   

注意:

以上两个示例在输出中保留了包含字段的双引号(如果有)。要删除字段周围的双引号,请传递参数-HasFieldsEnclosedInQuotes

【讨论】:

  • 这是对原生 import-csv 的很好改进的 +1,应该标记为已解决。可悲的是,提出这个问题的人似乎并不欣赏。
  • 哇,一个非常棒的解决方案,带有令人愉快的详细示例!这实际上有效。 (抱歉,我直到现在才注意到答案 - 现在已标记为已解决)。
【解决方案2】:

这应该可以按您的意愿工作。试试看,让我知道。

基本上这是在做 @mclayton 在 cmets 中推荐的。

$content=Get-Content ./test.csv|%{
    '"{0}"' -f ($_ -replace ';','";"')
}|ConvertFrom-Csv -Delimiter ';'


PS /~> $content


ResourceId GermanTranslation   EnglishTranslation Check
---------- -----------------   ------------------ -----
0          Deutscher Text      English text       OK
1            mit Leerzeichen     with spaces      OK

【讨论】:

    【解决方案3】:

    避免默认修剪文本的一种方法是先将空格转换为另一个字符,然后再将其视为 CSV。不确定哪个字符最适合使用,但这里有一个使用下划线的示例:

    Get-Content $SOURCE_FILE | %{
        $_ -replace ' ', '_'
    } | ConvertFrom-Csv -Delimiter ';' | %{
        '{0}|{1}|' -f $_.ResourceId, $_.EnglishTranslation
    } | % {$_ -replace '_', ' '} | Out-File $RESULT_FILE 
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-08-05
      • 2018-10-08
      • 1970-01-01
      • 1970-01-01
      • 2012-03-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多