PHP функция preg_match обрезает текст

verfaa

Профессор
Регистрация
29 Янв 2007
Сообщения
417
Реакции
49
Из формы приходит текст вида
[reply-to=16]
reply to comment
another text

Пытаюсь его обработать функцией PHP preg_match, но по непонятной для меня причине в карманы попадает лишь последняя строка текста. Т.е. код:

PHP:
    $text = <<<EOL
    [reply-to=16]
    reply to comment
    another text
    EOL;
    preg_match("#\s?(\[reply\-to=(\d+)\])?(.*)$#iu", $text, $poc);
    print_r($poc);

выдаёт:

Код:
    Array
    (
        [0] =>
    another text
        [1] =>
        [2] =>
        [3] => another text
    )

Хотя в $poc[0] я ожидаю увидеть весь текст, а в $poc[3] весь текст без [reply-to=16]

В чём ошибка и как её исправить?
 
Проблема в вашем регулярном выражении связана с охватом оператора .*, который пытается захватить как можно больше символов, включая [reply-to=16], и поэтому в результате получается только последняя строка текста.

Чтобы исправить это, вы можете использовать оператор .*?, который захватит как можно меньше символов, чтобы соответствовать вашему шаблону.


как-то так:
Код:
$text = <<<EOL
[reply-to=16]
reply to comment
another text
EOL;

preg_match("#\s?(\[reply\-to=(\d+)\])?(.*?)$#ius", $text, $poc);
print_r($poc);
 
Да, это типичная ошибка знакомства с regexp. Сам на нее попадался по началу.
Есть два варианта квантификаторов в регулярных выраениях greedy (жадные) и не жадные.
По умолчанию .* - жадный и везде его приводят в пример. Но при этом он может немного "странно" себя вести, если не понимать, как он работает.

Жадные квантификаторы пытаются захватить как можно больше символов, соответствующих шаблону.
Примеры для текста text = "ababab"
a.*b - в этом примере .* - жадный квантификатор, который пытается захватить как можно больше символов между "a" и "b". Он захватывает всю строку, включая все промежуточные "ab". Поэтому результатом будет "ababab".
Не жадные (Non-Greedy) квантификаторы захватывают минимальное количество символов, соответствующих шаблону.
a.*?b - в этом примере .*? - не жадный квантификатор, который захватывает минимальное количество символов между "a" и "b". Он захватывает только первую пару "ab". Поэтому результат будет "ab".

Еще с толку бывает сбивает, что обычно выражение работает до конца строки, а не до конца текста. Поэтому "не жадные" иногда "правильно" отрабатывают, а инога нет. Не всегда понять это можно с ходу
 
Давайте исправим регулярное выражение, чтобы оно захватывало всю строку, а также текст после [reply-to], если он присутствует. Вот обновленная версия вашего кода:

Код:
php
$text = <<<EOL
[reply-to=16]
reply to comment
another text
EOL;

// Изменим регулярное выражение
preg_match("#^\s*(\[reply\-to=(\d+)\])?\s*(.*?)(\r?\n|\r|$)#uis", $text, $poc);
print_r($poc);

Объяснение изменений:​

  1. Использование ^: Мы добавили ^ в начале выражения, чтобы гарантировать, что мы начинаем с начала строки.
  2. Использование .*?: Заменили .* на .*?, чтобы сделать захват ленивым. Это позволит нам захватить текст до конца первой строки, не захватывая лишние символы.
  3. Добавление (\r?\n|\r|$): Мы добавили группу для обозначения конца строки, чтобы точно определить, где заканчивается текст.

Ожидаемый вывод:​

Теперь при выполнении этого кода вы должны получить:
Код:
Array
(
[0] => [reply-to=16]
[1] => [reply-to=16]
[2] =>  // этот элемент будет пустым, если мы не захватим ничего после первого
[3] => reply to comment
[4] => another text
)

Теперь $poc[0] будет содержать всю строку, включая [reply-to=16], а $poc[3] будет содержать текст после [reply-to]. Если вы хотите захватить весь текст, кроме [reply-to], вы можете немного изменить структуру и добавить еще одно регулярное выражение для обработки текстов. Надеюсь Вам это поможет.
 
Назад
Сверху