2012-06-08 4 views
-4

Почему это регулярное выражение не работает?

У меня есть это регулярное выражение:

preg_match_all("/<\s*?img\s[^>]*?src=([\"']??)([^\"' >]*?)\1[^>]*?>/si", $content, $m); 

Идея заключается в том, чтобы найти все ссылки на изображения в кусок HTML. Учитывая это содержание:

<p> 
    <img alt="" src="/emailimg/interdigital_old.jpg" style="width: 377px; height: 245px; " />Some text here.</p><a href="site.html">test</a> 

после выполнения регулярных выражений, $ м представляет собой массив с 3-мя пустыми массивами на нем, но если я протестировать его с this site он говорит результат:

Array 
(
    [0] => Array 
     (
      [0] => <img alt="" src="/emailimg/interdigital_old.jpg" style="width: 377px; height: 245px; " /> 
     ) 

    [1] => Array 
     (
      [0] => " 
     ) 

    [2] => Array 
     (
      [0] => /emailimg/interdigital_old.jpg 
     ) 

) 

Что проблема? Это проблема конфигурации?

+5

Обязательная ссылка: http://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags - при всей серьезности, [DOM] (http: // php .net/manual/en/book.dom.php) и [XPath] (http://www.php.net/manual/en/class.domxpath.php) определенно будет лучше для этой работы. – DaveRandom

+0

Согласовано, использование XPath над DOM было бы предпочтительным способом для этого. –

+0

Согласитесь, но это небольшие фрагменты html, а не полная страница, их легко разобрать с помощью regex – Ivan

ответ

4

DOM/XPath (т.е. правильный) способ:

<?php 

    $html = ' 
<p> 
    <img alt="" src="/emailimg/interdigital_old.jpg" style="width: 377px; height: 245px; " />Some text here.</p><a href="site.html">test</a> 
'; 

    $dom = new DOMDocument('1.0'); 
    $dom->loadHTML($html); 

    $xpath = new DOMXPath($dom); 

    $links = array(); 
    foreach ($xpath->query('//img/@src') as $img) $links[] = $img->value; 
    print_r($links); 

Tested and working.

EDIT

Причина ваше регулярное выражение не работает двояко:

  1. Вы объявили свое регулярное выражение, используя двойные кавычки. Это часто приводит к тому, что вы не ожидаете и не совсем очевидны, поскольку строка с двойными кавычками будет интерполировать определенные escape-последовательности сама по себе, до передается в PCRE. Проблема, которая вызывала в вашем случае: \1 интерпретировалась как восьмеричное определение символа (как определено here), поэтому ваше выражение имело в нем символ буквально 0x01 (начало заголовка), а не строку \1, которую вы хотели получить PCRE для использования в качестве обратной ссылки.

    Я нахожу, что когда у меня есть такая проблема, хорошее место для начала - просто указать echo выражение на экран, чтобы увидеть, как PHP интерполирует строку, указанную в вашем скрипте. Here - это демонстрация этой конкретной проблемы.

  2. ([\"']??) - второй вопросительный знак разрушает его. Я на самом деле не уверен, что вы пытались сделать с этим, было ли это просто неправильным? Мне сложно понять, как именно это интерпретирует PCRE, и именно поэтому он ее разбивает, но достаточно сказать, что это так, и второй вопрос должен идти. FTR, эффект, который он имеет, заключается в том, что выражение по-прежнему соответствует тегу <img>, но следующая группа захвата (данные, которые вы действительно хотите) пуст.

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

  • <\s*?img - не-жадные * здесь бессмысленно, так как \s соответствует только пробельные и следующая последовательность будет альфа, просто <\s*img хватит. На самом деле я не уверен, что HTML-тегам разрешено иметь ведущие пробелы между открытием < и именем тега, но я думаю, что это не наносит вреда, чтобы позволить это, поскольку, вероятно, будут иметь место правильные парсеры.
  • \s[^>]*?src=(["']??) - как уже упоминалось, в группе захвата ?? нарушает выражение, и я не уверен, что вы пытались сделать с ним в первую очередь. Кроме того, я думаю, что не-жадный * бессмысленен, потому что тег будет заканчиваться >, и если мы не нашли src, к концу это все равно не соответствует. Плюс, если мы разрешаем пробелы в тех местах, где это не должно быть, но парсеры, вероятно, позволят, мы должны, вероятно, разрешить это вокруг =. Я переписал бы это \s[^>]*src\s*=\s*(["']?).
  • ([^"' >]*?)\1 - Предполагая, что вы обеспокоены тем, что имеете возможность обрабатывать некотируемые атрибуты, никаких жалоб здесь нет. Конечно, если вы do знаете, что атрибуты всегда будут указаны, вы можете просто использовать ([^\1]*?)\1 и отбросить ? из предыдущей группы захвата, где мы определили используемый тип цитирования.
  • [^>]*?> - здесь нет претензий.
  • /si - модификатор s бессмысленен, поскольку в выражении нет .. Это не наносит вреда, но и не помогает, так что это лишнее.

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

/<\s*img\s[^>]*src\s*=\s*(["']?)([^"' >]*?)\1[^>]*>/i 

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

$expr = '/<\s*img\s[^>]*src\s*=\s*(["\']?)([^"\' >]*?)\1[^>]*>/i'; 

... который works nicely,, между прочим.

Теперь я все еще утверждаю, что метод DOM лучше даже с учетом дополнительного кода, так как он, вероятно, поймает крайние случаи, о которых забыл мой regex skillz. Хотя, по общему признанию, регулярное выражение похоже somewhat faster.

+0

Это не отвечает на вопрос, но, в любом случае, это лучший ответ, поэтому я принимаю его – Ivan

+0

@Ivan См. Вышеизложенное – DaveRandom

+0

Отлично, теперь я понимаю, что случилось, спасибо. ([\ "'] ??) состоит в том, чтобы попытаться получить все возможности одиночных, двойных и не-кавычек, но есть дополнительный вопросительный знак (я делал некоторые тесты, чтобы выяснить, в чем проблема) – Ivan

Смежные вопросы