Arrays 分析带引号字段的字符串,如Powershell中的CSV行

Arrays 分析带引号字段的字符串,如Powershell中的CSV行,arrays,powershell,csv,Arrays,Powershell,Csv,我必须将变量输入字符串解析为字符串数组。 输入是CSV样式的逗号分隔字段列表,其中每个字段都有自己的带引号的字符串。 因为我不想编写自己完整的CSV解析器,所以到目前为止,我唯一可以创建的工作解决方案是: $input = '"Miller, Steve", "Zappa, Frank", "Johnson, Earvin ""Magic"""' Add-Type -AssemblyName M

我必须将变量输入字符串解析为字符串数组。 输入是CSV样式的逗号分隔字段列表,其中每个字段都有自己的带引号的字符串。 因为我不想编写自己完整的CSV解析器,所以到目前为止,我唯一可以创建的工作解决方案是:

$input = '"Miller, Steve", "Zappa, Frank", "Johnson, Earvin ""Magic"""'

Add-Type -AssemblyName Microsoft.VisualBasic
$enc = [System.Text.Encoding]::UTF8
$bytes = $enc.GetBytes($input)
$stream = [System.IO.MemoryStream]::new($bytes)
$parser = [Microsoft.VisualBasic.FileIO.TextFieldParser]::new($stream)
$parser.Delimiters = ','
$parser.HasFieldsEnclosedInQuotes = $true
$list = $parser.ReadFields()

$list
输出如下所示:

Miller, Steve
Zappa, Frank
Johnson, Earvin "Magic"
是否有更好的解决方案可通过另一个.NET库用于Powersell? 在最好的情况下,我可以避免这个额外的字节数组和流。 我也不确定这个视觉基础组件是否会长期有效


有什么想法吗?

为了安全和防止无意中的字符串外推,您可以与结合使用,不过请注意:

$fieldList='“米勒,史蒂夫”,“扎帕,弗兰克”,“约翰逊,埃尔文”,“魔术”,“亲爱的,我是$HOME”
#解析为数组。
$fields=(
调用表达式(“写入输出”+($fieldList-替换“\$”,“`0”))
)-替换“`0”,“$$”
注:

  • -替换“\$”,“`0”
    临时替换文字
    $
    字符。在具有NUL字符的输入中。防止意外(或恶意);第二个
    -replace
    操作恢复原始
    $
    字符。
    有关基于正则表达式的
    -replace
    运算符的更多信息,请参阅

  • 如果仅当输入字符串保证不包含嵌入的
    $
    字符时,解决方案可以简化为:

    $fields = Invoke-Expression "Write-Output $fieldList" 
    
输出
$fields
会产生以下结果:

史提夫·米勒 扎帕,弗兰克 约翰逊,埃尔文“魔术” 亲爱的,我回家了
说明和约束列表

解决方案依赖于使输入字符串成为字符串的一部分,该字符串的内容在语法上是有效的
Write-Output
调用,输入字符串用作后者的参数
Invoke Expression
然后对该字符串求值,就好像它的内容是作为命令直接提交的一样,并因此执行
Write Output
命令。根据PowerShell解析命令参数的方式,这意味着以下约束:

  • 支持的字段分隔符:

    • 或者:
      -分隔的
      (删除每个字段(无引号)的前导和/或尾随空格,如上所示)

    • 或者:空格分隔,在字段之间使用一个或多个空格字符

  • 不引用嵌入字段

    • 可以引用以下字段:

      • 如果单引号(
        “…”
        ),字段内部
        字符必须作为
        转义

      • 如果双引号,字段内部的
        字符必须作为
        `
        转义

    • 字段也可以不带引号:

      • 但是,此类字段不得包含任何PowerShell参数模式元字符(其中,
        <>@#
        仅是令牌开头的元字符):


        如果列表有限,则可以使用cmdlet的解析器,如:

        $List = '"Miller, Steve", "Zappa, Frank", "Johnson, Earvin ""Magic""", "Honey, I''m $HOME"'
        ($List | ConvertFrom-Csv -Header (0..99)).PSObject.Properties.Value.Where{ $Null -ne $_ }
        Miller, Steve
        Zappa, Frank
        Johnson, Earvin "Magic"
        Honey, I'm $HOME
        

        +1对于这个想法,尽管
        -Header(0..99)
        部分有点笨拙。一个简单的解决方案是提供两次输入字符串:
        ($List,$List | ConvertFrom Csv).psobject.Properties.Value
        提交两次输入字符串对于解决未知数量的列名来说是非常明智的
        $List = '"Miller, Steve", "Zappa, Frank", "Johnson, Earvin ""Magic""", "Honey, I''m $HOME"'
        ($List | ConvertFrom-Csv -Header (0..99)).PSObject.Properties.Value.Where{ $Null -ne $_ }
        Miller, Steve
        Zappa, Frank
        Johnson, Earvin "Magic"
        Honey, I'm $HOME