我必须将变量输入字符串解析为字符串数组。
输入是 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"
Powersell 的另一个 .NET 库是否有更好的解决方案?在最好的情况下,我可以避免这种额外的字节数组和流。
我也不确定这个 VisualBasic-Assembly 是否能长期使用。
这里有什么想法吗?
最佳答案
通过一些额外的安全预防措施并防止意外的字符串外推,您可以结合使用 Invoke-Expression
与 Write-Output
,但请注意 Invoke-Expression
should generally be avoided :
$fieldList = '"Miller, Steve", "Zappa, Frank", "Johnson, Earvin ""Magic""", "Honey, I''m $HOME"'
# Parse into array.
$fields = (
Invoke-Expression ("Write-Output " + ($fieldList -replace '\$', "`0"))
) -replace "`0", '$$'
笔记:-replace '\$', "`0"
暂时替换文字 $
字符。在带有 NUL 字符的输入中。防止意外(或恶意)string expansion (interpolation) ;第二个 -replace
操作恢复原状$
字符。见 this answer有关基于正则表达式的更多信息
-replace
运算符(operator)。$
时字符 ,解决办法可以是简化版 到:$fields = Invoke-Expression "Write-Output $fieldList"
输出
$fields
产生以下结果:Miller, Steve
Zappa, Frank
Johnson, Earvin "Magic"
Honey, I'm $HOME
约束说明和列表 :
该解决方案依赖于将输入字符串作为内容在语法上有效的字符串的一部分
Write-Output
调用,输入字符串作为后者的参数。 Invoke-Expression
然后评估这个字符串,就好像它的内容已经作为命令直接提交一样,因此执行 Write-Output
命令。根据 PowerShell 解析命令参数的方式,这意味着以下约束:,
-分隔 (每个字段(未加引号)前导和/或尾随空格被删除,如上所示)。'...'
), 字段内部 '
字符必须转义为 ''
."
字符必须转义为 ""
或 `"
.< > @ #
只是标记开头的元字符): <space> ' " ` , ; ( ) { } | & < > @ #
替代方法,通过
ConvertFrom-Csv
:iRon's helpful answer显示了基于
ConvertFrom-Csv
的解决方案,假设输入字符串中嵌入的字段列表是逗号分隔的 ( ,
):"..."
-引用字段和 ""
- 字段内部的转义"
, 并且不支持由不同数量的空格分隔的字段(仅)。-Delimiter
指定。范围。使解决方案尴尬的是需要预测最大值。嵌入字段的数量并为它们提供虚拟标题(列名)(
-Header (0..99)
),以便使 ConvertFrom-Csv
工作,这既脆弱又可能浪费。然而,一个简单的技巧可以绕过这个问题:提交输入字符串两次,在这种情况下
ConvertFrom-Csv
将输入字符串中的字段视为列名和唯一一个输出行(对象)的列值,然后可以查询其值:$fieldList = '"Miller, Steve", "Zappa, Frank", "Johnson, Earvin ""Magic""", "Honey, I''m $HOME"'
# Creates the same array as the solution at the top.
$fields = ($fieldList, $fieldList | ConvertFrom-Csv).psobject.Properties.Value
关于arrays - 解析带有引用字段的字符串,如 Powershell 中的 CSV 行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67422853/