read leer example con archivo javascript regex split

leer - ¿Cómo puedo analizar una cadena CSV con Javascript, que contiene coma en los datos?



leer csv javascript (13)

Tengo el siguiente tipo de cadena

var string = "''string, duppi, du'', 23, lala"

Quiero dividir la cadena en una matriz en cada coma, pero solo las comas fuera de las comillas simples.

No puedo descifrar la expresión regular correcta para la división ...

string.split(/,/)

me dará

["''string", " duppi", " du''", " 23", " lala"]

pero el resultado debería ser:

["string, duppi, du", "23", "lala"]

¿Hay alguna solución de navegador cruzado?


La solución RFC 4180

Esto no resuelve la cadena en la pregunta ya que su formato no se ajusta a RFC 4180; la codificación aceptable es la comilla doble de escape con comillas dobles. La siguiente solución funciona correctamente con los archivos CSV d / l de las hojas de cálculo de Google.

ACTUALIZACIÓN (3/2017)

Analizar una sola línea sería incorrecto. Según RFC 4180, los campos pueden contener CRLF, lo que hará que cualquier lector de línea rompa el archivo CSV. Aquí hay una versión actualizada que analiza cadenas CSV:

''use strict''; function csvToArray(text) { let p = '''', row = [''''], ret = [row], i = 0, r = 0, s = !0, l; for (l of text) { if (''"'' === l) { if (s && l === p) row[i] += l; s = !s; } else if ('','' === l && s) l = row[++i] = ''''; else if (''/n'' === l && s) { if (''/r'' === p) row[i] = row[i].slice(0, -1); row = ret[++r] = [l = '''']; i = 0; } else row[i] += l; p = l; } return ret; }; let test = ''"one","two with escaped """" double quotes""","three, with, commas",four with no quotes,"five with CRLF/r/n"/r/n"2nd line one","two with escaped """" double quotes""","three, with, commas",four with no quotes,"five with CRLF/r/n"''; console.log(csvToArray(test));

ANTIGUA RESPUESTA

(Solución de línea única)

function CSVtoArray(text) { let ret = [''''], i = 0, p = '''', s = true; for (let l in text) { l = text[l]; if (''"'' === l) { s = !s; if (''"'' === p) { ret[i] += ''"''; l = ''-''; } else if ('''' === p) l = ''-''; } else if (s && '','' === l) l = ret[++i] = ''''; else ret[i] += l; p = l; } return ret; } let test = ''"one","two with escaped """" double quotes""","three, with, commas",four with no quotes,five for fun''; console.log(CSVtoArray(test));

Y por diversión, así es cómo se crea CSV a partir de la matriz:

function arrayToCSV(row) { for (let i in row) { row[i] = row[i].replace(/"/g, ''""''); } return ''"'' + row.join(''","'') + ''"''; } let row = [ "one", "two with escaped /" double quote", "three, with, commas", "four with no quotes (now has)", "five for fun" ]; let text = arrayToCSV(row); console.log(text);


Renuncia

Actualización de 2014-12-01: la respuesta a continuación funciona solo para un formato muy específico de CSV. Como correctamente señaló DG en los comentarios, esta solución NO se ajusta a la definición de CSV RFC 4180 y tampoco se ajusta al formato MS 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 escapadas.

Una solución CSV no estándar

Como austincheney señala correctamente, realmente necesita analizar la cadena de principio a fin si desea manejar adecuadamente las cadenas entrecomilladas que pueden contener caracteres escapados. Además, el OP no define claramente qué es realmente una "cadena de CSV". Primero debemos definir qué constituye una cadena de CSV válida y sus valores individuales.

Dado: Definición de "Cadena CSV"

A los efectos de esta discusión, una "cadena CSV" consiste en cero o más valores, donde los valores múltiples están separados por una coma. Cada valor puede consistir en:

  1. Una cadena citada doble. (puede contener comillas simples sin guardar)
  2. Una cadena de una sola cita. (puede contener comillas dobles sin guardar)
  3. Una cadena no citada. (NO puede contener comillas, comas o barras diagonales inversas).
  4. 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 escapes: cualquier cosa, por ejemplo, ''that/'s cool'' .
  • Los valores que contienen comillas, comas o barras diagonales inversas deben citarse.
  • Los valores que contienen espacios en blanco iniciales o finales deben citarse.
  • La barra invertida se elimina de todos: /' en valores de una sola cotización.
  • La barra diagonal inversa se elimina de todos: /" en valores de doble cotización.
  • Las cadenas no citadas se recortan de los espacios iniciales y finales.
  • El separador de coma puede tener espacios en blanco adyacentes (que se ignora).

Encontrar:

Una función de JavaScript que convierte una cadena de CSV válida (como se definió anteriormente) en una matriz de valores de cadena.

Solución:

Las expresiones regulares usadas por 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ías. 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 (expresada usando la útil sintaxis de Python: r''''''...'''''' raw-multi-line-string).

Primero aquí hay una expresión regular que valida que una cadena CVS cumple con los requisitos anteriores:

Regex 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, esa cadena es una cadena CSV válida (de acuerdo con las reglas establecidas anteriormente) y puede analizarse utilizando la siguiente expresión regular. La siguiente expresión regular se usa para hacer coincidir un valor de la cadena CSV. Se aplica repetidamente hasta que no se encuentren más coincidencias (y se hayan analizado todos los valores).

Regex para analizar un valor de una cadena de 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 que esta expresión regular no coincide, el último valor cuando ese valor está vacío. Este caso especial de "último valor vacío" se prueba y maneja mediante la función js que sigue.

Función de JavaScript para analizar 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, las llaves se usan para delimitar las {result strings} . (Esto es para ayudar a visualizar los espacios iniciales / finales y las cadenas de longitud cero).

// Test 1: Test string from original question. var test = "''string, duppi, du'', 23, lala"; var a = CSVtoArray(test); /* Array hes 3 elements: a[0] = {string, duppi, du} a[1] = {23} a[2] = {lala} */

// Test 2: Empty CSV string. var test = ""; var a = CSVtoArray(test); /* Array hes 0 elements: */

// Test 3: CSV string with two empty values. var test = ","; var a = CSVtoArray(test); /* Array hes 2 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 hes 3 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 hes 3 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 hes 8 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 no incluidos pueden contener barras invertidas o comillas, por ejemplo, la siguiente cadena de CSV NO es válida:

var invalid1 = "one, that''s me!, escaped /, comma"

Esto no es realmente una limitación porque cualquier subcadena se puede representar como un valor cotizado simple o doble. Tenga en cuenta también que esta solución representa solo una posible definición para: "Valores separados por comas".

Editar: 2014-05-19: se agregó una exención de responsabilidad. Editar: 2014-12-01: Descargo de responsabilidad movido a la parte superior.


Además de la excelente y completa respuesta de ridgerunner, pensé en una solución muy simple para cuando tu back-end ejecute php.

Agregue este archivo php al back-end de su dominio (digamos: csv.php )

<?php session_start(); //optional header("content-type: text/xml"); header("charset=UTF-8"); //set the delimiter and the End of Line character of your csv content: echo json_encode(array_map(''str_getcsv'',str_getcsv($_POST["csv"],"/n"))); ?>

Ahora agregue esta función a su kit de herramientas de JavaScript (debería ser revisado un poco para hacer crossbrowser, creo).

function csvToArray(csv) { var oXhr = new XMLHttpRequest; oXhr.addEventListener("readystatechange", function () { if (this.readyState == 4 && this.status == 200) { console.log(this.responseText); console.log(JSON.parse(this.responseText)); } } ); oXhr.open("POST","path/to/csv.php",true); oXhr.setRequestHeader("Content-type","application/x-www-form-urlencoded; charset=utf-8"); oXhr.send("csv=" + encodeURIComponent(csv)); }

Le costará 1 llamada ajax, pero al menos no duplicará el código ni incluirá ninguna biblioteca externa.

Ref: http://php.net/manual/en/function.str-getcsv.php


De acuerdo con esta publicación del blog , esta función debería hacerlo:

String.prototype.splitCSV = function(sep) { for (var foo = this.split(sep = sep || ","), x = foo.length - 1, tl; x >= 0; x--) { if (foo[x].replace(/''/s+$/, "''").charAt(foo[x].length - 1) == "''") { if ((tl = foo[x].replace(/^/s+''/, "''")).length > 1 && tl.charAt(0) == "''") { foo[x] = foo[x].replace(/^/s*''|''/s*$/g, '''').replace(/''''/g, "''"); } else if (x) { foo.splice(x - 1, 2, [foo[x - 1], foo[x]].join(sep)); } else foo = foo.shift().split(sep).concat(foo); } else foo[x].replace(/''''/g, "''"); } return foo; };

Lo llamarías así:

var string = "''string, duppi, du'', 23, lala"; var parsed = string.splitCSV(); alert(parsed.join("|"));

Este tipo de jsfiddle funciona, pero parece que algunos de los elementos tienen espacios antes que ellos.


Gramática PEG (.js) que maneja ejemplos de RFC 4180 en http://en.wikipedia.org/wiki/Comma-separated_values :

start = [/n/r]* first:line rest:([/n/r]+ data:line { return data; })* [/n/r]* { rest.unshift(first); return rest; } line = first:field rest:("," text:field { return text; })* & { return !!first || rest.length; } // ignore blank lines { rest.unshift(first); return rest; } field = ''"'' text:char* ''"'' { return text.join(''''); } / text:[^/n/r,]* { return text.join(''''); } char = ''"'' ''"'' { return ''"''; } / [^"]

Prueba en http://jsfiddle.net/knvzk/10 o https://pegjs.org/online .

Descargue el analizador generado en https://gist.github.com/3362830 .


La gente parecía estar en contra de RegEx por esto. ¿Por qué?

(/s*''[^'']+''|/s*[^,]+)(?=,|$)

Aquí está el código. También hice un fiddle .

String.prototype.splitCSV = function(sep) { var regex = /(/s*''[^'']+''|/s*[^,]+)(?=,|$)/g; return matches = this.match(regex); } var string = "''string, duppi, du'', 23, ''string, duppi, du'', lala"; var parsed = string.splitCSV(); alert(parsed.join(''|''));


Me gustó la respuesta de FakeRainBrigand, sin embargo, contiene algunos problemas: no puede manejar espacios en blanco entre una comilla y una comilla, y no admite dos comas consecutivas. Traté de editar su respuesta, pero mi revisión fue rechazada por los revisores que aparentemente no entendieron mi código. Aquí está mi versión del código de FakeRainBrigand. También hay un violín: http://jsfiddle.net/xTezm/46/

String.prototype.splitCSV = function() { var matches = this.match(/(/s*"[^"]+"/s*|/s*[^,]+|,)(?=,|$)/g); for (var n = 0; n < matches.length; ++n) { matches[n] = matches[n].trim(); if (matches[n] == '','') matches[n] = ''''; } if (this[0] == '','') matches.unshift(""); return matches; } var string = '',"string, duppi, du" , 23 ,,, "string, duppi, du",dup,"", , lala''; var parsed = string.splitCSV(); alert(parsed.join(''|''));


Mi respuesta presupone que su entrada es un reflejo del código / contenido de las fuentes web donde los caracteres de comillas simple y doble son totalmente intercambiables siempre que ocurran como un conjunto de coincidencia no escapado.

No puedes usar regex para esto. De hecho, tienes que escribir un micro-analizador para analizar la cadena que deseas dividir. Por el bien de esta respuesta, llamaré a las partes citadas de sus cadenas como subcadenas. Necesitas caminar específicamente a través de la cuerda. Considere el siguiente caso:

var a = "some sample string with /"double quotes/" and ''single quotes'' and some craziness like this: ///" or //'", b = "sample of code from JavaScript with a regex containing a comma //,/ that should probably be ignored.";

En este caso, no tiene absolutamente ninguna idea de dónde comienza o finaliza una subcadena simplemente analizando la entrada de un patrón de caracteres. En su lugar, debe escribir lógica para tomar decisiones sobre si un carácter de cita se utiliza como un carácter de cita, si no se lo cita, y si el carácter de cita no sigue un escape.

No voy a escribir ese nivel de complejidad de código para usted, pero puede ver algo que recientemente escribí que tiene el patrón que necesita. Este código no tiene nada que ver con las comas, pero de lo contrario es un micro-analizador suficientemente válido para que usted pueda seguir escribiendo su propio código. Mire en la función asifix de la siguiente aplicación:

https://github.com/austincheney/Pretty-Diff/blob/master/fulljsmin.js


Mientras lee csv en una cadena, contiene un valor nulo entre cadena, así que pruébelo / 0 Línea por línea me funciona.

stringLine = stringLine.replace( //0/g, "" );


Para complementar esta respuesta

Si necesita analizar citas escapadas con otra cita, ejemplo:

"some ""value"" that is on xlsx file",123

Puedes usar

function parse(text) { const csvExp = /(?!/s*$)/s*(?:''([^''//]*(?://[/S/s][^''//]*)*)''|"([^"//]*(?://[/S/s][^"//]*)*)"|"([^""]*(?:"[/S/s][^""]*)*)"|([^,''"/s//]*(?:/s+[^,''"/s//]+)*))/s*(?:,|$)/g; const values = []; text.replace(csvExp, (m0, m1, m2, m3, m4) => { if (m1 !== undefined) { values.push(m1.replace(///'/g, "''")); } else if (m2 !== undefined) { values.push(m2.replace(///"/g, ''"'')); } else if (m3 !== undefined) { values.push(m3.replace(/""/g, ''"'')); } else if (m4 !== undefined) { values.push(m4); } return ''''; }); if (/,/s*$/.test(text)) { values.push(''''); } return values; }


Si puede hacer que su delimitador de cotización sea comillas dobles, entonces este es un duplicado de código JavaScript para analizar datos CSV .

Puede traducir todas las comillas simples a las comillas dobles primero:

string = string.replace( /''/g, ''"'' );

... o puede editar la expresión regular en esa pregunta para reconocer comillas simples en lugar de comillas dobles:

// Quoted fields. "(?:''([^'']*(?:''''[^'']*)*)''|" +

Sin embargo, esto supone cierto margen de beneficio que no queda claro a partir de su pregunta. Por favor aclare cuáles son las diversas posibilidades de marcado, según mi comentario sobre su pregunta.


También me he enfrentado al mismo tipo de problema cuando tengo que analizar un archivo CSV. El archivo contiene una dirección de columna que contiene el '',''.
Después de analizar ese CSV en JSON, obtengo un mapeo no coincidente de las claves al convertirlo en JSON File.
csvtojson nodo para analizar el archivo y la biblioteca como baby parse y csvtojson
Ejemplo de archivo -

address,pincode foo,baar , 123456

Mientras estaba analizando directamente sin utilizar el análisis del bebé en JSON, estaba recibiendo

[{ address: ''foo'', pincode: ''baar'', ''field3'': ''123456'' }]

Así que escribí un código que elimina la coma (,) con cualquier otro deliminador con cada campo

/* csvString(input) = "address, pincode//nfoo, bar, 123456//n" output = "address, pincode//nfoo {YOUR DELIMITER} bar, 123455//n" */ const removeComma = function(csvString){ let delimiter = ''|'' let Baby = require(''babyparse'') let arrRow = Baby.parse(csvString).data; /* arrRow = [ [ ''address'', ''pincode'' ], [ ''foo, bar'', ''123456''] ] */ return arrRow.map((singleRow, index) => { //the data will include /* singleRow = [ ''address'', ''pincode'' ] */ return singleRow.map(singleField => { //for removing the comma in the feild return singleField.split('','').join(delimiter) }) }).reduce((acc, value, key) => { acc = acc +(Array.isArray(value) ? value.reduce((acc1, val)=> { acc1 = acc1+ val + '','' return acc1 }, '''') : '''') + ''/n''; return acc; },'''') }

La función devuelta se puede pasar a la biblioteca csvtojson y, por lo tanto, se puede utilizar el resultado.

const csv = require(''csvtojson'') let csvString = "address, pincode//nfoo, bar, 123456//n" let jsonArray = [] modifiedCsvString = removeComma(csvString) csv() .fromString(modifiedCsvString) .on(''json'', json => jsonArray.push(json)) .on(''end'', () => { /* do any thing with the json Array */ })

Ahora puedes obtener el resultado como

[{ address: ''foo, bar'', pincode: 123456 }]


Tuve un caso de uso muy específico en el que quería copiar las celdas de Hojas de cálculo de Google en mi aplicación web. Las celdas podrían incluir comillas dobles y caracteres de nueva línea. Usando copiar y pegar, las celdas están delimitadas por una pestaña de caracteres, y las celdas con datos impares se cotizan en doble. Intenté esta solución principal, el artículo vinculado usando regexp, y Jquery-CSV, y CSVToArray. http://papaparse.com/ es el único que funcionó de la caja. Copiar y pegar es perfecto con Hojas de cálculo de Google con opciones predeterminadas de detección automática.