Изпреварващото твърдение е друг вид твърдение с нулев размер. Изпреварващите твърдения са достъпни и в позитивна, и в негативна форма. Те изглеждат така:
...
) пасне с
текста от текущата позиция, а в противен случай пропада. Но след като веднъж
вътрешният израз е бил изпробван, съпоставящата машина изобщо не напредва в
символния низ. Останалата част от образеца се изпробва на същото място,
където е пропаднало твърдението.
Един пример хем ще затвърди казаното дотук, хем нагледно ще покаже случай, когато е полезно използването на изпреварващо трвърдение. Да си представим един прост образец, който пасва на файлово име и го разцепва на основно име и разширение, разделени от ".". Например при "news.rc", "news" е основното име, а "rc" е разширението на файла.
Образецът, който ще пасне, е съвсем прост: .*[.].*$. (Обърнете внимание, че "." трябва да се обработи по особен начин, защото е метасимвол. Тук аз съм го сложил в рамките на клас от символи. Забележете и поставения накрая символ $. Той е добавен за да се гарантира, че всичко оставащо от символния низ трябва да бъде включено в разширението.) Този регулярен израз пасва с "foo.bar", "autoexec.bat", "sendmail.cf" и "printers.conf".
Нека сега си представим едно леко усложнение на задачата: какво ще стане, ако искаме да нашият израз да пасва с всички файлови имена, чието разширение не е "bat"? Ето няколко погрешни опита:
.*[.][^b].*$
Първи опит: "bat" се изключва чрез изискването първия символ от разширението да не е "b". Това е неправилно, понеже изразът няма да пасне и с "foo.bar".
.*[.]([b]..|.[â].|..[t])$
Изразът се обърка още повече като се опитахме да закърпим първото решение чрез изискването едновременно да съвпаднат следните ситуации: първият символ от разширението не е "b"; вторият символ да не е "a"; и третият символ да не е "t". Този израз приема "foo.bar" и отхвърля "autoexec.bat", но изисква трибуквено разширение и следователно не приема "sendmail.cf". Това е друга грешка, така че нека се опитаме да я поправим като още повече усложним образеца.
.*[.]([b].?.?|.[â]?.?|..?[t]?)$
При третия опит втората и третата буква са отбелязани като незадължителни, за да се позволи съвпадането с разширения, които са по-малки от три символа, като например "sendmail.cf".
Сега образецът е наистина сложен, което го прави много трудно четим и разбираем. Когато пишете даден регулярен израз, задайте си въпроса: ако срещнете този израз в някоя програма, колко трудно ще ви бъде да разберете за какво е предназначен?
Още по-лошо, това решение не е достатъчно мащабируемо; ако задачата се промени така, че трябва да изключвате едновременно разширенията "bat" и "exe", тогава шаблонът ще стане още по-сложен и объркващ.
Всичко се решава с едно негативно изпреварващо твърдение. Да се върнем обратно към първоначалния образец, и точно преди .* където трябва да се падне разширението, да вмъкнем (?!bat$). Това означава: ако изразът bat не пасва с текущата позиция, опитай останалата част от образеца; ако има съвпадение с bat$, тогава цялото съпоставяне с образеца ще пропадне. Символът $ накрая е необходим за да се гарантира, че няма погрешно да се изключват файловите разширения, които само започват с "bat" -- като в случая със "sample.batch", например.
След тази промяна, пълният обазец е: .*[.](?!bat$).*$. Сега вече е лесно изключването на още едно файлово разширение -- просто трябва да го добавим като алтернатива вътре в твърдението. .*[.](?!bat$|exe$).*$ изключва едновременно "bat" и "exe".