2010-07-07 4 views
3

Мое программное обеспечение позволяет пользователям использовать регулярное выражение для подготовки файлов. Я в процессе добавления библиотеки regexp по умолчанию с общими выражениями, которые могут быть повторно использованы для подготовки различных форматов. Одной из общих задач является удаление crlf в определенных частях файлов, но не в других. Например, это:Улучшение производительности регулярного выражения

<TU>Lorem 
    Ipsum</TU> 
    <SOURCE>This is a sentence 
    that should not contain 
    any line break. 
    </SOURCE> 

должны стать:

<TU>Lorem 
    Ipsum</TU> 
    <SOURCE>This is a sentence that should not contain any line break. 
    </SOURCE> 

У меня есть rexep, что делает работу очень хорошо:

(?(?<=<SOURCE>(?:(?!</?SOURCE>).)*)(\r\n)) 

Проблема заключается в том, что он обрабатывает интенсивный и файлов выше 500 КБ, это может занять 30+ секунд. (regex компилируется, в этом случае нескомпилированный намного медленнее)

Это не большая проблема, но мне интересно, есть ли лучший способ достичь тех же результатов с помощью Regex.

Заранее благодарим за ваши предложения.

+2

На каком языке? (И этот файл XML?) – kennytm

+0

Вы пробовали искать закрывающий тег вместо открытия? У меня часто возникает впечатление, что двигатель регулярного выражения более «естественно» идет вперед. – Jens

+2

Вы считали * not * использующим регулярное выражение? –

ответ

2

Попробуйте это :

\r\n(?=(?>[^<>]*(?><(?!/?SOURCE>)[^<>]*)*)</SOURCE>) 

Он начинается с соответствия \r\n, n использует lookahead, чтобы узнать, совпадает ли совпадение между <SOURCE> и </SOURCE>. Он делает это, ища </SOURCE>, но если он найдет <SOURCE>, сначала это не сработает. Атомные группы не позволяют сохранить информацию о состоянии, которая необходима для обратного отслеживания, потому что пропуск или отказ, возврат назад никогда не требуется.

+0

Спасибо большое! Сокращает время наполовину в Expresso и бреет около 2 секунд в моем приложении! – Sylverdrag

2

«Компиляция» обычного выражения просто означает преобразование его из Deterministic Finite Automaton в Non-deterministic Finite Automaton. Это не «компиляция в машинный код», как вы могли ожидать.

NFAs обычно меньше, чем их соответствующие DFA, и может выполнять более пространство эффективно. Каждый DFA имеет по крайней мере один эквивалент NFA и наоборот. Однако Perl Compatible Regular Expressions не являются actually Regular. Поэтому я не знаю, что они преобразуются в NFA, или если «компиляция» просто другой формы лексического анализа, которая была сделана, однажды не нужно повторять.

PCREs медленны в соответствии с Russ Cox, частично из-за их нерегулярности, и ваше выражение выше довольно нерегулярно.

О, и если вы пытаетесь распознать вложенные теги с регулярными выражениями, не надо. Используйте реальный парсер (X|HT)?ML. Вы действительно не хотите, чтобы the pony приехал

+0

Извините, я забыл упомянуть, что я использую C#. Смотрите http://blogs.msdn.com/b/bclteam/archive/2004/11/12/256783.aspx?wa=wsignin1.0, чтобы увидеть, что именно скомпилированный параметр в .NET. Статья Русса Кокса очень интересна. Никогда не было времени, чтобы полностью изучить его, но я знал некоторые ключевые моменты, и я понимаю, что нерегулярность является причиной медленного появления регулярного выражения. Мой вопрос можно перефразировать как «Есть ли способ получить тот же результат, используя регулярное выражение, которое на самом деле является регулярным?» Можете ли вы подумать о регулярном выражении, которое приведет к такому же результату без всего этого отступления? – Sylverdrag

+0

PS: Я предполагаю, что это опечатка, но согласно статье Руса (и определение) DFA более эффективны, чем NFA (один выбор вместо нескольких вариантов). – Sylverdrag

+0

Существует несколько преимуществ: я добавил слово выше, чтобы уточнить; Теперь я пашу через бумагу Кокса. – msw

2

Я бы предпочел отрицательное выражение для регулярного выражения, чтобы убедиться, что вы действительно можете сопоставить \r\n в текущей позиции. В противном случае движок должен сделать весь lookbehind (произвольный размер для загрузки) на каждом отдельном символе во всем файле, только чтобы узнать, что нет возврата каретки для замены.

(?=\r\n)(?(?<=<SOURCE>(?:(?!</?SOURCE>).)*)(\r\n)) 

должно быть намного быстрее. По крайней мере, в RegexBuddy для выполнения регулярного выражения требуется много меньше шагов. Если это не в .NET, я не знаю, почему. Возможно, условное регулярное выражение не так эффективно (я должен признать, что сначала я не узнал его и не думал о синтаксической ошибке в вашем регулярном выражении). Я думаю, что вам не нужно условное регулярное выражение в этом сценарии. Как насчет

\r\n(?<=<SOURCE>(?:(?!</?SOURCE>).)*) 

Это быстрее? Я предполагаю, что вы используете RegexOptions.Singleline для компиляции регулярного выражения.

Если нет, то, возможно, в вашем блоке <SOURCE>, вероятно, очень много возвратов каретки и много других символов, и произвольный размер выглядит просто долго.Тогда мое другое предложение разделить задачу:

  1. Сопрягай <SOURCE> блок
  2. Заменить все CRLFs внутри блока (не требуется регулярное выражения)
  3. Заменить <SOURCE> блок
+0

Спасибо за предложение. (+1) Это имеет большой смысл, и я возлагал большие надежды, но приурочен как секундомером, так и тем же образцом текста в Expresso. закончил ровно 21 секунду. По неизвестным причинам одна и та же операция в том же файле занимает 8 секунд в моей программе, опять же, независимо от того, тестирую ли я ее с негативным внешним ожиданием или без нее. Я предполагаю, что это означает, что .NET уже оптимизирует регулярное выражение перед его запуском. – Sylverdrag

+0

RE: Parser. Формат файла неизвестен заранее. Основная специализация моего программного обеспечения заключается в подготовке текстовых файлов, которые не соответствуют правилам или для которых не существует известных правил. d XML-подобные теги для этого примера, потому что это очень легко понять, что я хочу сделать, но это может быть что угодно. Я мог бы написать парсер для обработки этой конкретной функции, но есть много конструкций этого типа, и я не могу написать парсер для каждого из них и выпускать новую версию в любое время, когда кто-то запрашивает новый фрагмент. Как можно больше, я хочу придерживаться регулярного выражения. – Sylverdrag

+0

Спасибо за ваше обновление. \ r \ n (? <= (? :(?!).) *) немного лучше (18 секунд вместо 21), но я пошел с ответом Алана, так как это намного быстрее. Подвыражение без обратного отслеживания (>) - действительно опрятный трюк. – Sylverdrag

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