2015-04-08 1 views
3

Я хотел бы построить функцию, которая берет строку и обертывает каждую ее буквы в <span>, за исключением пробелов и тегов HTML (в моем случае, <br> теги).Оберните каждую букву строки в теге, избегая HTML-тегов

Итак:

"Hi <br> there." 

... должно стать

"<span>H</span><span>i</span> <br> <span>t</span><span>h</span><span>e</span><span>r</span><span>e</span><span>.</span>" 

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

Ближайшее, что я нашел, было ответом Neverever here.

Однако, похоже, что это не так хорошо, так как каждый символ тегов <br> был обернут в <span>, и он не соответствовал подчеркнутым символам, таким как éààï.

Как я могу продолжить это? И почему разбор HTML-тегов с регулярным выражением кажется настолько неправильным?

+0

Возможно, вам стоит описать, чего вы хотите достичь, вместо того чтобы просить помощи в решении, которое, по вашему мнению, может работать. – dognose

ответ

1

Вы можете рассмотреть возможность использования DOMDocument для разбора HTML и завернуть только символы в пределах стоимости DOMText узлов. См. Комментарии в коде.

// Define source 
$source = 'H&iuml; <br/> thérè.'; 

// Create DOM document and load HTML string, hinting that it is UTF-8 encoded. 
// We need a root element for this so we wrap the source in a temporary <div>. 
$hint = '<meta http-equiv="content-type" content="text/html; charset=utf-8">'; 
$dom = new DOMDocument(); 
$dom->loadHTML($hint . "<div>" . $source . "</div>"); 

// Get contents of temporary root node 
$root = $dom->getElementsByTagName('div')->item(0); 

// Loop through children 
$next = $root->firstChild; 
while ($node = $next) { 
    $next = $node->nextSibling; // Save for next while iteration 

    // We are only interested in text nodes (not <br/> etc) 
    if ($node->nodeType == XML_TEXT_NODE) { 
     // Wrap each character of the text node (e.g. "Hi ") in a <span> of 
     // its own, e.g. "<span>H</span><span>i</span><span> </span>" 
     foreach (preg_split('/(?<!^)(?!$)/u', $node->nodeValue) as $char) { 
      $span = $dom->createElement('span', $char); 
      $root->insertBefore($span, $node); 
     } 
     // Drop text node (e.g. "Hi ") leaving only <span> wrapped chars 
     $root->removeChild($node); 
    } 
} 

// Back to string via SimpleXMLElement (so that the output is more similar to 
// the source than would be the case with $root->C14N() etc), removing temporary 
// root <div> element and space-only spans as well. 
$withSpans = simplexml_import_dom($root)->asXML(); 
$withSpans = preg_replace('#^<div>|</div>$#', '', $withSpans); 
$withSpans = preg_replace('#<span> </span>#', ' ', $withSpans); 

echo $withSpans, PHP_EOL; 

Выход:

<span>H</span><span>ï</span> <br/> <span>t</span><span>h</span><span>é</span><span>r</span><span>è</span><span>.</span> 
+0

Ничего себе, спасибо! Раньше я никогда не использовал класс DOMDocument, так что вы не только ответили на мой вопрос, но и заставили меня открыть то, о чем я не знал. Одна вещь: подчеркнутые символы [éèà ...], похоже, не подобраны, и функция печатает символ ' ' всякий раз, когда встречается с ней. Любая идея, как я могу это решить? Еще раз спасибо! –

+0

DOMDocument действительно может быть весьма удобным. Посмотрите также на DOMXPath, пока вы на нем :) Функция loadHTML() предполагает ISO-8859-1 по умолчанию, поэтому для работы символов UTF-8 вам нужно будет преобразовать строку, используя '$ source = mb_convert_encoding ($ utf_8, 'HTML-ENTITIES', 'UTF-8'); 'или намекнуть на кодировку в источнике. Я обновил свой ответ с помощью последнего решения. – mhall

+0

... и да, 'str_split' был заменен на' preg_split' с поддержкой многобайтовой поддержки. Схватил из [примера] (http://tz1.php.net/manual/en/function.mb-split.php) в руководстве по PHP. – mhall

2

Вы могли бы попробовать что-то вроде ...

<?php 

    $str = "Hi <br> there."; 
    $newstr = ""; 
    $notintag = true; 
    for ($i = 0; $i < strlen($str); $i++) { 
    if (substr($str,$i,1) == "<") { 
     $notintag = false; 
    } 
    if (($notintag) and (substr($str,$i,1) != " ")) { 
     $newstr .= "<span>" . substr($str,$i,1) . "</span>"; 
    } else { 
     $newstr .= substr($str,$i,1); 
    } 

    if (substr($str,$i,1) == ">") { 
     $notintag = true; 
    } 


    } 
    echo $newstr; 

?> 
2

Вы можете достичь результата с ([^\s>])(?!(?:[^<>]*)?>) регулярным выражением. Чтобы включить поддержку Unicode, просто использовать его с u вариант:

<?php 
    $re = "/([^\\s>])(?!(?:[^<>]*)?>)/u"; 
    $str = "Hi <br> there."; 
    $subst = "<span>$1</span>"; 
    $result = preg_replace($re, $subst, $str); 
    echo $result; 
?> 

Здесь вы можете найти regex explanation and demo.

См. sample program без поддержки Юникода и здесь one with Unicode support (разница в опции u).

+0

Спасибо! Что касается ответа mhall, то ваш, кажется, отлично работает, пока не начнутся акцентированные персонажи, такие как é или à. –

+0

Чтобы включить поддержку Unicode в PHP regex, вам нужно добавить модификатор 'u' в конец регулярного выражения:'/([^ \\ s>]) (?! (?: [^ <>] *)?>)/u'.См. Обновленную примерную программу по адресу http://www.tutorialspoint.com/execute_php_online.php?PID=0Bw_CjBb95KQMRFByVWxEM09CY2c. –

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