Avertissement
Mise à jour du 01/12/2014: La réponse ci-dessous ne fonctionne que pour un format très spécifique de CSV. Comme l'a correctement souligné DG dans les commentaires , cette solution ne correspond pas à la définition RFC 4180 du CSV et elle ne correspond pas non plus au format Microsoft Excel. Cette solution montre simplement comment analyser une ligne d'entrée CSV (non standard) qui contient un mélange de types de chaînes, où les chaînes peuvent contenir des guillemets et des virgules.
Une solution CSV non standard
Comme le souligne à juste titre Austincheney , vous devez vraiment analyser la chaîne du début à la fin si vous souhaitez gérer correctement les chaînes entre guillemets qui peuvent contenir des caractères échappés. De plus, l'OP ne définit pas clairement ce qu'est réellement une "chaîne CSV". Nous devons d'abord définir ce qui constitue une chaîne CSV valide et ses valeurs individuelles.
Donné: Définition de "chaîne CSV"
Pour les besoins de cette discussion, une "chaîne CSV" se compose de zéro ou plusieurs valeurs, où plusieurs valeurs sont séparées par une virgule. Chaque valeur peut être constituée de:
- Une chaîne entre guillemets (peut contenir des guillemets simples sans échappement).
- Une chaîne entre guillemets simples (peut contenir des guillemets doubles non échappés).
- Une chaîne non entre guillemets ( ne peut pas contenir de guillemets, de virgules ou de barres obliques inverses).
- Une valeur vide. (Une valeur entièrement vide est considérée comme vide.)
Règles / Notes:
- Les valeurs citées peuvent contenir des virgules.
- Les valeurs citées peuvent contenir tout élément échappé, par exemple
'that\'s cool'
.
- Les valeurs contenant des guillemets, des virgules ou des barres obliques inverses doivent être entre guillemets.
- Les valeurs contenant des espaces de début ou de fin doivent être entre guillemets.
- La barre oblique inverse est supprimée de tout:
\'
dans les valeurs entre guillemets simples.
- La barre oblique inverse est supprimée de tout:
\"
dans les valeurs entre guillemets.
- Les chaînes non entre guillemets sont supprimées de tous les espaces de début et de fin.
- Le séparateur par virgule peut avoir un espace blanc adjacent (qui est ignoré).
Trouver:
Une fonction JavaScript qui convertit une chaîne CSV valide (telle que définie ci-dessus) en un tableau de valeurs de chaîne.
Solution:
Les expressions régulières utilisées par cette solution sont complexes. Et (IMHO) toutes les expressions régulières non triviales doivent être présentées en mode espacement libre avec beaucoup de commentaires et d'indentation. Malheureusement, JavaScript n'autorise pas le mode d'espacement libre. Ainsi, les expressions régulières implémentées par cette solution sont d'abord présentées dans la syntaxe des expressions régulières natives (exprimées en utilisant le pratique de Pythonr'''...'''
la syntaxe de chaîne brute multi-lignes ).
Voici d'abord une expression régulière qui valide qu'une chaîne CVS répond aux exigences ci-dessus:
Expression régulière pour valider une "chaîne CSV":
re_valid = r"""
# Validate a CSV string having single, double or un-quoted values.
^ # Anchor to start of string.
\s* # Allow whitespace before value.
(?: # Group for value alternatives.
'[^'\\]*(?:\\[\S\s][^'\\]*)*' # Either Single quoted string,
| "[^"\\]*(?:\\[\S\s][^"\\]*)*" # or Double quoted string,
| [^,'"\s\\]*(?:\s+[^,'"\s\\]+)* # or Non-comma, non-quote stuff.
) # End group of value alternatives.
\s* # Allow whitespace after value.
(?: # Zero or more additional values
, # Values separated by a comma.
\s* # Allow whitespace before value.
(?: # Group for value alternatives.
'[^'\\]*(?:\\[\S\s][^'\\]*)*' # Either Single quoted string,
| "[^"\\]*(?:\\[\S\s][^"\\]*)*" # or Double quoted string,
| [^,'"\s\\]*(?:\s+[^,'"\s\\]+)* # or Non-comma, non-quote stuff.
) # End group of value alternatives.
\s* # Allow whitespace after value.
)* # Zero or more additional values
$ # Anchor to end of string.
"""
Si une chaîne correspond à l'expression régulière ci-dessus, alors cette chaîne est une chaîne CSV valide (selon les règles précédemment énoncées) et peut être analysée à l'aide de l'expression régulière suivante. L'expression régulière suivante est ensuite utilisée pour faire correspondre une valeur de la chaîne CSV. Il est appliqué à plusieurs reprises jusqu'à ce qu'il n'y ait plus de correspondance (et que toutes les valeurs aient été analysées).
Expression régulière pour analyser une valeur à partir d'une chaîne CSV valide:
re_value = r"""
# Match one value in valid CSV string.
(?!\s*$) # Don't match empty last value.
\s* # Strip whitespace before value.
(?: # Group for value alternatives.
'([^'\\]*(?:\\[\S\s][^'\\]*)*)' # Either $1: Single quoted string,
| "([^"\\]*(?:\\[\S\s][^"\\]*)*)" # or $2: Double quoted string,
| ([^,'"\s\\]*(?:\s+[^,'"\s\\]+)*) # or $3: Non-comma, non-quote stuff.
) # End group of value alternatives.
\s* # Strip whitespace after value.
(?:,|$) # Field ends on comma or EOS.
"""
Notez qu'il existe une valeur de cas particulier à laquelle cette expression régulière ne correspond pas - la toute dernière valeur lorsque cette valeur est vide. Cette "dernière valeur vide" spéciale est testé et géré par la fonction JavaScript qui suit.
Fonction JavaScript pour analyser la chaîne CSV:
// Return array of string values, or NULL if CSV string not well formed.
function CSVtoArray(text) {
var re_valid = /^\s*(?:'[^'\\]*(?:\\[\S\s][^'\\]*)*'|"[^"\\]*(?:\\[\S\s][^"\\]*)*"|[^,'"\s\\]*(?:\s+[^,'"\s\\]+)*)\s*(?:,\s*(?:'[^'\\]*(?:\\[\S\s][^'\\]*)*'|"[^"\\]*(?:\\[\S\s][^"\\]*)*"|[^,'"\s\\]*(?:\s+[^,'"\s\\]+)*)\s*)*$/;
var re_value = /(?!\s*$)\s*(?:'([^'\\]*(?:\\[\S\s][^'\\]*)*)'|"([^"\\]*(?:\\[\S\s][^"\\]*)*)"|([^,'"\s\\]*(?:\s+[^,'"\s\\]+)*))\s*(?:,|$)/g;
// Return NULL if input string is not well formed CSV string.
if (!re_valid.test(text)) return null;
var a = []; // Initialize array to receive values.
text.replace(re_value, // "Walk" the string using replace with callback.
function(m0, m1, m2, m3) {
// Remove backslash from \' in single quoted values.
if (m1 !== undefined) a.push(m1.replace(/\\'/g, "'"));
// Remove backslash from \" in double quoted values.
else if (m2 !== undefined) a.push(m2.replace(/\\"/g, '"'));
else if (m3 !== undefined) a.push(m3);
return ''; // Return empty string.
});
// Handle special case of empty last value.
if (/,\s*$/.test(text)) a.push('');
return a;
};
Exemple d'entrée et de sortie:
Dans les exemples suivants, des accolades sont utilisées pour délimiter le {result strings}
. (Ceci permet de visualiser les espaces de début / de fin et les chaînes de longueur nulle.)
// Test 1: Test string from original question.
var test = "'string, duppi, du', 23, lala";
var a = CSVtoArray(test);
/* Array has three elements:
a[0] = {string, duppi, du}
a[1] = {23}
a[2] = {lala} */
// Test 2: Empty CSV string.
var test = "";
var a = CSVtoArray(test);
/* Array has zero elements: */
// Test 3: CSV string with two empty values.
var test = ",";
var a = CSVtoArray(test);
/* Array has two elements:
a[0] = {}
a[1] = {} */
// Test 4: Double quoted CSV string having single quoted values.
var test = "'one','two with escaped \' single quote', 'three, with, commas'";
var a = CSVtoArray(test);
/* Array has three elements:
a[0] = {one}
a[1] = {two with escaped ' single quote}
a[2] = {three, with, commas} */
// Test 5: Single quoted CSV string having double quoted values.
var test = '"one","two with escaped \" double quote", "three, with, commas"';
var a = CSVtoArray(test);
/* Array has three elements:
a[0] = {one}
a[1] = {two with escaped " double quote}
a[2] = {three, with, commas} */
// Test 6: CSV string with whitespace in and around empty and non-empty values.
var test = " one , 'two' , , ' four' ,, 'six ', ' seven ' , ";
var a = CSVtoArray(test);
/* Array has eight elements:
a[0] = {one}
a[1] = {two}
a[2] = {}
a[3] = { four}
a[4] = {}
a[5] = {six }
a[6] = { seven }
a[7] = {} */
Notes complémentaires:
Cette solution nécessite que la chaîne CSV soit "valide". Par exemple, les valeurs sans guillemets peuvent ne pas contenir de barres obliques inverses ou de guillemets, par exemple, la chaîne CSV suivante n'est pas valide:
var invalid1 = "one, that's me!, escaped \, comma"
Ce n'est pas vraiment une limitation car toute sous-chaîne peut être représentée sous la forme d'une valeur entre guillemets simples ou doubles. Notez également que cette solution ne représente qu'une seule définition possible pour les «valeurs séparées par des virgules».
Modifier l'historique
- 2014-05-19: Avertissement ajouté.
- 01/12/2014: mise en garde déplacée vers le haut.