【问题标题】:CSV-parsing regular expression performanceCSV 解析正则表达式性能
【发布时间】:2013-07-19 18:23:44
【问题描述】:

我正在使用正则表达式来解析类似 CSV 的文件。我是正则表达式的新手,虽然它可以工作,但当有很多字段并且其中一个字段包含很长的值时,它会变得很慢。如何优化它?

我必须解析的 CSV 具有以下风格:

  1. 所有字段都是用逗号分隔的引号括起来的字符串
  2. 字段内的引号以两个连续引号的形式转义
  3. 在某些行的开头有不可预知的垃圾需要被忽略(到目前为止它不包含引号,谢天谢地)
  4. 可以使用零长度字段和字段中的换行符

我正在使用 VB.NET。我正在使用以下正则表达式:

(^(?!").+?|^(?="))(?<Entry>"(",|(.*?)"(?<!((?!").("")+)),))*(?<LastEntry>"("$|(.*?)"(?<!((?!").("")+))$))

我通过将 StreamReader.ReadLine's 输入字符串变量来处理换行符,直到正则表达式成功,用空格替换换行符(这对于我的目的来说是可以的)。 然后我使用 Match.Groups("Entry").Captures 和 Match.Groups("LastEntry") 提取字段内容。

我想性能影响来自对转义引号的后视。有没有更好的办法?

感谢您的任何想法!

【问题讨论】:

  • 您可以构建一个简单的解析器,它 (a) 比任何正则表达式解决方案都要快,(b) 更具可读性,(c) 更易于调试。

标签: .net regex csv


【解决方案1】:

我认为您的正则表达式不必要地复杂,嵌套量词导致catastrophic backtracking。请尝试以下操作:

^[^"]*(?<Entry>(?>"(?>[^"]+|"")*"),)*(?<LastEntry>(?>"(?>[^"]+|"")*"))$

说明:

^                 # Start of string
[^"]*             # Optional non-quotes
(?<Entry>         # Match group 'entry'
 (?>              # Match, and don't allow backtracking (atomic group):
  "               # a quote
  (?>             # followed by this atomic group:
   [^"]+          # one or more non-quote characters
  |               # or
   ""             # two quotes in a row
  )*              # repeat 0 or more times.
  "               # Then match a closing quote
 )                # End of atomic group
 ,                # Match a comma
)*                # End of group 'entry'
(?<LastEntry>     # Match the final group 'lastEntry'
 (?>              # same as before
  "               # quoted field...
  (?>[^"]+|"")*   # containing non-quotes or double-quotes
  "               # and a closing quote
 )                # exactly once.
)                 # End of group 'lastEntry'
$                 # End of string

这也应该适用于整个文件,因此在正则表达式匹配之前,您不必在下一行之后添加一行,并且您不必替换换行符:

Dim RegexObj As New Regex("^[^""]*(?<Entry>(?>""(?:[^""]+|"""")*""),)*(?<LastEntry>(?>""(?:[^""]+|"""")*""))$", RegexOptions.Multiline)
Dim MatchResults As Match = RegexObj.Match(SubjectString)
While MatchResults.Success
    ' now you can access MatchResults.Groups("Entry").Captures and
    ' MatchResults.Groups("LastEntry")
    MatchResults = MatchResults.NextMatch()
End While

【讨论】:

  • 哇。这在各个方面都更加优雅。我想在查看参考资料后我明白了(我不知道否定结构);我现在去测试一下。非常感谢您的回答和解释!
  • 到目前为止,在不匹配的字符串上创建 Match 对象时,执行会冻结。我试图找出原因,还没有尝试过多线方法。任何想法为什么现在会发生这种情况?
  • 当我在字段内的非捕获组中将[^"]+ 替换为[^"](删除了+)时,解冻(并且与我的初始正则表达式相比变得近乎即时)。不过,我有点迷茫为什么它会结冰:-)
  • 试试用(?&gt;s替换所有(?:s而不是删除+会发生什么;那应该会更快。
猜你喜欢
  • 1970-01-01
  • 2012-05-18
  • 1970-01-01
  • 2012-07-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-18
  • 2012-10-17
相关资源
最近更新 更多