PHP preg_match все, пока

У меня есть запись в блоге, которая иногда содержит много текста/изображений, и я хочу вырезать отрывок из этого блога. Чтобы быть более конкретным, я хочу сопоставить все до второго тега изображения

ниже приведен образец текста.

Я пробовал отрицательный прогноз, например

/[\w\r\n;:',."&\s*<>=-_]+(?!<img)/i

но я не могу понять, как применить предпросмотр к модификатору «+». Кто что знает, буду очень признателен.

*override*
I've been stuck in a room lately, and though it's hard to stay creative all the time,         sometimes you need that extra kick. Well for some us we have to throw pictures of true creative genius at ourselves to stimulate us.

So sit back and soak in some inspiration I've come across the past year.

&nbsp;

&nbsp;

&nbsp;

<figure>
    <a href="">
    <img class="aligncenter" src="http://funnypagenet.com/wp-content/uploads/2011/07/Talesandminimalism_12_www.funnypagenet.com_.jpg" alt="" width="574" height="838" />
    </a>
    <figcaption></figcaption>
</figure>

&nbsp;

&nbsp;

&nbsp;

&nbsp;
<h4 style="text-align: center;">
    <a href="http://funnypagenet.com/tales-and-minimalism/">source</a>
</h4>
Couldn't find who did this, but couldn't explain the movie any simpler

&nbsp;

&nbsp;

&nbsp;

&nbsp;

&nbsp;

&nbsp;

&nbsp;

&nbsp;

&nbsp;

<figure>
    <img class="aligncenter" src="http://brickhut.files.wordpress.com/2011/05/theempirestrikesback1.jpg" alt="" width="540" height="800" />
    <figcaption></figcaption>
</figure>

&nbsp;

&nbsp;

&nbsp;

person Marius Miliunas    schedule 24.02.2012    source источник
comment
Можно легко сделать с помощью DOM, по какой причине вы предпочитаете решение на основе регулярных выражений?   -  person anubhava    schedule 24.02.2012
comment
Структура HTML кажется испорченной.   -  person Shiplu Mokaddim    schedule 24.02.2012
comment
@Shiplu Выглядит не так уж плохо ... может быть, просто не хватает какого-то ‹p›, но ничего такого испорченного, как вы говорите   -  person Damien Pirsy    schedule 24.02.2012
comment
@Дэмиен Ой! я досконально не осматривал. Структура вроде в порядке. Слишком много неразрывных пробелов   -  person Shiplu Mokaddim    schedule 24.02.2012
comment
причина, по которой я не хочу использовать DOM, заключается в том, что я хочу сделать как можно больше на стороне сервера, прежде чем я перенесу ее на внешний интерфейс.   -  person Marius Miliunas    schedule 25.02.2012


Ответы (3)


Очевидно, что прямая резка строки не подходит для вашего второго изображения:

...
<figure>
    <img class="aligncenter" src="http://brickhut.files.wordpress.com/2011/05/theempirestrikesback1.jpg" alt="" width="540" height="800" />
    <figcaption></figcaption>
</figure>

Вырезание после изображения оставит незакрытые элементы:

...
<figure>
    <img class="aligncenter" src="http://brickhut.files.wordpress.com/2011/05/theempirestrikesback1.jpg" alt="" width="540" height="800" />

Что может разрушить рендеринг страницы внутри браузера. И не играет роли, если вы используете здесь preg_match с регулярным выражением или какие-то строковые функции.

Вам нужен синтаксический анализатор DOM, такой как DOMDocument, способный обрабатывать HTML:

Учитывая пример кода HTML, который похож на ваш вопрос:

$html = <<<HTML
dolor sit amet, consectetuer adipiscing elit. <img src="http://example.com/img-a.jpg"> Aenean commodo 
ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, 
nascetur ridiculus mus.

<figure>
    <img src="http://example.com/img-b.jpg">
    <figcaption>Figure Caption</figcaption>
</figure>

Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut.
HTML;

Теперь вы можете использовать класс DOMDocument для загрузки фрагмента HTML внутри тега <body>, потому что это все ваше тело html для манипуляции. Поскольку вы используете нестандартные теги HTML (<figure> и <figcaption>), вам следует отключить предупреждения о них при загрузке строки с помощью libxml_use_internal_errors:

$doc = new DOMDocument();
libxml_use_internal_errors(1);
$doc->loadHTML(sprintf('<body>%s</body>', $html));

Это базовая настройка парсера DOM, теперь ваш HTML находится внутри парсера. Теперь самое интересное. Вы хотите создать выдержку до второго изображения документа. Это означает, что все, что находится после этого элемента, должно быть удалено. Звучит так же просто, как обрезать строку, которая, как мы знаем, не работает, но на этот раз парсер DOM сделает всю работу за нас.

Вам нужно только получить все узлы (<tag>, Text, <!-- comments -->, ...) и удалить их. Все узлы после второго тега <img> в (следующем порядке документа). Такие вещи можно выразить с помощью XPath:

/descendant::img[position()=2]/following::node()

Парсер PHP DOM поставляется с XPath, поэтому давайте сделаем это:

$xp = new DOMXPath($doc);
$delete = $xp->query('/descendant::img[position()=2]/following::node()');
foreach ($delete as $node)
{
    $node->parentNode->removeChild($node);
}

Осталось только получить (образцовый вывод) оставшийся отрывок. Как мы знаем, все это находится внутри тега <body>:

foreach ($doc->getElementsByTagName('body')->item(0)->childNodes as $child)
{
    echo $doc->saveHTML($child);
}

Что даст вам следующее:

dolor sit amet, consectetuer adipiscing elit. <img src="http://example.com/img-a.jpg"> Aenean commodo 
ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, 
nascetur ridiculus mus.

<figure><img src="http://example.com/img-b.jpg"></figure>

Как показывает этот пример, тег <figure> теперь правильно закрыт.

Аналогичный сценарий заключается в создании отрывка после определенной длины текста или количества слов: Перенос слов/вырезание текста в строке HTML

person hakre    schedule 24.02.2012
comment
О, чувак, мне нужно много читать, пока это не станет для меня понятным. Спасибо за развернутый ответ - person Marius Miliunas; 25.02.2012

Ну, это не регулярное выражение, но оно должно работать:

$post = str_ireplace('<img', '!!!<img', $post);
list($p1, $p2) = explode('!!!', $post);
$keep = $p1 . $p2;

Помещает маркер разделения перед тегами изображения (!!!), разделяет их и сохраняет первые два фрагмента, которые должны содержать все до второго тега изображения. Регулярное выражение не требуется.

Редактировать. Поскольку это отрывок, вы можете запустить strip_tags() для результата. Возможно, что если вы этого не сделаете, у вас будут открытые теги HTML, которые никогда не будут закрыты.

person Mr. Llama    schedule 24.02.2012
comment
Я решил свою проблему, сделав что-то подобное. Жаль, что я должен ждать 7 часов, чтобы опубликовать свой ответ - person Marius Miliunas; 25.02.2012

Если вам действительно нужно решение на основе регулярных выражений, то вот оно:

// assuming $str is your full HTML text
if ( preg_match_all('~^(.*?<img\s.*?<img\s[^>]*>)~si', $str, $m) )
    print_r ( $m[1] );
person anubhava    schedule 24.02.2012
comment
Что делать, если нет второго тега изображения? - person Mr. Llama; 25.02.2012
comment
@GigaWatt: Если вы прочитаете вопрос OP, вы заметите: I want to match everything until after the second image tag - person anubhava; 25.02.2012
comment
странно, когда проверял на другом тестере, он показал абсолютно все, надо будет еще раз посмотреть - person Marius Miliunas; 27.02.2012