7
votes

PHP et regex: diviser une ficelle par des virgules qui ne sont pas des crochets intérieurs (ainsi que des crochets imbriqués)

Il y a deux jours, j'ai commencé à travailler sur un analyseur de code et je suis coincé.

Comment puis-je diviser une chaîne par des virgules qui ne sont pas des crochets, laissez-moi vous montrer ce que je veux dire: p>

J'ai cette chaîne à analyser: p> xxx pré>

Je voudrais obtenir ce résultat: p> xxx pré>

mais je reçois : P>

array(
  "one"; 
  "two"; 
  "three"; 
  "(four"; 
  "(five"; 
  "six)"; 
  "(ten))";
  "seven"
)


0 commentaires

7 Réponses :


1
votes

J'ai peur que cela puisse être très difficile à analyser les crochets imbriqués comme un, deux, (trois, (quatre, cinq)) seulement avec regexp.


0 commentaires

5
votes

Vous ne pouvez pas, directement. Vous auriez besoin, au minimum, la largeur de la largeur variable et la dernière fois que je savais que le pcre de PHP n'a que la largeur de largeur fixe.

Ma première recommandation serait d'extraire d'abord des expressions parenthèses sur la chaîne. Je ne sais rien de votre problème réel, cependant, je ne sais pas si cela sera réalisable.


2 commentaires

Oui, c'était le piratage que je prévoyais d'utiliser. Remplacez les supports avec 1 $, 2 $ ou quelque chose de similaire, divisez la chaîne et de restaurer les crochets dans le résultat. Merci !


Le point est que ce que vous décrivez n'est pas une langue régulière, les expressions régulières sont donc mal adaptées. Ainsi, analyser toutes les pièces imbriquées d'abord n'est pas un "hack" mais la chose la plus sensée à faire.



2
votes

Je ne peux pas penser à un moyen de le faire en utilisant une seule regex, mais il est assez facile de bien pirater quelque chose qui fonctionne: xxx

Si vous l'invoquez comme ceci: xxx

it Sorties: xxx


3 commentaires

Merci beaucoup, cela devrait fonctionner. C'était comme ça que j'ai prévu de le faire en premier, mais je pensais qu'un moyen plus facile existe.


Votre méthode ne peut pas analyser "un, deux, trois, trois ((cinq), (quatre (six)), sept, huit, neuf". Je pense que la corrigue correcte serait une récursive: / ((([^ ()] + | (? R)) *) /.


Vous n'avez pas mentionné qu'il devait d'analyser les expressions récursives lorsque j'ai écrit cette réponse. Pourtant, d'autres ont certainement suggéré de meilleures solutions après avoir écrit cela.



2
votes

maladroit, mais il fait le travail ...

<?php

function split_by_commas($string) {
  preg_match_all("/\(.+?\)/", $string, $result); 
  $problem_children = $result[0];
  $i = 0;
  $temp = array();
  foreach ($problem_children as $submatch) { 
    $marker = '__'.$i++.'__';
    $temp[$marker] = $submatch;
    $string   = str_replace($submatch, $marker, $string);  
  }
  $result = explode(",", $string);
  foreach ($result as $key => $item) {
    $item = trim($item);
    $result[$key] = isset($temp[$item])?$temp[$item]:$item;
  }
  return $result;
}


$test = "one, two, three, (four, five, six), seven, (eight, nine), ten";

print_r(split_by_commas($test));

?>


0 commentaires

7
votes

hm ... ok déjà marqué comme répondu, mais comme vous avez demandé une solution facile, je vais essayer néanmoins: xxx

sortie xxx


1 commentaires

Merci beaucoup, votre aide est très appréciée. Mais maintenant, je me rends compte que je rencontrerai également des crochets imbriqués et que votre solution ne s'applique pas.



12
votes

Vous pouvez le faire plus facile:

$str = 'one, two, three, (four, (five, six), (ten)), seven';
$buffer = '';
$stack = array();
$depth = 0;
$len = strlen($str);
for ($i=0; $i<$len; $i++) {
    $char = $str[$i];
    switch ($char) {
    case '(':
        $depth++;
        break;
    case ',':
        if (!$depth) {
            if ($buffer !== '') {
                $stack[] = $buffer;
                $buffer = '';
            }
            continue 2;
        }
        break;
    case ' ':
        if (!$depth) {
            continue 2;
        }
        break;
    case ')':
        if ($depth) {
            $depth--;
        } else {
            $stack[] = $buffer.$char;
            $buffer = '';
            continue 2;
        }
        break;
    }
    $buffer .= $char;
}
if ($buffer !== '') {
    $stack[] = $buffer;
}
var_dump($stack);


5 commentaires

Oui, c'est plus facile, mais ne fonctionne pas en cas de supports imbriqués, comme: un, deux, trois, (quatre, (cinq, six), (dix)), sept)


C'est le point où vous devez utiliser un réel analyseur. Les expressions régulières ne peuvent pas compter ou gérer les états.


Je dois utiliser des expressions régulières. Les expressions régulières sont récursives et gourmandes, vous pouvez y accomplir.


Non tu ne peux pas. Bien sûr, il existe des fonctionnalités dans des implémentations modernes pouvant accomplir ce type d'équilibrage (? ...) msdn.microsoft.com/bs2twtah.aspx . Mais ils utilisent une machine à états et ce n'est plus une expression régulière de la manière classique.


Celui-ci est plus correct, mais ne fonctionne toujours pas pour une parenthèse imbriquée / [^ (,] * (?: ([^)] +))? [^),] * /



1
votes

Je pense que sa peine de noter que vous devriez toujours éviter les expressions régulières lorsque vous le pouvez éventuellement. À cette fin, vous devez savoir que pour PHP 5.3+, vous pouvez utiliser str_getcsv () . Toutefois, si vous travaillez avec des fichiers (ou des flux de fichiers), tels que des fichiers CSV, la fonction FgeCSV () pourrait être ce dont vous avez besoin, et c'est disponible depuis PHP4.

Enfin, je suis surpris que personne n'utilise Preg_split () , ou n'a-t-il pas fonctionné au besoin?


3 commentaires

Oui Ken, je veux utiliser preg_split (), mais quelle serait la regex qui ignore les virgules entre parenthèses?


Ah oui, bon point, après avoir essayé un min ou 2, je peux voir que c'est difficile avec les conditions énoncées.


Ouais tu as raison, j'ai aussi essayé votre solution et ne fonctionne pas. Merci encore.