2015-05-04 2 views
2

Итак, у меня есть следующий Regex, который я использую для подсветки синтаксиса:Regex оптимизации производительности

static Regex cKeyWords = new Regex("(\t|\r\n|\\s|\\(|\\)|^)(auto|break|c(ase|har|onst|ontinue)|d(efaut|ouble)|e(lse|num|xtern)|f(loat|or)|goto|i(f|nt)" + 
             "|long|re(gister|turn)|s(hort|igned|izeof|tatic|truct|witch)|typedef|u(nion|nsigned)|v(oid|olatile)|while)(?=\t|\r\n|\\s|\\(|\\)|{|}|$)", RegexOptions.Compiled); 

Он делает то, что я хочу, но когда дело доходит до больших файлов с около 200 000 символов, она занимает немного больше чем 6 секунд.

Если есть способ улучшить производительность?

EDIT: После того, как хороший взгляд на все комментарии/ответы/советы, теперь у меня есть это:

static Regex cKeyWords = new Regex(@"\b(?: 
    s(?:hort|i(?:gned|zeof)|t(?:atic|ruct)|witch) | c(?:ase|har|o(?:nst|ntinue)) | 
    e(?:lse|num|xtern) | i(?:f|nt) | f(?:loat|or) | d(?:efault|ouble) | un(?:ion|signed) | 
    re(?:gister|turn) | vo(?:id|latile) | while | break | long | typedef | auto | goto 
    )\b", 
    RegexOptions.Compiled | RegexOptions.IgnorePatternWhitespace); 

Это один может обрабатывать 200000 символов текста примерно 5,5 секунды. Лучше. Тем не менее, я продолжу делать некоторые тесты, чтобы узнать, могу ли я еще больше сократить время.

+7

Это регулярное выражение является оптимальным (помимо использования '()' вместо '(? :)', но это крошечная проблема). Вы должны посмотреть, почему вы пытаетесь выделить 200kb за один проход. Нет текстового редактора. Редакторы кода обычно поддерживают область в начале строки и выделяют поэтапно. См. [Документы AvalonEdit] (http://avalonedit.net/documentation/html/4d4ceb51-154d-43f0-b876-ad9640c5d2d8.htm). –

+0

@LucasTrzesniewski 1) Я действительно пытался использовать (? :). Это фактически сделало выделение немного быстрее (сокращенное общее время на 0,2 с). 2) Я думаю, вы правы. Выделение такого огромного файла за один проход не очень хорошо. Я посмотрю ссылку, которую вы предоставили. –

+1

И как еще одна оптимизация для регулярного выражения C#, используйте атомарную группировку по всему шаблону (заключите в '(?> ...)'), чтобы избежать ненужного возврата. –

ответ

4

По моему мнению (\t|\r\n|\\s|\\(|\\)|^) в начале и (?=\t|\r\n|\\s|\\(|\\)|{|}|$) в конце бесполезны и могут быть заменены границами слов для того же результата. (начиная шаблон с чередованием является одним из худших, что вы должны избегать, поскольку регулярное выражение двигатель должен проверить каждый позиции в строке со всеми альтернативами в худшем случае)

Используйте только группы захвата, когда это необходимо, потому что они использует память и время для ничего. В данном случае они вам вообще не нужны.

Таким образом, вы можете переписать шаблон так:

static Regex cKeyWords = new Regex(@"\b(?: 
    auto | break | c(?:ase|har|onst|ontinue) | d(?:efaut|ouble) | 
    e(?:lse|num|xtern) | f(?:loat|or) | goto | i(?:f|nt) | long | 
    re(?:gister|turn) | s(?:hort|igned|izeof|tatic|truct|witch) | typedef | 
    un(?:ion|signed) | vo(?:id|latile) | while)\b", 
    RegexOptions.Compiled | RegexOptions.IgnorePatternWhitespace); 

Обратите внимание, что ключевое слово в настоящее время в группе 0 (Весь матч).

Другие вещи, которые вы можете попробовать:

  • пытаются факторизовать более, например: c(?:ase|har|on(?:st|tinue)) и т.д.
  • пытаются не факторизовать вообще.
  • пытаются сортировать альтернативы по вероятности (например, есть несколько слов, начинается с «с», так что вы можете попробовать поставить s(?:hort|igned|izeof|tatic|truct|witch) на первое место.
  • пытаются сортировать альтернативы от наиболее частых ключевых слов.
  • попытайтесь добавить (?=[a-gilr-w])(так что первая буква всех ключевых слов) или не менее (?=[a-z]) сразу после границы первого слова (имейте в виду, что граница слова может преуспеть в позиции символа слова или в позиции символа без слова). Цель состоит в том, чтобы избежать проверки чередования, когда нет интересной буквы в граничной позиции слова.
+2

' (\ t | \ r \ n | \\ s | \\ (| \\) | ^) 'действительно нежелательно, так как' \ t' и '\ n' включены в '\ s'. Класс символов также предпочтительнее чередования, если тестируется только один символ, поскольку нет необходимости пробовать другие символы в чередовании, если он уже сопоставлен символом раньше. В любом случае решение с '\ b' является лучшим решением, как представлено в ответе. – nhahtdh

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