2010-07-09 2 views
1

J'ai besoin d'une fonction ou d'une expression rationnelle pour séparer des espaces dans une chaîne mais pour traiter une balise HTML comme un mot.Scinder des mots mais pas s'ils contiennent du HTML

$str = 'one two <a href="">three</a> four'; 
$x = explode(" ", $str); 
print_r($x); 

/* Returns: 
    Array 
(
    [0] => one 
    [1] => two 
    [2] => <a 
    [3] => href="">three</a> 
    [4] => four 
) 

Looking for way to return: 

Array 
(
    [0] => one 
    [1] => two 
    [2] => <a href="">three</a> 
    [3] => four 
) 

*/ 

Toutes les idées? Merci

+0

J'écris une fonction pour cela .. devrait être fait sous peu .. – Fosco

Répondre

2

C'est un peu plus simple alors ce qui précède, ne sont pas entièrement testé, mais donner un coup de feu.

$str = 'one two <a href="">three</a> four'; 

if(preg_match_all('%(<[^<]+.*?>|[^\s]+)%', $str, $matches)) { 
    array_shift($matches); 
    print_r($matches); 
} 

Voici une autre version que je l'ai testé pendant environ 5 minutes qui fonctionne un peu mieux:

$str = 'one two <a href="omfg hi I have spaces"> three</a> four <script type="javascript"> var a = "hello"; </script><random tag>la la la la<nested>hello?</nested></random tag>'; 

if(preg_match_all('%(<[^<]+.*?>|[^\s]+)%', preg_replace('%([\s]\<|\>[\s])%', '$1', $str), $matches)) { 
    array_shift($matches); 
    echo '<pre>'; 
    print_r($matches); 
    echo '</pre>'; 
} 
+0

qui fonctionne mais échoue s'il y a un
dans la chaîne – fire

0

Pourrait faire un regex remplacer sur les chaînes avant et après avoir explosé.

il irait en exploser comme

<a_href="">test</a> 

Au-delà des cas simples si vous parlez l'analyse syntaxique HTML qui n'est pas une bonne chose à faire avec RegEx.

Il ya beaucoup de questions ici sur l'analyse du code HTML ici. Peut-être pourriez-vous vous adapter à eux.

2
preg_split('/(<([A-Z][A-Z0-9]*)\b[^>]*>(.*?)</\1>)|| /, $text) 

Cela fonctionnerait parfois. Il se divise soit sur un ensemble de tags, soit sur un espace.

Cependant, ce que vous voulez n'est simplement pas aussi simple. Vous devriez couvrir tous les cas de balises imbriquées, les balises où le contenu a un espace ([a href] Foo Bar Baz [/ a]), et ainsi de suite. Pour cela, vous devez implémenter un analyseur XML (html) approprié.

Mais il me semble que vous avez un but avec ce tableau. Est-ce pour compter les "mots"? Si c'est le cas, la solution serait un appel de fonction beaucoup plus simple qui supprime tout le HTML du texte (strip_tags()) et ensuite appliquer votre séparateur de mots et les compter.

0

J'ai écrit et testé cette fonction personnalisée. Essayez-le et faites-moi savoir ce que vous en pensez.

function fireSplit($str) { 
    if (strpos($str,"<") === FALSE) return explode(" ",$str); 
    $str = trim($str); 
    $out = array(); 
    $curIdx = 0; 
    $endIdx = strlen($str) -1; 

    while ($curIdx <= $endIdx) { 
     if (substr($str,$curIdx,1) == " ") { 
       $curIdx += 1; 
       continue; 
     } 
     $nextspace = strpos($str," ",$curIdx); 
     $nexttag = strpos($str,"<",$curIdx); 
     $nexttag2 = strpos($str,"/",$nexttag); 
     $nexttag3 = strpos($str,">",$nexttag2); 

     if ($nextspace === FALSE) { 
       $out[] = substr($str,$curIdx); 
       $curIdx = $endIdx + 1; 
       continue; 
     } 

     if ($nexttag !== FALSE && $nexttag < $nextspace && $nexttag2 !== FALSE && $nexttag3 !== FALSE) { 
       $out[] = substr($str,$curIdx,($nexttag3 - $curIdx + 1)); 
       $curIdx = $nexttag3 + 1; 
     } else { 
       $out[] = substr($str,$curIdx,($nextspace - $curIdx)); 
       $curIdx = $nextspace; 
     } 
    } 
return $out; 
} 

J'ai appelé:

fireSplit("one two <a href=\"haha\">three</a> four"); 
fireSplit("a <b>strong</b> c d e f"); 

Il est revenu:

array(4) { 
    [0]=> 
    string(3) "one" 
    [1]=> 
    string(3) "two" 
    [2]=> 
    string(24) "<a href="haha">three</a>" 
    [3]=> 
    string(4) "four" 
} 

array(6) { 
    [0]=> 
    string(1) "a" 
    [1]=> 
    string(13) "<b>strong</b>" 
    [2]=> 
    string(1) "c" 
    [3]=> 
    string(1) "d" 
    [4]=> 
    string(1) "e" 
    [5]=> 
    string(1) "f" 
} 
+0

Des essais supplémentaires peuvent découvrir un scénario ou deux que je n'ai pas pris en compte ... J'ai juste pensé à un, où le prochain caractère après la fin de l'étiquette n'est pas un espace. – Fosco

+0

J'ai mis à jour la fonction pour tenir compte du scénario manqué dans mon dernier commentaire. – Fosco

+0

@Fosco fonction méchante prendre $ str = 'a fort c d e f'; Je reçois erreur fatale: la taille de la mémoire autorisée de 104857600 octets épuisés, donc il doit y avoir une fuite de mémoire quelque part ?! – fire

Questions connexes