我不太擅长Regex,我的任务是使用Powershell将csv加载到数据表中。 csv的值用引号引起来,用逗号分隔。麻烦的是,某些行由列值组成,这些值本身包含引号。
$csvSplit = "($csvdelimiter)"
$csvSplit += '(?=(?:[^"]|"[^"]*")*$)'
$regexOptions = [System.Text.RegularExpressions.RegexOptions]::ExplicitCapture
引发这种情况的行中包含值,其中值可能会说诸如3-1/8"
之类的值。我也是Powershell的新手,但我真的不确定如何更改正则表达式以使其忽略这些情况。
非常感谢任何解释和帮助!
尝试这些解决方案似乎并不能完全解决问题,只是将问题转移到了另一个地方。我被认为是CSV本身的问题,但我还没有找到格式错误的例子。这些答案是很好的答案,我希望将来有人能从阅读本文和它的出色答案中受益。谢谢大家。
答案 0 :(得分:3)
因为"
个字符。您的字段中的嵌入是不可逃脱:
您不能可靠地使用Import-Csv
(或ConvertFrom-Csv
)。
"
字符。要正确解析,它们必须表示为""
(加倍)。需要进行手动解析,这只有在您进行假设时才有效。
如果可以假设嵌入(场内)"
永远不会紧跟,
,则可以尝试以下方法(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
注意事项:考虑到必须为每个输入行执行一个脚本块,这种解决方案相对来说 slow 比较慢。
注意:
正则表达式'"(.*?)"(?:,|$)'
非贪婪地*?
与封闭的"
匹配,只要结束"
后紧跟着{{1} }或(,
)行(|
)的结尾。
$
内的.*?
括起来(捕获组),使(...)
实例之间的字符串(即原始字段值)可用作第二个元素(索引{{1} })"
返回的匹配对象的1
属性.Groups
中的[regex]::Matches()
表示非捕获组,之所以选择该组是因为以后不需要访问该组匹配的内容。除了发信号通知稍后关注哪些组之外,这还使正则表达式的效率更高。 ?:
输出所有原始字段值,并将它们保存为变量(?:,|$)
中的数组。
.ForEach({ $_.Groups[1].Value })
和$fieldValues
定义带有顺序键的辅助哈希表,并为第一个输入行的字段值创建(最初为空)条目,这些条目假定为列名; $ohtAux = [ordered] @{}
然后将哈希表转换为自定义对象,该对象将作为对象的模板输出,以跟随数据行。
答案 1 :(得分:1)
假设以逗号作为分隔符,这应该可以解决问题:
((Get-Content '.\split.txt' -raw) -split '"?,"?|^"|"$' -ne '')
,
前后使用可选的"
进行了拆分-ne
运算符。注意事项:如果报价不是平衡的双引号对的一部分,则可能会丢失报价。
答案 2 :(得分:0)
您需要正则表达式吗?出于某种原因,内置Powershell CSV转换器对您不起作用?
$csv = Get-Content .\split.txt | ConvertFrom-CSV
或类似的东西。我建议您从一个小于32Gb的文件开始测试您的方法。正如其他人提到的那样,引用数据存在很多陷阱,但是只要您的输入格式正确,并且您愿意等待PowerShell读取32Gb,这可能对您有用。