PHP preg_split по пробелам, но не внутри тегов

я использую preg_split("/\"[^\"]*\"(*SKIP)(*F)|\x20/", $input_line); и запускаю его на phpliveregex.com, он создает массив:

array(10
  0=><b>test</b>
  1=>or
  2=><em>oh
  3=>yeah</em>
  4=>and
  5=><i>
  6=>oh
  7=>yeah
  8=></i>
  9=>"ye we 'hold' it"
)

НЕ то, что я хочу, это должно быть разделено пробелами только вне тегов html, например:

array(5
  0=><b>test</b>
  1=>or
  2=><em>oh yeah</em>
  3=>and
  4=><i>oh yeah</i>
  5=>"ye we 'hold' it"
)

в этом регулярном выражении я могу добавить исключение только в «двойную кавычку», но мне действительно нужна помощь, чтобы добавить больше, например тег <img/><a></a><pre></pre><code></code><strong></strong><b></b><em></em><i></i>

Любое объяснение того, как работает это регулярное выражение, также приветствуется.


person Al-Jazary    schedule 19.06.2016    source источник
comment
Просто используйте < и >: preg_split("/<[^<]*>(*SKIP)(*F)| /", $input_line);   -  person Wiktor Stribiżew    schedule 19.06.2016
comment
Используйте DOMDocument и DOMXPath.   -  person Casimir et Hippolyte    schedule 19.06.2016


Ответы (2)


Проще использовать DOMDocument, так как вам не нужно описывать, что такое HTML-тег и как он выглядит. Вам нужно только проверить nodeType. Если это textNode, разделите его с помощью preg_match_all (это более удобно, чем создавать шаблон для preg_split):

$html = 'spaces in a text node <b>test</b> or <em>oh yeah</em> and <i>oh yeah</i>
"ye we \'hold\' it"
"unclosed double quotes at the end';

$dom = new DOMDocument;
$dom->loadHTML('<div>' . $html . '</div>', LIBXML_HTML_NOIMPLIED);

$nodeList = $dom->documentElement->childNodes;

$results = [];

foreach ($nodeList as $childNode) {
    if ($childNode->nodeType == XML_TEXT_NODE &&
        preg_match_all('~[^\s"]+|"[^"]*"?~', $childNode->nodeValue, $m))
        $results = array_merge($results, $m[0]);
    else
        $results[] = $dom->saveHTML($childNode);
}

print_r($results);

Примечание. Я выбрал поведение по умолчанию, когда часть двойной кавычки остается незакрытой (без закрывающей кавычки), вы можете изменить ее.

Примечание 2: Иногда константы LIBXML_ не определены. Вы можете решить эту проблему, протестировав ее раньше и определив ее при необходимости:

if (!defined('LIBXML_HTML_NOIMPLIED'))
    define('LIBXML_HTML_NOIMPLIED', 8192);
person Casimir et Hippolyte    schedule 19.06.2016
comment
о, мне нужна помощь, этот код работает отлично, так как в localhost, но после перехода на сервер возникла проблема с dom. Warning: DOMDocument::loadHTML() expects parameter 2 to be long, string given in /home/u74 - person Al-Jazary; 27.06.2016
comment
@ Al-Jazary: в некоторых конфигурациях константы LIBXML_... не определены (поэтому вы получаете это сообщение). В этом случае проверьте, существует ли эта константа перед скриптом с помощью defined('constantName'), и если она возвращает false, определите ее с помощью define(...). LIBXML_HTML_NOIMPLIED значение равно 8192. - person Casimir et Hippolyte; 27.06.2016
comment
Я понял, но функция не работает. так что это должна быть версия хоста libxml. следует обновить до версии 2.7.7, как сказано в PHP web. Only available in Libxml >= 2.7.7 (as of PHP >= 5.4.0) - person Al-Jazary; 28.06.2016

Описание

Вместо использования команды разделения просто сопоставьте нужные разделы.

<(?:(?:img)(?=[\s>\/])(?:[^>=]|=(?:'[^']*'|"[^"]*"|[^'"\s>]*))*\s?\/?>|(a|span|pre|code|strong|b|em|i)(?=[\s>\\])(?:[^>=]|=(?:'[^']*'|"[^"]*"|[^'"\s>]*))*\s?\/?>.*?<\/\1>)|(?:"[^"]*"|[^"<]*)*

Визуализация регулярных выражений

Пример

Текущая демонстрация

https://regex101.com/r/bK8iL3/1

Образец текста

Обратите внимание на сложный пограничный случай во втором абзаце.

<b>test</b> or <strong> this </strong><em> oh yeah </em> and <i>oh yeah</i> Here we are "ye we 'hold' it"

some<img/>gfsf<a html="droids.html" onmouseover=' var x=" Not the droid I am looking for " ; '>droides</a><pre></pre><code></code><strong></strong><b></b><em></em><i></i>

Примеры совпадений

MATCH 1
0.  [0-11]  `<b>test</b>`

MATCH 2
0.  [11-15] ` or `

MATCH 3
0.  [15-38] `<strong> this </strong>`

MATCH 4
0.  [38-56] `<em> oh yeah </em>`

MATCH 5
0.  [56-61] ` and `

MATCH 6
0.  [61-75] `<i>oh yeah</i>`

MATCH 7
0.  [75-111]    ` Here we are "ye we 'hold' it" some`

MATCH 8
0.  [111-117]   `<img/>`

MATCH 9
0.  [117-121]   `gfsf`

MATCH 10
0.  [121-213]   `<a html="droids.html" onmouseover=' var x=" Not the droid I am looking for " ; '>droides</a>`

MATCH 11
0.  [213-224]   `<pre></pre>`

MATCH 12
0.  [224-237]   `<code></code>`

MATCH 13
0.  [237-254]   `<strong></strong>`

MATCH 14
0.  [254-261]   `<b></b>`

MATCH 15
0.  [261-270]   `<em></em>`

MATCH 16
0.  [270-277]   `<i></i>`

Объяснение

NODE                     EXPLANATION
----------------------------------------------------------------------
  <                        '<'
----------------------------------------------------------------------
  (?:                      group, but do not capture:
----------------------------------------------------------------------
    (?:                      group, but do not capture:
----------------------------------------------------------------------
      img                      'img'
----------------------------------------------------------------------
    )                        end of grouping
----------------------------------------------------------------------
    (?=                      look ahead to see if there is:
----------------------------------------------------------------------
      [\s>\/]                  any character of: whitespace (\n, \r,
                               \t, \f, and " "), '>', '\/'
----------------------------------------------------------------------
    )                        end of look-ahead
----------------------------------------------------------------------
    (?:                      group, but do not capture (0 or more
                             times (matching the most amount
                             possible)):
----------------------------------------------------------------------
      [^>=]                    any character except: '>', '='
----------------------------------------------------------------------
     |                        OR
----------------------------------------------------------------------
      =                        '='
----------------------------------------------------------------------
      (?:                      group, but do not capture:
----------------------------------------------------------------------
        '                        '\''
----------------------------------------------------------------------
        [^']*                    any character except: ''' (0 or more
                                 times (matching the most amount
                                 possible))
----------------------------------------------------------------------
        '                        '\''
----------------------------------------------------------------------
       |                        OR
----------------------------------------------------------------------
        "                        '"'
----------------------------------------------------------------------
        [^"]*                    any character except: '"' (0 or more
                                 times (matching the most amount
                                 possible))
----------------------------------------------------------------------
        "                        '"'
----------------------------------------------------------------------
       |                        OR
----------------------------------------------------------------------
        [^'"\s>]*                any character except: ''', '"',
                                 whitespace (\n, \r, \t, \f, and "
                                 "), '>' (0 or more times (matching
                                 the most amount possible))
----------------------------------------------------------------------
      )                        end of grouping
----------------------------------------------------------------------
    )*                       end of grouping
----------------------------------------------------------------------
    \s?                      whitespace (\n, \r, \t, \f, and " ")
                             (optional (matching the most amount
                             possible))
----------------------------------------------------------------------
    \/?                      '/' (optional (matching the most amount
                             possible))
----------------------------------------------------------------------
    >                        '>'
----------------------------------------------------------------------
   |                        OR
----------------------------------------------------------------------
    (                        group and capture to \1:
----------------------------------------------------------------------
      a                        'a'
----------------------------------------------------------------------
     |                        OR
----------------------------------------------------------------------
      span                     'span'
----------------------------------------------------------------------
     |                        OR
----------------------------------------------------------------------
      pre                      'pre'
----------------------------------------------------------------------
     |                        OR
----------------------------------------------------------------------
      code                     'code'
----------------------------------------------------------------------
     |                        OR
----------------------------------------------------------------------
      strong                   'strong'
----------------------------------------------------------------------
     |                        OR
----------------------------------------------------------------------
      b                        'b'
----------------------------------------------------------------------
     |                        OR
----------------------------------------------------------------------
      em                       'em'
----------------------------------------------------------------------
     |                        OR
----------------------------------------------------------------------
      i                        'i'
----------------------------------------------------------------------
    )                        end of \1
----------------------------------------------------------------------
    (?=                      look ahead to see if there is:
----------------------------------------------------------------------
      [\s>\\]                  any character of: whitespace (\n, \r,
                               \t, \f, and " "), '>', '\\'
----------------------------------------------------------------------
    )                        end of look-ahead
----------------------------------------------------------------------
    (?:                      group, but do not capture (0 or more
                             times (matching the most amount
                             possible)):
----------------------------------------------------------------------
      [^>=]                    any character except: '>', '='
----------------------------------------------------------------------
     |                        OR
----------------------------------------------------------------------
      =                        '='
----------------------------------------------------------------------
      (?:                      group, but do not capture:
----------------------------------------------------------------------
        '                        '\''
----------------------------------------------------------------------
        [^']*                    any character except: ''' (0 or more
                                 times (matching the most amount
                                 possible))
----------------------------------------------------------------------
        '                        '\''
----------------------------------------------------------------------
       |                        OR
----------------------------------------------------------------------
        "                        '"'
----------------------------------------------------------------------
        [^"]*                    any character except: '"' (0 or more
                                 times (matching the most amount
                                 possible))
----------------------------------------------------------------------
        "                        '"'
----------------------------------------------------------------------
       |                        OR
----------------------------------------------------------------------
        [^'"\s>]*                any character except: ''', '"',
                                 whitespace (\n, \r, \t, \f, and "
                                 "), '>' (0 or more times (matching
                                 the most amount possible))
----------------------------------------------------------------------
      )                        end of grouping
----------------------------------------------------------------------
    )*                       end of grouping
----------------------------------------------------------------------
    \s?                      whitespace (\n, \r, \t, \f, and " ")
                             (optional (matching the most amount
                             possible))
----------------------------------------------------------------------
    \/?                      '/' (optional (matching the most amount
                             possible))
----------------------------------------------------------------------
    >                        '>'
----------------------------------------------------------------------
    .*?                      any character (0 or more times (matching
                             the least amount possible))
----------------------------------------------------------------------
    <                        '<'
----------------------------------------------------------------------
    \/                       '/'
----------------------------------------------------------------------
    \1                       what was matched by capture \1
----------------------------------------------------------------------
    >                        '>'
----------------------------------------------------------------------
  )                        end of grouping
----------------------------------------------------------------------
 |                        OR
----------------------------------------------------------------------
  (?:                      group, but do not capture (0 or more times
                           (matching the most amount possible)):
----------------------------------------------------------------------
    "                        '"'
----------------------------------------------------------------------
    [^"]*                    any character except: '"' (0 or more
                             times (matching the most amount
                             possible))
----------------------------------------------------------------------
    "                        '"'
----------------------------------------------------------------------
   |                        OR
----------------------------------------------------------------------
    [^"<]*                   any character except: '"', '<' (0 or
                             more times (matching the most amount
                             possible))
----------------------------------------------------------------------
  )*                       end of grouping
----------------------------------------------------------------------
person Ro Yo Mi    schedule 19.06.2016