Page 1 sur 2

"explode" sur caractère sauf si entre apostrophes

Posté : 30 juin 2012, 17:12
par JABch
Bonjour,

Je cherche à sortir un tableau de requetes SQL à partir du contenu d'un fichier où les requêtes sont séparées par ";" au moyen de "$tableau = explode(";", $requetes);".
Cela fonctionne bien, sauf si l'intérieur d'une requete contient ";" entre apostrophes comme par exemple
INSERT INTO ´table´ VALUES (12345, 'abc', 'ici il y a un ; qui pose problème', 'xyz');
car dans cas explode renvoie un élément "INSERT INTO ´table´ VALUES (12345, 'abc', 'ici il y a un " ! :roll:

Je suppose que je devrais utiliser "$tableau = preg_split($expression, $requetes)" mais je ne parviens pas à trouver l'$expression qui va bien. :oops:
Pouvez-vous m'aider svp ?

A+
Jacky 8-|

Re: "explode" sur caractère sauf si entre apostrophes

Posté : 30 juin 2012, 18:00
par Mazarini
Les éléments qui comportent un nombre impair de quote doivent etre concaténer avec le suivant et ignoré. Il ne faut pas prendre en compte les \'.

Re: "explode" sur caractère sauf si entre apostrophes

Posté : 30 juin 2012, 18:16
par JABch
Merci, c'est une piste intéressante.
Je vois donc que je peux continuer d'utiliser explode(";", $s) mais ensuite refais un explode("'", (guillemet+apostrophe+guillemet) sur chaque élément pour compter les '.
S'il y en a un nombre impair, je concatène avec l'élément suivant puis je refais un explode("'", et ainsi de suite.

Deux questions cependant :
1) Y a-t-il un moyen plus simple de compter les ' dans un élément (chaîne) ?
2) Le problème ne peut-il pas être résolu par un "$tableau = preg_split($expression, $requetes)" ? C'eût été élégant :wink:

A+
Jacky


{edit} question 1 : TROUVÉ il y a une fonction PHP qui fait ça : int substr_count ( string $haystack , string $needle [, int $offset = 0 [, int $length ]] ) {/edit}

[résolu] "explode" sur caractère sauf si entre apostrophes

Posté : 01 juil. 2012, 13:48
par JABch
C'est résolu, mais ce n'était pas aussi simple ...
Voici ma routine pour exécuter un fichier de commandes SQL : (on assume que la base de données est déjà ouverte)

Code : Tout sélectionner

<?php function SQLExecuteFile($filename, &$errmsg){ // On récupère le contenu du fichier $lignes = file($filename); if(!$lignes) { $errmsg = "Je ne trouve pas le fichier '$filename' !"; return false; } // On place chaque ligne qui ne commence pas par '--' dans $script en séparant par un espace $script = ''; foreach($lignes as $ligne) { $ligne = trim($ligne); if(substr($ligne,0,2)!='--')) $script.=" ".$ligne; } unset($lignes); if(!$script) { $errmsg = "Aucune instruction dans le fichier '$filename'."; return false; } // on crée un tableau des requêtes en coupant au point-virgule $requetes=explode(";",$script); unset($script); // Boucle sur chaque soi-disant requête $i=0; while($i<count($requetes)) { $distance=0; $requetes[$i]=trim($requetes[$i]); // On ne traite que les lignes non-vides if($requetes[$i]) { // Pour déterminer si la requête est complète, on compte le nombre (merci Mazarini :)) // d'apostrophes diminué du nombre de séquences \' (apostrophes "échappés") $nb=substr_count($requetes[$i],"'")-substr_count($requetes[$i],@"\'"); // Si ce n'est pas un nombre pair, cela signifie que la requête a été tronquée // à l'emplacement d'un point-vurgule interne à une chaîne ('...;...') while($nb%2) { // Alors on prend la suite sur la ligne suivante (il peut y en avoir // plusieurs d'où le compteur $distance $distance++; $requetes[$i].=';'.trim($requetes[$i+$distance]); // On vide la ligne que l'on vient de coller à la précédente $requetes[$i+$distance]=''; // On recalcule pour voir s'il faut poursuivre ... $nb=substr_count($requetes[$i],"'")-substr_count($requetes[$i],@"\'"); } // On tient notre requête complète, on lui ajoute un point-virgule $requetes[$i].=';'; // On l'exécute if(!mysql_query($requetes[$i])) // (on assume que la connexion est pré-existante) { $errmsg = 'Erreur : "'.utf8_encode($requetes[$i]).'" !<br/><br/>'.mysql_errno().' '.mysql_error(); return false; } } // On passe à la requête suivante $i+=1+$distance; } return true; }?>
et voilà.

Je reste intéressé par une solution en preg pur :wink:

A+
Jacky :mrgreen:

Re: "explode" sur caractère sauf si entre apostrophes

Posté : 04 juil. 2012, 14:45
par Thaandor
N'est-il pas possible d'utiliser directement l'instruction MySQL: LOAD DATA INFILE ?
http://dev.mysql.com/doc/refman/5.1/en/load-data.html

Re: "explode" sur caractère sauf si entre apostrophes

Posté : 04 juil. 2012, 18:45
par JABch
N'est-il pas possible d'utiliser directement l'instruction MySQL: LOAD DATA INFILE ?
http://dev.mysql.com/doc/refman/5.1/en/load-data.html
Bonjour,

Non car LOAD DATA INFILE 'file_name' INTO TABLE tbl_name ne fait que charger une table.
Or mon script SQL fait beaucoup plus que ça ! Il exécute plusieurs fonctions.

A+
Jacky

Re: "explode" sur caractère sauf si entre apostrophes

Posté : 04 juil. 2012, 22:17
par Berzemus
Les commandes SQL, y'en a une par fichier ? (en clair, y'a un retour à la ligne après chaque point-virgule final ?)

Parce qu'alors, un simple preg_split() avec une expression régulière du genre "#;$#" suffirait, non ? (avec un flag pour éviter les morceaux vides, pour être sur).

Une autre manière aurait été de traverser le texte caractère par caractère, et de décider alors en fonction des contextes si un point-virgule termine une commande ou pas. Ce serait plus rapide (si le texte tient dans la mémoire vive), probablement plus que l'expression régulière.

Re: "explode" sur caractère sauf si entre apostrophes

Posté : 05 juil. 2012, 04:26
par JABch
Les commandes SQL, y'en a une par fichier ? (en clair, y'a un retour à la ligne après chaque point-virgule final ?)
Non, il peut y en avoir de une à une dizaine et chacune peut se poursuivre sur plusieurs lignes de texte.
Parce qu'alors, un simple preg_split() avec une expression régulière du genre "#;$#" suffirait, non ?
Non car il ne faut pas traiter les ";" à l'intérieur de de champ de texte (entre apostrophes).
Une autre manière aurait été de traverser le texte caractère par caractère ...
Oui, ça je sais faire, mais c'est un peu "agricole". Je cherchais une manière élégante tout en apprenant un peu
à manipuler "preg". C'est raté.

A+
Jacky

Re: "explode" sur caractère sauf si entre apostrophes

Posté : 05 juil. 2012, 08:44
par sirakawa
Bonjour,

Je cherche à sortir un tableau de requetes SQL à partir du contenu d'un fichier où les requêtes sont séparées par ";" au moyen de "$tableau = explode(";", $requetes);".
Cela fonctionne bien, sauf si l'intérieur d'une requete contient ";" entre apostrophes comme par exemple
INSERT INTO ´table´ VALUES (12345, 'abc', 'ici il y a un ; qui pose problème', 'xyz');
car dans cas explode renvoie un élément "INSERT INTO ´table´ VALUES (12345, 'abc', 'ici il y a un " ! :roll:

Je suppose que je devrais utiliser "$tableau = preg_split($expression, $requetes)" mais je ne parviens pas à trouver l'$expression qui va bien. :oops:
Pouvez-vous m'aider svp ?

A+
Jacky 8-|
Question bête: le ; final des requêtes suit-il toujours immédiatement la parenthèse fermante finale?
Une autre idée serait de remplacer dans la BD ); per );; et d'exploser par );;, par exemple.

Re: "explode" sur caractère sauf si entre apostrophes

Posté : 05 juil. 2012, 12:00
par Berzemus
Parce qu'alors, un simple preg_split() avec une expression régulière du genre "#;$#" suffirait, non ?
Non car il ne faut pas traiter les ";" à l'intérieur de de champ de texte (entre apostrophes).
Si chaque point-virgule final était suivi d'un retour à la ligne, cela aurait fonctionné à merveille car l'expression régulière précisée ne s'applique qu'aux point-virgules en fin de ligne (cf. le signe $). Mais comme c'est pas le cas, ce n'est pas utile.
Une autre manière aurait été de traverser le texte caractère par caractère ...
Oui, ça je sais faire, mais c'est un peu "agricole". Je cherchais une manière élégante tout en apprenant un peu
à manipuler "preg". C'est raté.

A+
Jacky
Le fait est qu'il faut une vraie structure pour utiliser efficacement les expressions régulières. Et comme ici, si je peux me permettre, c'est un peu le bordel avec des point-virgules partout à l'intérieur des lignes et non-échappées dans les requêtes, c'est un peu plus complexe. Il serait intéressant de savoir ce qui génère ton script SQL, surtout si une importation directe par mysql (genre mysql banque_de_donnees < fichier_texte) ne fonctionne pas.

Re: "explode" sur caractère sauf si entre apostrophes

Posté : 05 juil. 2012, 14:59
par sirakawa
[quote Le moniteur belge]Le fait est qu'il faut une vraie structure pour utiliser efficacement les expressions régulières. Et comme ici, si je peux me permettre, c'est un peu le bordel avec des point-virgules partout à l'intérieur des lignes et non-échappées dans les requêtes, c'est un peu plus complexe. Il serait intéressant de savoir ce qui génère ton script SQL, surtout si une importation directe par mysql (genre mysql banque_de_donnees < fichier_texte) ne fonctionne pas.[/quote]
C'est pourquoi je suggèrais de modifier la fin de requête en y plaçant un signe ou un ensemble de signes unique (conseil du maître Lohro : choisir une suite qui ne risque pas de se présenter dans le corps du texte, moi je mets volontiers !#! en ajoutant un # à chaque élément imbriqué d'un tableau : !#!ligne!#!ligne !#!!##!cellule!##!cellule!#! .).
Il vaut sûrement mieux modifier les données que chercher l'exploit sportif pour traiter des données mal organisées: un preg__ quand un simple explode devrait suffire.

Re: "explode" sur caractère sauf si entre apostrophes

Posté : 05 juil. 2012, 16:29
par Mazarini
De toutes facon, les commandes contiennes du texte libre d'après ce que je comprend. Il est impossible de préjuger du contenu de ce texte libre et seul l'analyse dans les quotes ou hors de quotes me semble possible pour exclure la partie texte libre.

Re: "explode" sur caractère sauf si entre apostrophes

Posté : 05 juil. 2012, 18:40
par Thaandor
Je pense avoir la solution avec preg_match_all :) !
preg_match_all("#.*?;$#ms", $fileContent, $sql);
A Tester ici : http://lumadis.be/regex/test_regex.php?id=1227

Excellent tuto sur les regex PCRE : http://regex.lumadis.be/tuto_pcre.php

Re: "explode" sur caractère sauf si entre apostrophes

Posté : 05 juil. 2012, 21:37
par Berzemus
C'est pourquoi je suggèrais de modifier la fin de requête en y plaçant un signe ou un ensemble de signes unique
Quelque chose de très amusant à faire dans ce contexte est d'utiliser un caractère qu'on n'utilise plus ou totalement obsolète mais néanmoins supporté, genre le 7ème caractère ascii (qui est un caractère de contrôle qui sonne une cloche ):
http://fr.wikipedia.org/wiki/Caract%C3%A8re_d%27appel

Re: "explode" sur caractère sauf si entre apostrophes

Posté : 06 juil. 2012, 06:31
par JABch
[...]c'est un peu le bordel avec des point-virgules partout à l'intérieur des lignes et non-échappées dans les requêtes, c'est un peu plus complexe.
Hé oui, c'est ça la vie :D
Je pense avoir la solution avec preg_match_all :) !
preg_match_all("#.*?;$#ms", $fileContent, $sql);
A Tester ici : http://lumadis.be/regex/test_regex.php?id=1227

Excellent tuto sur les regex PCRE : http://regex.lumadis.be/tuto_pcre.php
Merci ! Je vais essayer ça dès que possible !
Merci aussi pour les liens :D

A+
Jacky :mrgreen: