web-dev-qa-db-fra.com

PHP exploser la chaîne, mais traiter les mots entre guillemets comme un seul mot

Comment puis-je exploser la chaîne suivante:

Lorem ipsum "dolor sit amet" consectetur "adipiscing elit" dolor

dans

array("Lorem", "ipsum", "dolor sit amet", "consectetur", "adipiscing elit", "dolor")

Pour que le texte entre guillemets soit traité comme un seul mot.

Voici ce que j'ai pour l'instant:

$mytext = "Lorem ipsum %22dolor sit amet%22 consectetur %22adipiscing elit%22 dolor"
$noquotes = str_replace("%22", "", $mytext");
$newarray = explode(" ", $noquotes);

mais mon code divise chaque mot en un tableau. Comment puis-je faire des mots entre guillemets traités comme un seul mot?

46
timofey

Vous pouvez utiliser une preg_match_all(...):

$text = 'Lorem ipsum "dolor sit amet" consectetur "adipiscing \\"elit" dolor';
preg_match_all('/"(?:\\\\.|[^\\\\"])*"|\S+/', $text, $matches);
print_r($matches);

qui produira:

Array
(
    [0] => Array
        (
            [0] => Lorem
            [1] => ipsum
            [2] => "dolor sit amet"
            [3] => consectetur
            [4] => "adipiscing \"elit"
            [5] => dolor
        )

)

Et comme vous pouvez le voir, il représente également les guillemets échappés à l'intérieur des chaînes entre guillemets.

MODIFIER

Une brève explication:

"           # match the character '"'
(?:         # start non-capture group 1 
  \\        #   match the character '\'
  .         #   match any character except line breaks
  |         #   OR
  [^\\"]    #   match any character except '\' and '"'
)*          # end non-capture group 1 and repeat it zero or more times
"           # match the character '"'
|           # OR
\S+         # match a non-whitespace character: [^\s] and repeat it one or more times

Et en cas de correspondance %22 au lieu de guillemets doubles, vous feriez:

preg_match_all('/%22(?:\\\\.|(?!%22).)*%22|\S+/', $text, $matches);
82
Bart Kiers

Cela aurait été beaucoup plus facile avec str_getcsv() .

$test = 'Lorem ipsum "dolor sit amet" consectetur "adipiscing elit" dolor';
var_dump(str_getcsv($test, ' '));

Vous donne

array(6) {
  [0]=>
  string(5) "Lorem"
  [1]=>
  string(5) "ipsum"
  [2]=>
  string(14) "dolor sit amet"
  [3]=>
  string(11) "consectetur"
  [4]=>
  string(15) "adipiscing elit"
  [5]=>
  string(5) "dolor"
}
75
Petah

Vous pouvez également essayer cette fonction d'éclatement multiple

function multiexplode ($delimiters,$string)
{

$ready = str_replace($delimiters, $delimiters[0], $string);
$launch = explode($delimiters[0], $ready);
return  $launch;
}

$text = "here is a sample: this text, and this will be exploded. this also | this one too :)";
$exploded = multiexplode(array(",",".","|",":"),$text);

print_r($exploded);
4
Nikz

Je suis venu ici avec un problème de division de chaîne complexe similaire à celui-ci, mais aucune des réponses ici n'a fait exactement ce que je voulais - j'ai donc écrit la mienne.

Je le poste ici juste au cas où il serait utile à quelqu'un d'autre.

C'est probablement une façon très lente et inefficace de le faire - mais cela fonctionne pour moi.

function explode_adv($openers, $closers, $togglers, $delimiters, $str)
{
    $chars = str_split($str);
    $parts = [];
    $nextpart = "";
    $toggle_states = array_fill_keys($togglers, false); // true = now inside, false = now outside
    $depth = 0;
    foreach($chars as $char)
    {
        if(in_array($char, $openers))
            $depth++;
        elseif(in_array($char, $closers))
            $depth--;
        elseif(in_array($char, $togglers))
        {
            if($toggle_states[$char])
                $depth--; // we are inside a toggle block, leave it and decrease the depth
            else
                // we are outside a toggle block, enter it and increase the depth
                $depth++;

            // invert the toggle block state
            $toggle_states[$char] = !$toggle_states[$char];
        }
        else
            $nextpart .= $char;

        if($depth < 0) $depth = 0;

        if(in_array($char, $delimiters) &&
           $depth == 0 &&
           !in_array($char, $closers))
        {
            $parts[] = substr($nextpart, 0, -1);
            $nextpart = "";
        }
    }
    if(strlen($nextpart) > 0)
        $parts[] = $nextpart;

    return $parts;
}

L'utilisation est la suivante. explode_adv prend 5 arguments:

  1. Un tableau de caractères qui ouvrent un bloc - par exemple [, (, etc.
  2. Un tableau de caractères qui ferme un bloc - par exemple ], ), etc.
  3. Un tableau de caractères qui bascule un bloc - par exemple ", ', etc.
  4. Un tableau de caractères qui devrait provoquer une scission dans la partie suivante.
  5. La chaîne sur laquelle travailler.

Cette méthode a probablement des défauts - les modifications sont les bienvenues.

1

Dans certaines situations, le peu connu token_get_all() peut s'avérer utile:

$tokens = token_get_all("<?php $text ?>");
$separator = ' ';
$items = array();
$item = "";
$last = count($tokens) - 1;
foreach($tokens as $index => $token) {
    if($index != 0 && $index != $last) {
        if(count($token) == 3) {
            if($token[0] == T_CONSTANT_ENCAPSED_STRING) {
                $token = substr($token[1], 1, -1);
            } else {
                $token = $token[1];
            }
        }
        if($token == $separator) {
            $items[] = $item;
            $item = "";
        } else {
            $item .= $token;
        }
    }
}

Résultats:

Array
(
    [0] => Lorem
    [1] => ipsum
    [2] => dolor sit amet
    [3] => consectetur
    [4] => adipiscing elit
    [5] => dolor
)
1
cleong