2010-07-10 7 views
0

Je travaille sur un modèle de classe et j'ai un problème en essayant d'analyser une liste de chaînes entre guillemets dans une liste d'arguments. Prenons par exemple la chaîne:Expression rationnelle PHP pour faire correspondre des chaînes doubles et/ou simples entre guillemets

$string = 'VAR_SELECTED, \'Hello m\'lady\', "null"'; 

Je vais avoir un problème à venir avec un regex qui extrait la chaîne « Bonjour m'dame » et « null ». Le plus proche que j'ai est

$string = 'VAR_SELECTED, \'Hello m\'lady\', "null", \'TE\'ST\''; 
preg_match_all('/(?:[^\']|\\\\.)+|(?:[^"]|\\\\.)+/', $string, $matches); 
print_r($matches); 

qui délivre en sortie:

Array 
(
    [0] => Array 
     (
      [0] => VAR_SELECTED, 
      [1] => 'Hello m'lady', 
      [2] => "null", 
      [3] => 'TE'ST' 
     ) 

) 

Cependant un cas plus complexe:

$string = 'VAR_SELECTED, \'Hello "Father"\', "Hello \'Luke\'"'; 
preg_match_all('/(?:[^\']|\\\\.)+|(?:[^"]|\\\\.)+/', $string, $matches); 
print_r($matches); 

sorties:

Array 
(
    [0] => Array 
     (
      [0] => VAR_SELECTED, 
      [1] => 'Hello 
      [2] => "Father" 
      [3] => ', 
      [4] => "Hello 
      [5] => 'Luke' 
      [6] => " 
     ) 

) 

Quelqu'un peut-il me aider résoudre ce problème? Les expressions régulières multiples sont-elles la voie à suivre?

Modifier Peut-être serait-il plus facile de remplacer les virgules dans les chaînes par un espace réservé, puis de les casser avec une explosion?

Édition 2 Juste pensé à une option non sécurisée simple (que je ne vais pas utiliser), mais génère une erreur E_NOTICE.

$string = 'return array(VAR_SELECTED, \'Hello , "Father"\', "Hello \'Luke\'4");'; 
$string = eval($string); 
print_r($string); 
+0

Comment sauriez-vous que l'apostrophe de "m'lady" est contenue entre guillemets dans l'exemple que vous avez donné ci-dessus - cela ne se désagrégerait-il pas s'il y avait beaucoup plus de guillemets simples dans la chaîne? –

+0

C'est un peu le point que j'essaie de contourner. – buggedcom

Répondre

3

Essayez ceci:

/(?<=^|[\s,])(?:(['"]).*?\1|[^\s,'"]+)(?=[\s,]|$)/ 

Ou, comme une chaîne unique cité PHP littérale:

'/(?<=^|[\s,])(?:([\'"]).*?\1|[^\s,\'"]+)(?=[\s,]|$)/' 

Ce regex donne le résultat souhaité, mais Je pense que vous allez à propos de ce problème. Généralement, si une chaîne entre guillemets doit contenir un caractère littéral, la citation est échappée, soit avec une barre oblique inverse, soit avec une autre citation. Vous ne faites pas cela, alors j'ai dû utiliser un hack fragile basé sur des lookarounds. Êtes-vous sûr que les données ne sont pas censées ressembler à ceci? En y pensant, PHP n'a-t-il pas de support intégré pour les données CSV?

+0

Le problème est qu'il dit que les virgules peuvent être dans les chaînes elles-mêmes, avec des citations et des mélanges sans échappement Je pense presque qu'il a besoin d'explorer la chaîne pour trouver les caractères "start" inégalés, mais c'est horrible C++ pour php – Caladain

+0

Merci, mais je pense que votre regex l'a. Parser, et une fonction str (php> = 5.3), cependant avec ce problème php échoue toujours à analyser correctement les données que les enceintes peuvent être soit un "ou un" dans la même liste d'arguments, idiot je sais mais les concepteurs de modèles sont stupides . @Caladain - Je pense que cela résout en fait. Essayez cette chaîne avec un preg_match. $ string = 'VAR_SELECTED, \' Bonjour, "Père" \ ', "Enfer, o \' Luke \ '", \', "\ ''; – buggedcom

+0

Considérons la chaîne: $ string = 'VAR_SELECTED, \' Bonjour, \ '"Fa \' ther" \ ', "Bonjour, \' Luke," mon Fils "\ '"'; Ne se casse pas bien Alan: L'initiative d'Alan est correcte ici, je pense, et les retours de piste peuvent être très frêles Avoir des données formatées et échappées uniformément rend le problème beaucoup plus simple, sinon vous ne pouvez jamais garantir que vous ne recevrez pas une chaîne malformée (parfois pour injecter du code, parfois parce que les utilisateurs sont des singes qui martèlent le clavier et s'en fichent à propos des choses qui s'échappent correctement) – Caladain

0

Vous voulez utiliser un back reference dans la chaîne de match.

preg_match_all('@([\'"]).*[^\\\\]\[email protected]', $string, $matches); 

Cela commencera correspondant à la première instance de « ou" et correspondre à la plus longue chaîne qui se termine par une mise en correspondance » ou« que n'est pas se sont échappés.

Array (
[0] => Array 
    (
     [0] => 'Hello m'lady', "null", 'TE'ST' 
    ) 

[1] => Array 
    (
     [0] => ' 
    ) 
+0

hmm, les correspondances requises sont «Hello m'lady», «null» et «TE'ST» en tant que chaînes individuelles, pas une longue. – buggedcom

+0

Oh bien. J'ai mal lu quel était le problème. C'est cette vieille chose de handicap de 1 bière. –

1

Voilà comment je le ferais:

Briser la tâche en les étapes de composants que vous voulez prendre:

1.) Exploser la chaîne sur des virgules.

For 'VAR_SELECTED, \'Hello m\'lady\', "null"' this gives me 
[0]=>"VAR_SELECTED" 
[1]=>" \'Hello m\'lady\'" 
[2]=>" "null"" 

For 'VAR_SELECTED, \'Hello "Father"\', "Hello \'Luke\'"' this gives me 
[0]=>"VAR_SELECTED" 
[1]=>" \'Hello "Father"\'" 
[2]=>" "Hello \'Luke\'"" 

2.) Exécuter gréer tous les trois pour se débarrasser de tout les espaces

For 'VAR_SELECTED, \'Hello m\'lady\', "null"' this gives me 
[0]=>"VAR_SELECTED" 
[1]=>"\'Hello m\'lady\'" 
[2]=>""null"" 

For 'VAR_SELECTED, \'Hello "Father"\', "Hello \'Luke\'"' this gives me 
[0]=>"VAR_SELECTED" 
[1]=>"\'Hello "Father"\'" 
[2]=>""Hello \'Luke\'"" 

3.) Exécuter str_replace ("\", » », $ text) pour se débarrasser des barres obliques . (Supprimer spaces..added pour une meilleure lisibilité que, ce qui devrait être une barre oblique nue et un « vide » string)

For 'VAR_SELECTED, \'Hello m\'lady\', "null"' this gives me 
[0]=>"VAR_SELECTED" 
[1]=>"'Hello m'lady'" 
[2]=>""null"" 

For 'VAR_SELECTED, \'Hello "Father"\', "Hello \'Luke\'"' this gives me 
[0]=>"VAR_SELECTED" 
[1]=>"'Hello "Father"'" 
[2]=>""Hello 'Luke'"" 

4.) Exécuter à nouveau garniture, couper seulement (texte $, « ' » ") (enlever spaces..added pour seulement la lisibilité)

For 'VAR_SELECTED, \'Hello m\'lady\', "null"' this gives me 
[0]=>"VAR_SELECTED" 
[1]=>"Hello m'lady" 
[2]=>"null" 

For 'VAR_SELECTED, \'Hello "Father"\', "Hello \'Luke\'"' this gives me 
[0]=>"VAR_SELECTED" 
[1]=>"Hello "Father"" 
[2]=>"Hello 'Luke'" 

Je ne l'ai pas testé, mais la logique est saine. Un moyen rapide et sale pour tester 98% de l'ensemble de l'expression rationnelle (dans mon expérience) est d'utiliser http://rubular.com/ C'est un super site.Habituellement, s'il commence à s'étouffer sur une regex, c'est mon premier signe que je devrais résoudre le problème plus bas. (C'est juste opinion ~ enfile ignifugé costume ~)

+0

Cela fonctionnerait si les chaînes ne contiennent pas elles-mêmes de virgules, sinon vous auriez aussi des chaînes cassées. – buggedcom

+0

En fait, vous devez être en mesure d'avoir un motif ou une chaîne qui délimite chaque "champ" dans la chaîne. Comma, &,! .. quelque chose. Sinon, aucune approche ne fonctionnera ... l'ordinateur est trop bête. Si vous donnez votre personnage ou motif de délination au milieu de la chaîne, une regex ou une autre méthode va se "diviser" sur ce modèle .. – Caladain

+0

sûrement les citations font cela? vous voulez dire une chaîne rare comme # ou quelque chose – buggedcom