PCRE регулярен израз с lookahead и lookbehind винаги връща true

Опитвам се да създам регулярен израз за валидиране на формуляр, но той винаги връща true. Потребителят трябва да може да добави нещо като {user|2|S} като вход, но също така да използва скоби, ако те са екранирани с \.

Този код проверява за лявата скоба { засега.

$regex = '/({(?=([a-zA-Z0-9]+\|[0-9]*\|(S|D[0-9]*)}))|[^{]|(?<=\\\){)*/';
if (preg_match($regex, $value)) {
     return TRUE;
} else {
    return FALSE;
}

Възможен правилен вход би бил:

Hello {user|1|S}, you have {amount|2|D2}

or

Hello {user|1|S}, you have {amount|2|D2} in \{the_bracket_bank\}

Това обаче трябва да върне false:

Hello {user|1|S}, you have {amount|2}

и това също:

Hello {user|1|S}, you have {amount|2|D2} in {the_bracket_bank}

Пример на живо може да се намери тук: http://regexr.com?37tpu Имайте предвид, че има \ в lookbehind в края, PHP ми даваше съобщения за грешка, защото трябваше да го избягам допълнително в моя код.


person dumazy    schedule 08.01.2014    source източник
comment
Вероятно ще ви е по-лесно да изхвърлите третия параметър на pregmatch (т.е. да погледнете какво съвпада) и да тествате всеки OR regex отделно. Документирането на вашия регулярен израз също би помогнало (на вас).   -  person AD7six    schedule 08.01.2014
comment
Защо регулярният израз не трябва да съвпада с Hello {user|1|S}, you have {amount|2|D2} in {the_bracket_bank} ?   -  person revo    schedule 08.01.2014


Отговори (3)


Основната грешка е, че не сте посочили, че регулярният израз трябва да съвпада от началото до маркирания низ. Използвайте твърденията ^ и $.

Мисля, че трябва да избегнете { и } във вашия регулярен израз, тъй като те имат специално значение. Заедно те образуват квантор.

(?<=\\\) е по-добре написано (?<=\\\\). Обратната наклонена черта трябва да бъде двойно екранирана, тъй като има специално значение както в низ с единични кавички, така и в PCRE регулярен израз. Използването на \\\ също работи, защото ако низът в единични кавички съдържа някаква екранираща последователност с изключение на \\ и \', той я обработва като буквална обратна наклонена черта и буква, следователно \) се приема буквално. Но изричното избягване на обратната наклонена черта два пъти ми се струва по-лесно за четене.

Регулярният израз трябва да бъде

$regex = '/^(\{(?=([a-zA-Z0-9]+\|[0-9]*\|(S|D[0-9]*)\}))|[^{]|(?<=\\\\)\{)*$/';

Но забележете, че твърденията за оглед не са необходими. Този регулярен израз също трябва да свърши работата:

$regex = '/^([^{]|\\\{|\{[a-zA-Z0-9]+\|[0-9]*\|(S|D[0-9]*)\})*$/';

Всички знаци, различни от {, се съпоставят с първата алтернатива. Когато се чете {, се използва една от останалите две алтернативи. Или шаблонът за скобите съвпада, или механизмът на регулярните изрази връща назад един знак и се опитва да съпостави последователност от \{ знака. Ако не успее и в двете посоки, той се връща назад, докато достигне началото на низа и се провали напълно.

person Palec    schedule 08.01.2014
comment
работи като чар. Все пак не съм го пробвал без огледните твърдения - person dumazy; 08.01.2014

Съвпадение без поглед назад

Можете да направите регулярен израз за това, без да използвате lookbehind/lookaheads (което обикновено се препоръчва).

Например, ако вашето изискване е да можете да съпоставите всеки знак освен { и }, освен ако не е предшестван от \. Можете също да кажете:

Съвпада с който и да е знак освен { и } ИЛИ съвпада с \{ или \}. За да съпоставите всеки знак освен { и }, използвайте:

[^{}]

За съответствие с \{ използвайте:

\\\{

Една обратна наклонена черта е за екраниране на { (което може да не е необходимо, в зависимост от вашия компилатор на регулярен израз), а една обратна наклонена черта е за екраниране на друга обратна наклонена черта.

Ще завършите с това:

(?:
    [^{}]
|
    \\\{
|
    \\\}
)+

Добре форматирах този регулярен израз, така че да може да се чете. Ако искате да го използвате във вашия код по този начин, не забравяйте да използвате модификатора [PCRE_EXTENDED][1].

person gitaarik    schedule 08.01.2014
comment
Благодаря за този страхотен и добре обяснен отговор. Това също може да работи, но решението на Palec работи мигновено и се справя с всичко както трябва - person dumazy; 08.01.2014
comment
Да, вярно, разбира се е добре възможно в един регулярен израз. И днес не съм съвсем наясно :P. Актуализирах отговора си, за да включвам само обяснението за съвпадение без огледи. - person gitaarik; 08.01.2014

Изглежда ми по-скоро работа за преглед назад:

/((?<!\\\\)\{[a-zA-Z0-9]+\|[0-9]+\|[SD][0-9]*\})/

Факторът на обфускация обаче е толкова висок, че предпочитам да разпозная всички низове в скоби и да ги анализирам по-късно.

person kuroi neko    schedule 08.01.2014