Вот входные данные:
*** INVOICE ***
THE BIKE SHOP
1 NEW ROAD, TOWNVILLE,
SOMEWHERE, UK, AB1 2CD
TEL 01234-567890
To: COUNTER SALE No: 243529 Page: 1
Date: 04/06/10 12:00
Ref: Aiden
Cust No: 010000
Вот regex, который работает (Опции: одна строка, ignorewhitespace, скомпилированный) - это сразу соответствует, и группы правильно заполняются:
\W+INVOICE\W+
(?<shopAddr>.*?)\W+
To:\W+(?<custAddr>.*?)\W+
No:\W+(?<invNo>\d+).*?
Date:\W+(?<invDate>[0-9/ :]+)\W+
Ref:\W+(?<ref>[\w ]*?)\W+
Cust
Как только я добавляю, что 'N' из Cust No в короля, анализируя вход зависает навсегда:
\W+INVOICE\W+
(?<shopAddr>.*?)\W+
To:\W+(?<custAddr>.*?)\W+
No:\W+(?<invNo>\d+).*?
Date:\W+(?<invDate>[0-9/ :]+)\W+
Ref:\W+(?<ref>[\w ]*?)\W+
Cust N
Если я добавляю что-то как "любой символ":
\W+INVOICE\W+
(?<shopAddr>.*?)\W+
To:\W+(?<custAddr>.*?)\W+
No:\W+(?<invNo>\d+).*?
Date:\W+(?<invDate>[0-9/ :]+)\W+
Ref:\W+(?<ref>[\w ]*?)\W+
Cust .
Это работает, но как только я добавляю фиксированный символ, король зависает снова:
\W+INVOICE\W+
(?<shopAddr>.*?)\W+
To:\W+(?<custAddr>.*?)\W+
No:\W+(?<invNo>\d+).*?
Date:\W+(?<invDate>[0-9/ :]+)\W+
Ref:\W+(?<ref>[\w ]*?)\W+
Cust ..:
Кто-либо может советовать, почему, добавляя что-то таким образом тривиальное заставило бы это падать? Я могу позволить некоторой трассировке следить за действием соответствия, чтобы видеть, застревает ли это в катастрофическом отслеживании в обратном порядке?
С помощью RegexOptions.IgnorePatternWhitespace
вы указываете движку игнорировать пробелы в шаблоне. Таким образом, когда вы пишете Cust No
в шаблоне, на самом деле это означает CustNo
, что не соответствует введенным данным. Это и есть причина проблемы.
Из документации:
По умолчанию пробел в шаблоне регулярного выражения является значимым; это заставляет механизм регулярного выражения соответствовать символу пробела во входной строке. [...]
Опция
RegexOptions.IgnorePatternWhitespace
, илиx
inline, изменяет это поведение по умолчанию следующим образом:
- Незаписанный пробел в шаблоне регулярного выражения игнорируется. Чтобы быть частью шаблона регулярного выражения, символы пробела должны быть экранированы (например, как
\s
или"\ "
).
Поэтому вместо Cust No
в режиме IgnorePatternWhitespace
вы должны написать Cust\ No
, потому что иначе это будет интерпретировано как CustNo
.
polygenelubricants уже объясняли, почему ваше регулярное выражение не удалось . Причина зависания в том, что вы столкнулись с катастрофическим возвратом . В вашем регулярном выражении много частей, которые могут соответствовать одному и тому же тексту разными способами. Если общее совпадение не удается, механизм регулярных выражений будет пробовать все возможные перестановки, пока не исчерпает их все или не прервется с переполнением стека.
E. грамм. в To: \ W + (?
. *?
с радостью будет соответствовать тем же символам, что и \ W
, и поскольку вы используете Singleline
, . *?
также будет переходить в часть No: ...
входного текста и дальше и дальше. В вашем примере я тестировал в RegexBuddy, что произойдет, если вы добавите «N» после «Cust» - механизм регулярных выражений прерывается после 1 000 000 шагов.
Чтобы избежать этого, вам нужно сделать регулярное выражение более конкретным или (в данном случае это может быть лучший вариант) не допускать возврата механизма регулярных выражений, заключая части, которые уже совпали в " атомных группах ":
(?>\W+INVOICE\W+)
(?>(?<shopAddr>.*?)\W+To:)
(?>\W+(?<custAddr>.*?)\W+No:)
(?>\W+(?<invNo>\d+).*?Date:)
(?>\W+(?<invDate>[0-9/\ :]+)\W+Ref:)
(?>\W+(?<ref>[\w\ ]*?)\W+Cust)
Это позволяет регулярному выражению выходить из строя намного быстрее, если входные данные и регулярное выражение не подходят друг к другу.