L'analyseur CSV utilisé dans le plug-in jquery-csv
C'est un analyseur grammatical de base de Chomsky Type III .
Un tokenizer regex est utilisé pour évaluer les données caractère par caractère. Lorsqu'un caractère de contrôle est rencontré, le code est transmis à une instruction switch pour une évaluation plus approfondie basée sur l'état de départ. Les caractères non contrôlés sont regroupés et copiés en masse pour réduire le nombre d'opérations de copie de chaîne nécessaires.
Le tokenizer:
var tokenizer = /("|,|\n|\r|[^",\r\n]+)/;
Le premier jeu de correspondances est constitué des caractères de contrôle: séparateur de valeurs (") séparateur de valeurs (,) et séparateur d'entrée (toutes les variantes de la nouvelle ligne). La dernière correspondance gère le regroupement de caractères non contrôlés.
L'analyseur doit respecter 10 règles:
- Règle n ° 1 - Une entrée par ligne, chaque ligne se termine par une nouvelle ligne
- Règle n ° 2 - saut de ligne à la fin du fichier omis
- Règle n ° 3 - La première ligne contient des données d'en-tête
- Règle n ° 4 - Les espaces sont considérés comme des données et les entrées ne doivent pas contenir de virgule de fin
- Règle n ° 5 - Les lignes peuvent ou non être délimitées par des guillemets doubles
- Règle n ° 6 - Les champs contenant des sauts de ligne, des guillemets doubles et des virgules doivent être placés entre guillemets doubles
- Règle n ° 7 - Si des guillemets doubles sont utilisés pour entourer des champs, un guillemet double apparaissant à l'intérieur d'un champ doit être échappé en le précédant d'un autre guillemet double
- Amendement n ° 1 - Un champ non cité peut ou peut
- Amendement n ° 2 - Un champ cité peut ou non
- Amendement n ° 3 - Le dernier champ d'une entrée peut ou non contenir une valeur nulle
Remarque: Les 7 principales règles sont dérivées directement de l' IETF RFC 4180 . Les 3 derniers ont été ajoutés pour couvrir les cas marginaux introduits par les applications de feuille de calcul modernes (ex Excel, Google Spreadsheet) qui ne délimitent pas (c.-à-d. Citent) toutes les valeurs par défaut. J'ai essayé de contribuer aux modifications du RFC mais je n'ai toujours pas entendu de réponse à ma demande.
Assez avec la liquidation, voici le diagramme:
États:
- état initial d'une entrée et / ou d'une valeur
- une citation d'ouverture a été rencontrée
- une deuxième citation a été rencontrée
- une valeur non cotée a été rencontrée
Transitions:
- une. vérifie les valeurs entre guillemets (1), les valeurs sans guillemets (3), les valeurs nulles (0), les entrées nulles (0) et les nouvelles entrées (0)
- b. vérifie un deuxième caractère de devis (2)
- c. vérifie la citation échappée (1), la fin de la valeur (0) et la fin de l'entrée (0)
- ré. vérifie la fin de la valeur (0) et la fin de l'entrée (0)
Remarque: il manque en fait un état. Il devrait y avoir une ligne de 'c' -> 'b' marquée avec l'état '1' car un deuxième délimiteur échappé signifie que le premier délimiteur est toujours ouvert. En fait, il serait probablement préférable de le représenter comme une autre transition. La création de ceux-ci est un art, il n'y a pas une seule façon correcte.
Remarque: il manque également un état de sortie mais sur des données valides, l'analyseur se termine toujours lors de la transition 'a' et aucun des états n'est possible car il ne reste plus rien à analyser.
La différence entre les États et les transitions:
Un état est fini, ce qui signifie qu'il ne peut être déduit que pour signifier une chose.
Une transition représente le flux entre les états et peut donc signifier beaucoup de choses.
Fondamentalement, la relation état-> transition est 1 -> * (c'est-à-dire un à plusieurs). L'État définit «ce que c'est» et la transition définit «comment c'est géré».
Remarque: Ne vous inquiétez pas si l'application d'états / transitions ne semble pas intuitive, elle n'est pas intuitive. Il a fallu des correspondances approfondies avec quelqu'un de bien plus intelligent que moi avant de finalement avoir le concept.
Le pseudo-code:
csv = // csv input string
// init all state & data
state = 0
value = ""
entry = []
output = []
endOfValue() {
entry.push(value)
value = ""
}
endOfEntry() {
endOfValue()
output.push(entry)
entry = []
}
tokenizer = /("|,|\n|\r|[^",\r\n]+)/gm
// using the match extension of string.replace. string.exec can also be used in a similar manner
csv.replace(tokenizer, function (match) {
switch(state) {
case 0:
if(opening delimiter)
state = 1
break
if(new-line)
endOfEntry()
state = 0
break
if(un-delimited data)
value += match
state = 3
break
case 1:
if(second delimiter encountered)
state = 2
break
if(non-control char data)
value += match
state = 1
break
case 2:
if(escaped delimiter)
state = 1
break
if(separator)
endOfValue()
state = 0
break
if(newline)
endOfEntry()
state = 0
break
case 3:
if(separator)
endOfValue()
state = 0
break
if(newline)
endOfEntry()
state = 0
break
}
}
Remarque: c'est l'essentiel, dans la pratique, il y a beaucoup plus à considérer. Par exemple, la vérification des erreurs, les valeurs nulles, une ligne vide de fin (c'est-à-dire qui est valide), etc.
Dans ce cas, l'état est l'état des choses lorsque le bloc de correspondance d'expression régulière termine une itération. La transition est représentée par les instructions case.
En tant qu'êtres humains, nous avons tendance à simplifier les opérations de bas niveau en des résumés de niveau supérieur, mais travailler avec un FSM, c'est travailler avec des opérations de bas niveau. Bien que les états et les transitions soient très faciles à travailler individuellement, il est intrinsèquement difficile de visualiser le tout d'un coup. J'ai trouvé plus facile de suivre les chemins individuels d'exécution encore et encore jusqu'à ce que je puisse comprendre comment les transitions se déroulent. C'est comme apprendre des mathématiques de base, vous ne pourrez pas évaluer le code à partir d'un niveau supérieur jusqu'à ce que les détails de bas niveau deviennent automatiques.
À part: Si vous regardez la mise en œuvre réelle, il manque beaucoup de détails. Tout d'abord, tous les chemins impossibles lèveront des exceptions spécifiques. Il devrait être impossible de les toucher, mais si quelque chose se casse, ils déclencheront absolument des exceptions dans le lanceur de test. Deuxièmement, les règles de l'analyseur pour ce qui est autorisé dans une chaîne de données CSV «légale» sont assez lâches, donc le code nécessaire pour gérer un grand nombre de cas de bord spécifiques. Indépendamment de ce fait, c'était le processus utilisé pour se moquer du FSM avant toutes les corrections de bogues, les extensions et le réglage fin.
Comme avec la plupart des conceptions, ce n'est pas une représentation exacte de la mise en œuvre, mais elle décrit les parties importantes. Dans la pratique, il existe en fait 3 fonctions d'analyseur différentes dérivées de cette conception: un séparateur de ligne spécifique au csv, un analyseur à ligne unique et un analyseur à lignes multiples complet. Ils fonctionnent tous de manière similaire, ils diffèrent dans la façon dont ils traitent les caractères de nouvelle ligne.