"explode" sur caractère sauf si entre apostrophes

Petit nouveau ! | 7 Messages

30 juin 2012, 17:12

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-|

ViPHP
ViPHP | 2577 Messages

30 juin 2012, 18:00

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 \'.

Petit nouveau ! | 7 Messages

30 juin 2012, 18:16

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}

Petit nouveau ! | 7 Messages

01 juil. 2012, 13:48

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:

Petit nouveau ! | 7 Messages

04 juil. 2012, 14:45

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

Petit nouveau ! | 7 Messages

04 juil. 2012, 18:45

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

ViPHP
ViPHP | 4039 Messages

04 juil. 2012, 22:17

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.
Mais qu'importe. (je suis ici - dernier petit projet)
Berze going social.

Petit nouveau ! | 7 Messages

05 juil. 2012, 04:26

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

Mammouth du PHP | 2278 Messages

05 juil. 2012, 08:44

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.
Vanitas vanitatum et omnia vanitas
Mes derniers livres :
Sauvez les Mots chez BoD,
Tous les chemins mènent à ROM chez BoD

ViPHP
ViPHP | 4039 Messages

05 juil. 2012, 12:00

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.
Mais qu'importe. (je suis ici - dernier petit projet)
Berze going social.

Mammouth du PHP | 2278 Messages

05 juil. 2012, 14:59

[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.
Vanitas vanitatum et omnia vanitas
Mes derniers livres :
Sauvez les Mots chez BoD,
Tous les chemins mènent à ROM chez BoD

ViPHP
ViPHP | 2577 Messages

05 juil. 2012, 16:29

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.

Petit nouveau ! | 7 Messages

05 juil. 2012, 18:40

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

ViPHP
ViPHP | 4039 Messages

05 juil. 2012, 21:37

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
Mais qu'importe. (je suis ici - dernier petit projet)
Berze going social.

Petit nouveau ! | 7 Messages

06 juil. 2012, 06:31

[...]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: