PHP preg_match_all не соответствует всему

Рассмотрим следующий фрагмент кода:

$example = "DELIM1test1DELIM2test2DELIM1test3DELIM2test4"; // and so on

preg_match_all('/DELIM1(.*?)DELIM2(.*?)/', $example, $matches);

$matches массив становится:

array:3 [
  0 => array:2 [
    0 => "DELIM1test1DELIM2"
    1 => "DELIM1test3DELIM2"
  ]
  1 => array:2 [
    0 => "test1"
    1 => "test3"
  ]
  2 => array:2 [
    0 => ""
    1 => ""
  ]
]

Как видите, не удается получить test2 и test4. Любая причина, по которой это происходит, и какое может быть возможное решение? Спасибо.


person user1365914    schedule 23.03.2016    source источник


Ответы (5)


.*? не жадный; если у вас нет ограничений после него, он будет соответствовать минимуму необходимых: ноль символов. Вам нужно ограничение после него, чтобы заставить его соответствовать более чем тривиально. Например:

/DELIM1(.*?)DELIM2(.*?)(?=DELIM1|$)/
person Amadan    schedule 23.03.2016

Ленивые подшаблоны в конце шаблона соответствуют либо 0 (*?), либо 1 (+?) символам, потому что они совпадают как можно меньше.

Вы по-прежнему можете использовать ленивое сопоставление и добавить опережающий просмотр, который потребует, чтобы DELIM1 появлялся после значения или конца строки:

/DELIM1(.*?)DELIM2(.*?)(?=$|DELIM1)/

См. демонстрацию. По производительности он очень близок к умеренному жадному токену < / a> (DELIM1(.*?)DELIM2((?:(?!DELIM1).)*) - демонстрация).

Однако лучше всего развернуть его:

DELIM1(.*?)DELIM2([^D]*(?:D(?!ELIM1)[^D]*)*)

См. другую демонстрацию

person Wiktor Stribiżew    schedule 23.03.2016

preg_split было бы лучше:

$example = "DELIM1test1DELIM2test2DELIM1test3DELIM2test4"; // and so on
$keywords = preg_split("/DELIM1|DELIM2/", $example,0,PREG_SPLIT_NO_EMPTY);
print_r($keywords);

выход:

Array
(
    [0] => test1
    [1] => test2
    [2] => test3
    [3] => test4
)

демонстрация: http://ideone.com/s5nC0k

person Community    schedule 23.03.2016

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

str:  DELIM1  test1  DELIM2         test2   DELIM1  test3  DELIM2        test4
pat:  DELIM1  (.*?)  DELIM2  (.*?)          DELIM1  (.*?)  DELIM2 (.*?) 
             match #1                                match #2

(.*?) не является жадным совпадением и может / будет соответствовать строке нулевой длины. Поскольку граница между M2 и te представляет собой строку нулевой длины, этот невидимый символ нулевой длины совпадает, и шаблон на этом заканчивается.

person Marc B    schedule 23.03.2016

Вы можете использовать это регулярное выражение отрицательного просмотра вперед:

preg_match_all('/DELIM1((?:(?!DELIM1|DELIM2).)*)DELIM2((?:(?!DELIM1|DELIM2).)*)/',
                $example, $matches);

(?:(?!DELIM1|DELIM2).)* будет соответствовать 0 или более любому символу, у которого нет DELIM1 или DELIM2 в следующей позиции.

Вывод:

print_r($matches);

    Array
    (
        [0] => Array
            (
                [0] => DELIM1test1DELIM2test2
                [1] => DELIM1test3DELIM2test4
            )

        [1] => Array
            (
                [0] => test1
                [1] => test3
            )

        [2] => Array
            (
                [0] => test2
                [1] => test4
            )        
    )
person anubhava    schedule 23.03.2016