Un collègue et moi avons récemment discuté de la question de savoir si une expression rationnelle pure est capable d'encapsuler entièrement le format csv, de telle sorte qu'elle soit capable d'analyser tous les fichiers avec n'importe quel caractère d'échappement, de guillemet et de séparateur.
La regex n'a pas besoin d'être capable de changer ces caractères après la création, mais elle ne doit échouer sur aucun autre cas de bord.
J'ai soutenu que cela est impossible pour un simple tokenizer. Le seul regex qui pourrait être en mesure de le faire est un style PCRE très complexe qui va au-delà de la simple symbolisation.
Je cherche quelque chose dans le sens de:
... le format csv est une grammaire sans contexte et en tant que tel, il est impossible d'analyser avec regex seul ...
Ou ai-je tort? Est-il possible d'analyser csv avec juste une expression rationnelle POSIX?
Par exemple, si le caractère d'échappement et le caractère de citation le sont "
, ces deux lignes sont des csv valides:
"""this is a test.""",""
"and he said,""What will be, will be."", to which I replied, ""Surely not!""","moving on to the next field here..."
la source
"
? Ensuite, ce qui suit est valide:"""this is a test.""",""
Réponses:
Agréable en théorie, terrible en pratique
Par CSV, je vais supposer que vous entendez la convention décrite dans la RFC 4180 .
Bien que la correspondance des données CSV de base soit triviale:
Remarque: BTW, il est beaucoup plus efficace d'utiliser une fonction .split ('/ n'). Split ('"') pour des données très simples et bien structurées comme celle-ci. Les expressions régulières fonctionnent comme un NDFSM (Non-Deterministic Finite State Machine) qui gaspille beaucoup de temps à revenir en arrière une fois que vous commencez à ajouter des cas marginaux comme des caractères d'échappement.
Par exemple, voici la chaîne de correspondance d'expressions régulières la plus complète que j'ai trouvée:
Il gère raisonnablement les valeurs entre guillemets simples et doubles, mais pas les retours à la ligne des valeurs, les guillemets échappés, etc.
Source: Stack Overflow - Comment puis-je analyser une chaîne avec JavaScript
Cela devient un cauchemar une fois que les cas de bord communs sont introduits comme ...
Le cas de bord de nouvelle ligne en tant que valeur suffit à lui seul à casser 99,9999% des analyseurs basés sur RegEx trouvés dans la nature. La seule alternative «raisonnable» consiste à utiliser la correspondance RegEx pour la tokenisation de caractère de contrôle / non-contrôle de base (c'est-à-dire terminal vs non-terminal) couplée à une machine d'état utilisée pour une analyse de niveau supérieur.
Source: Expérience autrement connue sous le nom de douleur et de souffrance étendues.
Je suis l'auteur de jquery-CSV , le seul analyseur CSV basé sur javascript et entièrement compatible RFC au monde. J'ai passé des mois à résoudre ce problème, à parler à de nombreuses personnes intelligentes et à essayer une tonne d'implémentations différentes, y compris 3 réécritures complètes du moteur de l'analyseur principal.
tl; dr - Morale de l'histoire, PCRE est nul à lui seul pour analyser tout sauf les grammaires régulières les plus simples et les plus strictes (c'est-à-dire de type III). Bien qu'il soit utile pour la tokenisation des chaînes terminales et non terminales.
la source
Regex peut analyser n'importe quel langage normal et ne peut pas analyser des choses fantaisistes comme des grammaires récursives. Mais CSV semble être assez régulier, donc analysable avec une expression régulière.
Travaillons à partir de la définition : sont autorisés la séquence, les alternatives de forme de choix (
|
) et la répétition (étoile de Kleene, la*
).[^,]*
# tout caractère mais virgule"([^\"]|\\\\|\\")*"
# séquence de tout sauf guillemet"
ou guillemet\"
échappé ou échappement échappé\\
("")*"
à l'expression ci-dessus.|
<valeur-quotée>(,
<valeur>)*
\n
est également évidemment régulière.Je n'ai pas testé méticuleusement chacune de ces expressions et je n'ai jamais défini de groupes de captures. J'ai aussi passé sous silence certains aspects techniques, comme les variantes de caractères qui peuvent être utilisés au lieu de
,
,"
ou séparateurs ligne: ceux - ci ne se cassent pas la régularité, vous obtenez juste plusieurs langues légèrement différentes.Si vous pouvez détecter un problème dans cette épreuve, veuillez commenter! :)
Mais malgré cela, l' analyse pratique des fichiers CSV par des expressions régulières pures peut être problématique. Vous devez savoir laquelle des variantes est envoyée à l'analyseur, et il n'y a pas de norme pour cela. Vous pouvez essayer plusieurs parseurs contre chaque ligne jusqu'à ce que l'un réussisse, ou diviser en quelque sorte les commentaires du formulaire de format. Mais cela peut nécessiter des moyens autres que les expressions régulières pour faire efficacement, ou pas du tout.
la source
[^,"]*|"(\\(\\|")|[^\\"])*"
, et le dernier devrait être quelque chose comme[^,"]*|"(""|[^"])*"
. (Attention, je n'ai testé aucun de ces deux!)perl -pi -e 's/"([^\"]|\\\\|\\")*"/yay/'
et que je redirige ,"I have here an item,\" that is a test\""
le résultat est `yay c'est un test \" ". Je pense que votre regex est défectueux.Réponse simple - probablement pas.
Le premier problème est le manque de norme. Bien que l'on puisse décrire leur csv d'une manière strictement définie, on ne peut pas s'attendre à obtenir des fichiers csv strictement définis. "Soyez conservateur dans ce que vous faites, soyez libéral dans ce que vous acceptez des autres" -Jon Postal
En supposant que l'on ait un standard standard acceptable, il y a la question des caractères d'échappement et si ceux-ci doivent être équilibrés.
Une chaîne dans de nombreux formats csv est définie comme
string value 1,string value 2
. Cependant, si cette chaîne contient une virgule, c'est maintenant"string, value 1",string value 2
. S'il contient une citation, il devient"string, ""value 1""",string value 2
.À ce stade, je pense que c'est impossible. Le problème étant que vous devez déterminer le nombre de guillemets que vous avez lus et si une virgule se trouve à l'intérieur ou à l'extérieur du mode entre guillemets doubles de la valeur. L'équilibrage des parenthèses est un problème d'expression rationnelle impossible. Certains moteurs d'expression régulière étendus (PCRE) peuvent y faire face, mais ce n'est pas une expression régulière alors.
Https://stackoverflow.com/questions/8629763/csv-parsing-with-a-context-free-grammar peut vous être utile.
Modifié:
J'ai cherché des formats pour les caractères d'échappement et je n'en ai trouvé aucun qui nécessite un comptage arbitraire - ce n'est probablement pas le problème.
Cependant, il y a des problèmes de caractère d'échappement et de délimiteur d'enregistrement (pour commencer). http://www.csvreader.com/csv_format.php est une bonne lecture sur les différents formats à l'état sauvage.
'This, is a value'
contre"This, is a value"
"This ""is a value"""
contre"This \"is a value\""
"This {rd}is a value"
vs (échappé)"This \{rd}is a value"
vs (traduit)"This {0x1C}is a value"
L'essentiel ici est qu'il est possible d'avoir une chaîne qui aura toujours plusieurs interprétations valides.
La question connexe (pour les cas marginaux) "est-il possible d'avoir une chaîne invalide acceptée?"
Je doute fortement qu'il existe une expression régulière qui puisse correspondre à chaque CSV valide créé par une application et rejeter chaque csv qui ne peut pas être analysé.
la source
("")*"
. Si les devis à l' intérieur de la valeur sont hors d'équilibre, ce n'est déjà pas notre affaire.Définissez d'abord la grammaire de votre CSV (les délimiteurs de champ sont-ils échappés ou encodés d'une manière ou d'une autre s'ils apparaissent dans le texte?), Puis il peut être déterminé s'il est analysable avec regex. Première grammaire: deuxième analyseur: http://www.boyet.com/articles/csvparser.html Il convient de noter que cette méthode utilise un tokenizer - mais je ne peux pas constuire une expression régulière POSIX qui correspondrait à tous les cas de bord. Si votre utilisation des formats CSV est non régulière et sans contexte ... alors votre réponse est dans votre question. Bon aperçu ici: http://nikic.github.com/2012/06/15/The-true-power-of-regular-expressions.html
la source
Cette expression régulière peut symboliser le CSV normal, comme décrit dans le RFC:
/("(?:[^"]|"")*"|[^,"\n\r]*)(,|\r?\n|\r)/
Explication:
("(?:[^"]|"")*"|[^,"\n\r]*)
- un champ CSV, cité ou non"(?:[^"]|"")*"
- un champ entre guillemets;[^"]|""
- chaque caractère est soit non"
, soit"
échappé comme""
[^,"\n\r]*
- un champ sans guillemets, qui ne peut pas contenir,
"
\n
\r
(,|\r?\n|\r)
- le séparateur suivant, soit,
une nouvelle ligne\r?\n|\r
- une nouvelle ligne, l'une des\r\n
\n
\r
Un fichier CSV entier peut être mis en correspondance et validé à l'aide de cette expression régulière à plusieurs reprises. Il est alors nécessaire de corriger les champs entre guillemets et de les diviser en lignes en fonction des séparateurs.
Voici le code d'un analyseur CSV en Javascript, basé sur l'expression rationnelle:
Si cette réponse aide à régler votre argument, c'est à vous de décider; Je suis juste heureux d'avoir un petit analyseur CSV simple et correct.
À mon avis, un
lex
programme est plus ou moins une grande expression régulière, et ceux-ci peuvent symboliser des formats beaucoup plus complexes, tels que le langage de programmation C.En référence aux définitions de la RFC 4180 :
espaces sont considérés comme faisant partie d'un champ et ne doivent pas être ignorés - d'accord
Le dernier champ de l'enregistrement ne doit pas être suivi d'une virgule - non appliqué
L'expression régulière elle-même satisfait la plupart des exigences RFC 4180. Je ne suis pas d'accord avec les autres, mais il est facile d'ajuster l'analyseur pour les implémenter.
la source