Как сделать регулярное выражение не голодным с кавычками?

Как сделать так, чтобы он не был голодным preg_match_all('/"[\p{L}\p{Nd}а-яА-ЯёЁ -_\.\+]+"/ui', $outStr, $matches);

3 ответа

Решение

Вы имеете в виду не жадный, как найти самый короткий матч вместо самого длинного? *, +, а также ? квантификаторы по умолчанию жадные и будут максимально соответствовать. Добавьте знак вопроса после них, чтобы сделать их не жадными.

preg_match_all('/"[\p{L}\p{Nd}а-яА-ЯёЁ -_\.\+]+?"/ui', $outStr, $matches);

Жадный матч:

"foo" and "bar"
^^^^^^^^^^^^^^^

Нежадное совпадение:

"foo" and "bar"
^^^^^

Смотрите: http://www.php.net/manual/en/reference.pcre.pattern.modifiers.php

U (PCRE_UNGREEDY)

Этот модификатор инвертирует "жадность" квантификаторов, так что они не являются жадными по умолчанию, но становятся жадными, если за ними следует?. Это не совместимо с Perl. Его также можно установить с помощью настройки модификатора (?U) в шаблоне или с помощью вопросительного знака за квантификатором (например, *?).

Ты предложил

/"[\p{L}\p{Nd}а-яА-ЯёЁ -_\.\+]+"/ui

который я представляю эквивалентно:

/"[\pL\p{Nd}а-яА-ЯёЁ -_.+]+"/ui

Чтобы показать людям, что вы используете не ASCII, если это не очевидно, используйте \x{⋯} ускользает от этого:

/"[\pL\p{Nd}\x{430}-\x{44F}\x{410}-\x{42F}\x{451}\x{401} -_.+]+"/ui

И использование именованных символов это:

/"[\pL\p{Nd}\N{CYRILLIC SMALL LETTER A}-\N{CYRILLIC SMALL LETTER YA}\N{CYRILLIC CAPITAL LETTER A}-\N{CYRILLIC CAPITAL LETTER YA}\N{CYRILLIC SMALL LETTER IO}\N{CYRILLIC CAPITAL LETTER IO} -_.+]+"/ui

Кстати, они производятся путем запуска их через сценарийuniquote, первое использование uniquote -x и второе использование uniquote -v,

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

U+0410 ‹А› \N{CYRILLIC CAPITAL LETTER A}
U+0430 ‹а› \N{CYRILLIC SMALL LETTER A}
U+0401 ‹Ё› \N{CYRILLIC CAPITAL LETTER IO}
U+0451 ‹ё› \N{CYRILLIC SMALL LETTER IO}

за:

U+0041 ‹A› \N{LATIN CAPITAL LETTER A}
U+0061 ‹a› \N{LATIN SMALL LETTER A}
U+00CB ‹Ë› \N{LATIN CAPITAL LETTER E WITH DIAERESIS}
U+00EB ‹ë› \N{LATIN SMALL LETTER E WITH DIAERESIS}

И теперь я думаю об этом, это все буквы, поэтому я не могу понять, почему вы перечисляете список кириллицы. Это потому, что вам нужны не все буквы кириллицы, а только этот конкретный набор? В противном случае я бы просто сделал:

/"[\pL\p{Nd} -_.+]+"/ui

В какой момент мне интересно об этом /i, Я не могу понять, какова его цель, поэтому просто написал бы:

/"[\pL\p{Nd} -_.+]+"/u

Как уже упоминалось, обмен максимально количественно + для соответствующей минимальной версии, +?, буду работать:

/"[\pL\p{Nd} -_.+]+?"/u

Тем не менее, я обеспокоен этим диапазоном [ -_], то есть, \p{SPACE}-\p{LOW LINE}, Я считаю, что это очень специфический диапазон. Это означает, что любой из этих

!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_

Во-первых, вы снова включили заглавные буквы ASCII. Для другого вы пропустили некоторые символы и знаки препинания:

% unichars -g '\p{ASCII}' '[\pS\pP]' 'ord() < ord(" ") || ord() > ord("_")'
 `  U+0060 GC=Sk GRAVE ACCENT
 {  U+007B GC=Ps LEFT CURLY BRACKET
 |  U+007C GC=Sm VERTICAL LINE
 }  U+007D GC=Pe RIGHT CURLY BRACKET
 ~  U+007E GC=Sm TILDE

(Это вывод из сценарияunichars, если вам интересно.)

Что кажется странно произвольным. Поэтому мне интересно, может быть, это не достаточно хорошо для вас:

/"[\pL\p{Nd}\s\pS\pP]+?"/u

Теперь, когда я думаю об этом, эти два могут вызвать другие проблемы:

U+0401 ‹Ё› \N{CYRILLIC CAPITAL LETTER IO}
U+0451 ‹ё› \N{CYRILLIC SMALL LETTER IO}

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

NFD("\N{CYRILLIC CAPITAL LETTER IO}") => "\N{CYRILLIC SMALL LETTER IE}\N{COMBINING DIAERESIS}"
NFD("\N{CYRILLIC SMALL LETTER IO}")   => "\N{CYRILLIC CAPITAL LETTER IE}\N{COMBINING DIAERESIS}"

А теперь у вас нет писем!

% uniprops "COMBINING DIAERESIS"
U+0308 ‹◌̈› \N{COMBINING DIAERESIS}
    \w \pM \p{Mn}
    All Any Assigned InCombiningDiacriticalMarks Case_Ignorable CI Combining_Diacritical_Marks Dia Diacritic M Mn Gr_Ext Grapheme_Extend Graph GrExt ID_Continue IDC Inherited Zinh Mark Nonspacing_Mark Print Qaai Word XID_Continue XIDC

Так что, возможно, вы бы на самом деле хотели:

/"[\pL\pM\p{Nd}\s\pS\pP]+?"/u

Если вы хотите ограничить вашу строку тем, что в ней будут только символы из латинского или кириллического алфавита (а не, скажем, греческого или катакана), вы бы добавили на этот счет дополнительные сведения:

/"(?:(?=[\p{Latin}\p{Cyrillic}])[\pL\pM\p{Nd}\s\pS\pP])+?"/u

Кроме того, что вам также нужно Common чтобы получить цифры и различные обозначения и символы, и вам нужно Inherited для объединения отметок после ваших писем. Это подводит нас к этому:

/"(?:(?=[\p{Latin}\p{Cyrillic}\p{Common}\p{Inherited}])[\pL\pM\p{Nd}\s\pS\pP])+?"/u

Теперь предлагается другой способ добиться минимального соответствия между двойными кавычками:

/"(?:(?!")(?=[\p{Latin}\p{Cyrillic}\p{Common}\p{Inherited}])[\pL\pM\p{Nd}\s\pS\pP])+"/u

Что становится все сложнее, чтобы не бежать /x Режим:

/
    "               # literal double quote
    (?:
  ### This group specifies a single char with
  ### three separate constraints:

        # Constraint 1: next char must NOT be a double quote
        (?!")

        # Constraint 2: next char must be from one of these four scripts
        (?=[\p{Latin}\p{Cyrillic}\p{Common}\p{Inherited}])

        # Constraint 3: match one of either Letter, Mark, Decimal Number,
        #               whitespace, Symbol, or Punctuation:
        [\pL\pM\p{Nd}\s\pS\pP]

    )       # end constraint group
    +       # repeat entire group 1 or more times
    "       # and finally match another double-quote
/ux

Если бы это был Perl, я написал бы это с m{⋯}xu

m{
    "               # literal double quote
    (?:
  ### This group specifies a single char with
  ### three separate constraints:

        # Constraint 1: next char must NOT be a double quote
        (?!")

        # Constraint 2: next char must be from one of these four scripts
        (?=[\p{Latin}\p{Cyrillic}\p{Common}\p{Inherited}])

        # Constraint 3: match one of either Letter, Mark, Decimal Number,
        #               whitespace, Symbol, or Punctuation:
        [\pL\pM\p{Nd}\s\pS\pP]

    )       # end constraint group
    +       # repeat entire group 1 or more times
    "       # and finally match another double-quote
}ux

Но я не знаю, можете ли вы использовать парные, заключающие в скобки разделители, как это в PHP.

Надеюсь это поможет!

Другие вопросы по тегам