用于解析 CSV 的正则表达式
Posted
技术标签:
【中文标题】用于解析 CSV 的正则表达式【英文标题】:Regex for parsing CSV 【发布时间】:2012-07-01 00:04:47 【问题描述】:我正在尝试编写一个从 CSV 文件中提取单个字段的正则表达式。
例如,如果在 CSV 文件中给出以下行:
123, Bob ,Bob, " Foo Bar ", "a, ""b"", c"
应该给出以下结果(不带单引号):
'123'
'Bob'
'Bob'
' Foo Bar '
'a, "b", c'
请注意,除非在引号内,否则应修剪前导和尾随空格。
我不担心无效的 CSV 行,例如没有匹配右引号的开引号。根据上述规则,您可以放心地假设 CSV 文件完全有效。
如果单个正则表达式很困难,我也可以使用多个正则表达式。但我喜欢避免使用标准 C# 操作,除非它们简单而简短。 (我不想最终写很多代码。)
那么,有什么建议吗?
【问题讨论】:
为什么是正则表达式?只能用吗? 如果 CSV 完全有效,则引用字段中有换行符。 我认为“拆分”方法更适合这种情况。 我认为您关于 CSV 的“通用”概念是错误的。国际海事组织“通用”也会把报价还给你。 您正在尝试解决一个已经解决的问题 - 一次又一次 - 而且更优雅。您遇到的一些主要问题是您不了解“有效 CSV”是什么,并且确实有效的 CSV 数据集可以是多行的。 【参考方案1】:我不会尝试编写自己的 csv 解析器,那里有很多可以为您完成这项工作。
http://www.filehelpers.com/ http://coding.abel.nu/2012/06/built-in-net-csv-parser/【讨论】:
如果有可用的简单解决方案,我宁愿不必安装第三方库。但我会研究您第二个链接中提到的内置 Microsoft.VisualBasic.FileIO.TextFieldParser。【参考方案2】:嗯,正则表达式可能存在许多陷阱和错误...尝试按照它对我有用的代码,它既甜美又简单...
Using Reader As New Microsoft.VisualBasic.FileIO.TextFieldParser("C:\MyFile.csv")
Reader.TextFieldType = Microsoft.VisualBasic.FileIO.FieldType.Delimited
Dim MyDelimeters(0 To 0) As String
Reader.HasFieldsEnclosedInQuotes = False
Reader.SetDelimiters(","c)
Dim currentRow As String()
While Not Reader.EndOfData
Try
currentRow = Reader.ReadFields()
Dim currentField As String
For Each currentField In currentRow
MsgBox(currentField)
Next
Catch ex As Microsoft.VisualBasic.FileIO.MalformedLineException
MsgBox("Line " & ex.Message &
"is not valid and will be skipped.")
End Try
End While
End Using
如果发现方便,标记为答案...;)
请在此处查看相同的implementation,,,
【讨论】:
我使用的是 C#,但我似乎找不到 Microsoft.VisualBasic.FileIO 命名空间。有没有我需要导入的参考资料? C# 你应该只添加“使用 System.IO;”这将为您提供读者和作者以及文件访问权限。 System.IO 没有 TextFieldParser 类。我在问我需要什么参考才能获得该课程。 哟,我有你的问题......更新了我的答案。 @Jigar 感谢您的帮助。 TextFieldParser 拥有我需要的一切......除了一件事:如果针对我上面的示例数据使用,字段“Foo Bar”返回“Foo Bar”而不是“Foo Bar”。但是如果我将 TrimWhiteSpace 设置为 false,那么像 Bob 这样的其他字段将不会修剪它们的空间......有什么想法吗?【参考方案3】:我同意正则表达式不是“正确”的答案,但这是问题所要求的,我喜欢一个好的正则表达式挑战。
下面的模式是我的standard CSV parsing regex 的修改版本,它删除了空格并假设 CSV 符合您的要求。您的问题中唯一没有解决的部分是它不会删除转义/双引号。在模式之后给出了取消转义引号的示例。
当 CSV 文件/流的一个或多个行/记录被传递到下面的正则表达式时,它将为每个非空行/记录返回一个匹配项。每个匹配项都将包含一个名为 Value
的捕获组,其中包含该行/记录中的捕获值。
这是注释模式(测试它on Regexstorm.net):
(?<=\r|\n|^)(?!\r|\n|$) // Records start at the beginning of line (line must not be empty)
(?: // Group for each value and a following comma or end of line (EOL) - required for quantifier (+?)
[^\S\r\n]* // Removes leading spaces
(?: // Group for matching one of the value formats before a comma or EOL
"(?<Value>(?:[^"]|"")*)"| // Quoted value -or-
(?<Value>[^,\r\n]+)| // Unquoted/open ended quoted value -or-
(?<Value>) // Empty value before comma (before EOL is excluded by "+?" quantifier later)
)
[^\S\r\n]* // Removes trailing spaces
(?:,|(?=\r|\n|$)) // The value format matched must be followed by a comma or EOL
)+? // Quantifier to match one or more values (non-greedy/as few as possible to prevent infinite empty values)
(?:(?<=,)(?<Value>))? // If the group of values above ended in a comma then add an empty value to the group of matched values
(?:\r\n|\r|\n|$) // Records end at EOL
这是没有所有 cmets 或空格的原始模式。
(?<=\r|\n|^)(?!\r|\n|$)(?:[^\S\r\n]*(?:"(?<Value>(?:[^"]|"")*)"|(?<Value>[^,\r\n]+)|(?<Value>))[^\S\r\n]*(?:,|(?=\r|\n|$)))+?(?:(?<=,)(?<Value>))?(?:\r\n|\r|\n|$)
而且,这是 C# 转义版本。
String CSVPattern=
@"(?<=\r|\n|^)(?!\r|\n|$)" +
@"(?:" +
@"[^\S\r\n]*" +
@"(?:" +
@"""(?<Value>(?:[^""]|"""")*)""|" +
@"(?<Value>[^,\r\n]+)|" +
@"(?<Value>)" +
@")" +
@"[^\S\r\n]*" +
@"(?:,|(?=\r|\n|$))" +
@")+?" +
@"(?:(?<=,)(?<Value>))?" +
@"(?:\r\n|\r|\n|$)";
关于如何使用正则表达式模式的示例(好吧,可以用这种模式替换的原始模式)可以在我对类似问题here、C# pad here 或here 的回答中找到。
注意:上面的示例包含取消转义/取消双引号的逻辑,如下所示:
if (Capture.Length == 0 || Capture.Index == Record.Index || Record.Value[Capture.Index - Record.Index - 1] != '\"')
// No need to unescape/undouble quotes if the value is empty, the value starts
// at the beginning of the record, or the character before the value is not a
// quote (not a quoted value)
Console.WriteLine(Capture.Value);
else
// The character preceding this value is a quote
// so we need to unescape/undouble any embedded quotes
Console.WriteLine(Capture.Value.Replace("\"\"", "\""));
【讨论】:
我会为你在这个庞大的正则表达式中所做的工作量 +1 ! :P @SF Lee 谢谢!实际上,我一直担心这个答案会引起任何关注,因为担心它会因为作为正则表达式而被否决而被遗忘。至少现在它可以承受五次这样的投票而不会对声誉产生负面影响。 :)【参考方案4】:您可以使用 .NET 框架中内置的 TextFieldParser 类。
为了在您的 C# 应用程序中使用该类,您需要在以下位置添加 Microsoft.VisualBasic.dll
的引用(假设您进行了默认设置安装)
C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\Microsoft.VisualBasic.dll
现在在你的 C# 类中有以下 using 语句:
using Microsoft.VisualBasic.FileIO
【讨论】:
以上是关于用于解析 CSV 的正则表达式的主要内容,如果未能解决你的问题,请参考以下文章