因为"
字符。嵌入在您的字段中不会被转义:
如果可以假设embedded (field-interior)永远不会"
直接跟随,
,您可以尝试以下方法(PSv4+):
# Sample array of CSV lines.
# Note that some fields have unescaped internal " chars.
$csv = @'
"col1","col2"
"one","3-1/0""
"normal","line"
"3-1/1"","two"
"3" of rain","today"
'@ -split '\r?\n'
$lineNo = 0
# Process the CSV lines one by one.
# Note: Replace `$csv |` with `Get-Content yourFile.csv`
$csv | ForEach-Object {
# Extract the field values based on the assumption above.
$fieldValues = ([regex]::Matches($_, '"(.*?)"(?:,|$)')).ForEach({ $_.Groups[1].Value })
if (++$lineNo -eq 1) { # 1st == header line
# Create an object *template* with the 1st line's field values as
# property names.
$propNames = $fieldValues
$ohtAux = [ordered] @{}
foreach ($propName in $propNames) { $ohtAux[$propName] = $null }
$objTemplate = [pscustomobject] $ohtAux
} else { # 2nd and subsequent lines: data lines
# Clone the template object.
$obj = $objTemplate.psobject.Copy()
# Fill the clone's properties with the field values.
$i = 0
foreach ($propName in $propNames) { $obj.$propName = $fieldValues[$i++] }
# Output the clone.
$obj
}
}
以上产生:
col1 col2
---- ----
one 3-1/0"
normal line
3-1/1" two
3" of rain today
警告:此解决方案相对较慢,因为必须为每个输入行执行一个脚本块。
笔记:
正则表达式'"(.*?)"(?:,|$)'
非贪婪地*?
匹配"
-enclosed 值,只要关闭"
后紧跟 a,
或 ( |
) 行尾 ( $
)。
- 包含
.*?
在(...)
- 捕获组中 - 使"
实例之间的字符串(即原始字段值)可用作匹配对象返回1
的属性的第二个元素(索引).Groups
[regex]::Matches()
- 请注意,
?:
in(?:,|$)
表示非捕获组,选择该组是因为以后不需要访问组匹配的内容。除了发出以后感兴趣的组的信号外,这使正则表达式的效率稍高一些。
- 注意:wp78de 的有用答案显示了一种更简单、更快捷的方法,该方法基于使用正则表达式来匹配分隔符而不是字段值,它直接返回原始字段值。
.ForEach({ $_.Groups[1].Value })
因此输出所有原始字段值并将它们保存为变量中的数组$fieldValues
。
$ohtAux = [ordered] @{}
并foreach ($propName in $propNames) { $ohtAux[$propName] = $null }
定义一个带有有序键的辅助哈希表,并为第一输入行的字段值创建(最初为空)条目,这些值被假定为列名;[pscustomobject] $ohtAux
然后将哈希表转换为自定义对象,该对象用作要输出的对象的模板,以供随后的数据行使用。