Descargo de responsabilidad
Actualización de 2014-12-01: la respuesta a continuación solo funciona para un formato muy específico de CSV. Como señaló correctamente DG en los comentarios , esta solución no se ajusta a la definición de CSV de RFC 4180 y tampoco se ajusta al formato de Microsoft Excel. Esta solución simplemente demuestra cómo se puede analizar una línea de entrada CSV (no estándar) que contiene una combinación de tipos de cadenas, donde las cadenas pueden contener comillas y comillas de escape.
Una solución CSV no estándar
Como señala correctamente austincheney , realmente necesita analizar la cadena de principio a fin si desea manejar adecuadamente las cadenas entre comillas que pueden contener caracteres de escape. Además, el OP no define claramente qué es realmente una "cadena CSV". Primero debemos definir qué constituye una cadena CSV válida y sus valores individuales.
Dado: Definición de "Cadena CSV"
A los efectos de esta discusión, una "cadena CSV" consta de cero o más valores, donde varios valores están separados por una coma. Cada valor puede constar de:
- Una cadena entre comillas dobles (puede contener comillas simples sin escape).
- Una sola cadena entre comillas (puede contener comillas dobles sin escape).
- Una cadena sin comillas ( no puede contener comillas, comas o barras invertidas).
- Un valor vacío. (Un valor de todos los espacios en blanco se considera vacío).
Reglas / Notas:
- Los valores entre comillas pueden contener comas.
- Los valores entre comillas pueden contener cualquier cosa de escape, por ejemplo
'that\'s cool'
.
- Los valores que contienen comillas, comas o barras invertidas deben estar entrecomillados.
- Los valores que contienen espacios en blanco al principio o al final deben estar entre comillas.
- La barra invertida se elimina de todos:
\'
en valores entre comillas simples.
- La barra invertida se elimina de todos:
\"
en valores entre comillas dobles.
- Las cadenas no entrecomilladas se recortan de los espacios iniciales y finales.
- El separador de coma puede tener espacios en blanco adyacentes (que se ignoran).
Encontrar:
Una función de JavaScript que convierte una cadena CSV válida (como se define arriba) en una matriz de valores de cadena.
Solución:
Las expresiones regulares que utiliza esta solución son complejas. Y (en mi humilde opinión) todas las expresiones regulares no triviales deben presentarse en modo de espacio libre con muchos comentarios y sangría. Desafortunadamente, JavaScript no permite el modo de espacio libre. Por lo tanto, las expresiones regulares implementadas por esta solución se presentan primero en la sintaxis de expresiones regulares nativas (expresadas utilizando la práctica r'''...'''
sintaxis de cadena de múltiples líneas sin procesar de Python ).
Primero, aquí hay una expresión regular que valida que una cadena CVS cumple con los requisitos anteriores:
Expresión regular para validar una "cadena 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 una cadena coincide con la expresión regular anterior, entonces esa cadena es una cadena CSV válida (de acuerdo con las reglas indicadas anteriormente) y se puede analizar utilizando la siguiente expresión regular. La siguiente expresión regular se usa luego para hacer coincidir un valor de la cadena CSV. Se aplica repetidamente hasta que no se encuentran más coincidencias (y se han analizado todos los valores).
Expresión regular para analizar un valor de una cadena CSV válida:
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.
"""
Tenga en cuenta que hay un valor de caso especial con el que esta expresión regular no coincide: el último valor cuando ese valor está vacío. Este caso especial de "último valor vacío" es probado y manejado por la función de JavaScript que sigue.
Función de JavaScript para analizar la cadena 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;
};
Ejemplo de entrada y salida:
En los siguientes ejemplos, se utilizan llaves para delimitar el {result strings}
. (Esto es para ayudar a visualizar espacios iniciales / finales y cadenas de longitud cero).
// 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] = {} */
Notas adicionales:
Esta solución requiere que la cadena CSV sea "válida". Por ejemplo, los valores sin comillas pueden no contener barras invertidas ni comillas, por ejemplo, la siguiente cadena CSV no es válida:
var invalid1 = "one, that's me!, escaped \, comma"
Esto no es realmente una limitación porque cualquier subcadena puede representarse como un valor entre comillas simple o doble. Tenga en cuenta también que esta solución representa sólo una posible definición de "valores separados por comas".
Editar historial
- 2014-05-19: Descargo de responsabilidad agregado.
- 2014-12-01: Se movió el descargo de responsabilidad al principio.