2013-07-19 6 views
2

Я использую регулярное выражение для анализа CSV-подобного файла. Я новичок в регулярных выражениях, и, хотя он работает, он замедляется, когда есть много полей, и одно из полей содержит очень длинное значение. Как я могу его оптимизировать?CSV-синтаксический анализ регулярных выражений

CSV-я должен разобрать имеет следующий аромат:

  1. Все поля являются строки, заключенные в кавычки, разделенных запятыми
  2. Котировки внутри полей экранируются в виде двух последовательных котировок
  3. Существует непредсказуемый мусор в начале некоторых строк, которые должны быть проигнорированы (до сих пор он не содержал котировок, к счастью)
  4. Возможно использование полей нули и линий новой строки в полях

Я работаю с VB.NET. Я использую следующее регулярное выражение:

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

Я обрабатывать переводы строк путем подачи StreamReader.ReadLine в переменную строки, пока регулярное выражение не удается, заменив строку с пробелом (это хорошо для моих целей). Затем я извлекаю содержимое поля с помощью Match.Groups («Entry»). Captures and Match.Groups («LastEntry»).

Я полагаю, что удар по производительности исходит из взгляда на скрытые кавычки. Есть ли способ лучше?

Спасибо за любые идеи!

+0

вы можете просто создать простой анализатор, который (а) способ быстрее, чем любое регулярное выражение решения, (б) более удобного для чтения, (с) более отладочным. – Joey

ответ

0

Я думаю, что ваше регулярное выражение излишне сложно, и вложенные кванторы вызывают 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 
+0

Ничего себе. Это намного более элегантно во всех отношениях. Я думаю, что понимаю это, посмотрев в ссылке (я не знал о конструкции отрицания); Я сейчас испытаю это. Большое спасибо за ответ и объяснение! – savyuk

+0

До сих пор выполнение зависает при создании объекта Match в строке, которая не соответствует. Я пытаюсь понять, почему, еще не пробовал многолинейный подход. Любые идеи, почему это может произойти сейчас? – savyuk

+0

Unfroze (и стал почти мгновенным по сравнению с моим начальным регулярным выражением), когда я заменил '[^"] + 'на' [^ "]' (удалил +) в группе без захвата внутри поля. Я немного потерял, почему он замораживает :-) – savyuk

Смежные вопросы