¿Cómo leer datos del archivo * .CSV usando javascript?

196

Mis datos csv se ven así:

encabezado1, encabezado2, encabezado3, encabezado4, encabezado5, valor1_1, valor2_1, valor3_1, valor4_1, valor5_1, valor1_2, valor2_2, valor3_2, valor4_2, valor5_2 ...

¿Cómo lee estos datos y los convierte a una matriz como esta usando Javascript ?:

[título1: valor1_1, título2: valor2_1, título3: valor3_1, título4: valor4_1, título5: valor5_1], [título1: valor1_2, título2: valor2_2, título3: valor3_2, título4: valor4_2, título5: valor5_2] ...

¡Probé este código pero no tuve suerte !:

<script type="text/javascript">
    var allText =[];
    var allTextLines = [];
    var Lines = [];

    var txtFile = new XMLHttpRequest();
    txtFile.open("GET", "file://d:/data.txt", true);
    txtFile.onreadystatechange = function()
    {
        allText = txtFile.responseText;
        allTextLines = allText.split(/\r\n|\n/);
    };

    document.write(allTextLines);<br>
    document.write(allText);<br>
    document.write(txtFile);<br>
</script>
Mahesh Thumar
fuente
Sin saltos de línea en su archivo CSV, será imposible para cualquier código JavaScript saber dónde se detiene una matriz (u objeto) y comienza la otra (a menos que sepa de antemano que siempre hay exactamente cinco encabezados). ¿Era esto un descuido de cortar y pegar?
Blazemonger
Sí, sé de antemano que hay exactamente cinco campos.
Mahesh Thumar
1
Siguiente pregunta: ¿se permite jQuery en la solución? Usó la etiqueta pero su código de muestra es JavaScript puro.
Blazemonger
sí, jQuery está permitido, por eso lo incluyo en la etiqueta.
Mahesh Thumar
1
No creo que file://...se permita el uso de XMLHttpRequest.
Noel Llevares

Respuestas:

118

NOTA: inventé esta solución antes de recordar todos los "casos especiales" que pueden ocurrir en un archivo CSV válido, como las comillas escapadas. Dejo mi respuesta para aquellos que quieren algo rápido y sucio, pero recomiendo la respuesta de Evan para mayor precisión.


Este código funcionará cuando su data.txtarchivo sea una larga cadena de entradas separadas por comas, sin nuevas líneas:

data.txt:

 heading1,heading2,heading3,heading4,heading5,value1_1,...,value5_2

javascript:

$(document).ready(function() {
    $.ajax({
        type: "GET",
        url: "data.txt",
        dataType: "text",
        success: function(data) {processData(data);}
     });
});

function processData(allText) {
    var record_num = 5;  // or however many elements there are in each row
    var allTextLines = allText.split(/\r\n|\n/);
    var entries = allTextLines[0].split(',');
    var lines = [];

    var headings = entries.splice(0,record_num);
    while (entries.length>0) {
        var tarr = [];
        for (var j=0; j<record_num; j++) {
            tarr.push(headings[j]+":"+entries.shift());
        }
        lines.push(tarr);
    }
    // alert(lines);
}

El siguiente código funcionará en un archivo CSV "verdadero" con saltos de línea entre cada conjunto de registros:

data.txt:

heading1,heading2,heading3,heading4,heading5
value1_1,value2_1,value3_1,value4_1,value5_1
value1_2,value2_2,value3_2,value4_2,value5_2

javascript:

$(document).ready(function() {
    $.ajax({
        type: "GET",
        url: "data.txt",
        dataType: "text",
        success: function(data) {processData(data);}
     });
});

function processData(allText) {
    var allTextLines = allText.split(/\r\n|\n/);
    var headers = allTextLines[0].split(',');
    var lines = [];

    for (var i=1; i<allTextLines.length; i++) {
        var data = allTextLines[i].split(',');
        if (data.length == headers.length) {

            var tarr = [];
            for (var j=0; j<headers.length; j++) {
                tarr.push(headers[j]+":"+data[j]);
            }
            lines.push(tarr);
        }
    }
    // alert(lines);
}

http://jsfiddle.net/mblase75/dcqxr/

Blazemonger
fuente
44
Por cierto, esto supone que el archivo CSV tiene de hecho múltiples filas, eso es en lo que se allText.split(/\r\n|\n/)divide. Si todos sus datos son, de hecho, una larga cadena de datos separados por comas sin líneas nuevas, no es un archivo CSV real.
Blazemonger
1
Hola, he usado este código: pero no hay salida. Solo se muestra una alerta en blanco. mi archivo se ve así: título1, título2, título3, título4, título5, valor1_1, valor2_1, valor3_1, valor4_1, valor5_1, valor1_2, valor2_2, valor3_2, valor4_2, valor5_2 Tanto csv.html como data.txt están en la misma carpeta
Mahesh Thumar
Si este no es el archivo (o datos) correcto, ¿cómo debería verse mi archivo?
Mahesh Thumar
77
El código puede no manejar todos los archivos CSV estándar IETF válidos, y puede fallar si hay cadenas que tienen comas, saltos de línea o comillas dobles. Por ejemplo, lo 1, "IETF allows ""quotes"", commas and \nline breaks"que está permitido ya que la cadena está entre comillas dobles y las comillas dobles se escapan.
prototipo
1
Intentaba leer un archivo .csv desde una Mac. ¡Solo pude hacer que este script reconociera caracteres de nueva línea cuando cambié la primera división a esto! var allTextLines = allText.split("\r"); Después de eso, ¡funcionó muy bien! ¡Gracias!
Joe
207

No es necesario que escribas el tuyo ...

La biblioteca jQuery-CSV tiene una función llamada $.csv.toObjects(csv)que realiza la asignación automáticamente.

Nota: La biblioteca está diseñada para manejar cualquier dato CSV que cumpla con RFC 4180 , incluidos todos los casos extremos desagradables que la mayoría de las soluciones 'simples' pasan por alto.

Como ya dijo @Blazemonger, primero debe agregar saltos de línea para que los datos sean CSV válidos.

Usando el siguiente conjunto de datos:

heading1,heading2,heading3,heading4,heading5
value1_1,value2_1,value3_1,value4_1,value5_1
value1_2,value2_2,value3_2,value4_2,value5_2

Usa el código:

var data = $.csv.toObjects(csv):

La salida guardada en 'datos' será:

[
  { heading1:"value1_1",heading2:"value2_1",heading3:"value3_1",heading4:"value4_1",heading5:"value5_1" } 
  { heading1:"value1_2",heading2:"value2_2",heading3:"value3_2",heading4:"value4_2",heading5:"value5_2" }
]

Nota: Técnicamente, la forma en que escribió la asignación de clave-valor es JavaScript no válido. Los objetos que contienen los pares clave-valor deben estar entre paréntesis.

Si desea probarlo usted mismo, le sugiero que eche un vistazo a la Demostración de uso básico en la pestaña 'toObjects ()'.

Descargo de responsabilidad: soy el autor original de jQuery-CSV.

Actualizar:

Editado para usar el conjunto de datos que el operador proporcionó e incluyó un enlace a la demostración donde se puede probar la validez de los datos.

Actualización2:

Debido al cierre de Google Code. jquery-csv se mudó a GitHub

Evan Plaice
fuente
3
IOW, "toObject" es o puede considerarse como "toJSON", ¿no? Y, ¿el colon que sigue a la llamada a toObjects (csv) es un error tipográfico? IOW, ¿no debería ser un punto y coma?
B. Clay Shannon
11
¿CSV es un nombre de archivo?
burbuja
10
Fantástica biblioteca. Para su información, el parámetro csvpasado es una cadena csv: lea el archivo csv como texto para obtener la cadena csv.
callmekatootie
3
@Evan Plaice ¿Cómo usar esta biblioteca para leer desde un archivo csv?
Richa Sinha
1
@RichaSinha Lea el archivo como un búfer de texto a través de la API de archivos HTML5 o AJAX. Luego pase el buffer de cadena al analizador. Como resultado, escupirá una matriz de datos. Vea la página del proyecto para ver ejemplos.
Evan Plaice
75

No se divida en comas: no funcionará para la mayoría de los archivos CSV, y esta pregunta tiene demasiadas vistas para que el tipo de datos de entrada del solicitante se aplique a todos. Analizar CSV es un poco aterrador ya que no existe un estándar realmente oficial, y muchos escritores de texto delimitados no consideran casos extremos.

Esta pregunta es antigua, pero creo que hay una mejor solución ahora que Papa Parse está disponible. Es una biblioteca que escribí, con ayuda de colaboradores, que analiza el texto o los archivos CSV. Es la única biblioteca JS que conozco que admite archivos de gigabytes de tamaño. También maneja la entrada malformada con gracia.

Archivo de 1 GB analizado en 1 minuto: Analizó un archivo de 1 GB en 1 minuto

( Actualización: con Papa Parse 4, el mismo archivo tomó solo unos 30 segundos en Firefox. Papa Parse 4 es ahora el analizador CSV más rápido conocido para el navegador).

Analizar texto es muy fácil:

var data = Papa.parse(csvString);

Analizar archivos también es fácil:

Papa.parse(file, {
    complete: function(results) {
        console.log(results);
    }
});

La transmisión de archivos es similar (aquí hay un ejemplo que transmite un archivo remoto):

Papa.parse("http://example.com/bigfoo.csv", {
    download: true,
    step: function(row) {
        console.log("Row:", row.data);
    },
    complete: function() {
        console.log("All done!");
    }
});

Si su página web se bloquea durante el análisis, Papa puede usar trabajadores web para mantener su sitio web reactivo.

Papa puede detectar automáticamente delimitadores y unir valores con columnas de encabezado, si hay una fila de encabezado. También puede convertir valores numéricos en tipos de números reales. Analiza adecuadamente los saltos de línea y las comillas y otras situaciones extrañas, e incluso maneja la entrada con formato incorrecto de la manera más sólida posible. Me inspiré en las bibliotecas existentes para hacer Papa, por lo que es compatible con otras implementaciones de JS.

Mate
fuente
¡Papa es fácil de usar y rápido! ¡Gracias!
Technotronic
+1 Buen trabajo en Papa Parse. Me gustaría estudiarlo en detalle algún día para ver cómo manejó los archivos grandes y la transmisión. Estoy muy feliz de ver a otros desarrolladores escribiendo analizadores con todas las funciones que retoman donde dejó jquery-csv.
Evan Plaice
3
@EvanPlaice Gracias. Puede que le guste esta presentación que di anoche en una reunión local: docs.google.com/presentation/d/…
Matt
1
@ Matt Esa fue una presentación increíble que describe sobre el análisis de papá de una manera más comprensiva
siva
1
@ Malky.Kid Eso no es CSV válido (es decir, los espacios en un valor no delimitado no son buenos). La implementación del formato CSV de MS Excel apesta. Si aún tiene acceso al archivo fuente, debería haber una opción para habilitar delimitadores de comillas. Una vez que haga eso, sus datos deberían funcionar con cualquier analizador csv.
Evan Plaice
10

Estoy usando d3.js para analizar el archivo csv. Muy facil de usar. Aquí están los documentos .

Pasos:

  • npm install d3-request

Usando Es6;

import { csv } from 'd3-request';
import url from 'path/to/data.csv';

csv(url, function(err, data) {
 console.log(data);
})

Por favor, vea los documentos para más.

Actualización: la solicitud d3 está en desuso. puedes usar d3-fetch

Bimal Grg
fuente
4

Puedes usar PapaParse para ayudar. https://www.papaparse.com/

Aquí hay un CodePen. https://codepen.io/sandro-wiggers/pen/VxrxNJ

Papa.parse(e, {
            header:true,
            before: function(file, inputElem){ console.log('Attempting to Parse...')},
            error: function(err, file, inputElem, reason){ console.log(err); },
            complete: function(results, file){ $.PAYLOAD = results; }
        });
Sandro Wiggers
fuente
3

Aquí hay una función de JavaScript que analiza los datos CSV, que representan las comas que se encuentran dentro de las comillas.

// Parse a CSV row, accounting for commas inside quotes                   
function parse(row){
  var insideQuote = false,                                             
      entries = [],                                                    
      entry = [];
  row.split('').forEach(function (character) {                         
    if(character === '"') {
      insideQuote = !insideQuote;                                      
    } else {
      if(character == "," && !insideQuote) {                           
        entries.push(entry.join(''));                                  
        entry = [];                                                    
      } else {
        entry.push(character);                                         
      }                                                                
    }                                                                  
  });
  entries.push(entry.join(''));                                        
  return entries;                                                      
}

Ejemplo de uso de la función para analizar un archivo CSV que se ve así:

"foo, the column",bar
2,3
"4, the value",5

en matrices:

// csv could contain the content read from a csv file
var csv = '"foo, the column",bar\n2,3\n"4, the value",5',

    // Split the input into lines
    lines = csv.split('\n'),

    // Extract column names from the first line
    columnNamesLine = lines[0],
    columnNames = parse(columnNamesLine),

    // Extract data from subsequent lines
    dataLines = lines.slice(1),
    data = dataLines.map(parse);

// Prints ["foo, the column","bar"]
console.log(JSON.stringify(columnNames));

// Prints [["2","3"],["4, the value","5"]]
console.log(JSON.stringify(data));

Así es como puede transformar los datos en objetos, como el analizador csv de D3 (que es una solución sólida de terceros):

var dataObjects = data.map(function (arr) {
  var dataObject = {};
  columnNames.forEach(function(columnName, i){
    dataObject[columnName] = arr[i];
  });
  return dataObject;
});

// Prints [{"foo":"2","bar":"3"},{"foo":"4","bar":"5"}]
console.log(JSON.stringify(dataObjects));

Aquí hay un violín funcional de este código .

¡Disfrutar! - Curran

curran
fuente
1

Aquí hay otra forma de leer un CSV externo en Javascript (usando jQuery).

Es un poco más largo, pero creo que al leer los datos en matrices puede seguir exactamente el proceso y facilita la resolución de problemas.

Podría ayudar a alguien más.

El ejemplo del archivo de datos:

Time,data1,data2,data2
08/11/2015 07:30:16,602,0.009,321

Y aquí está el código:

$(document).ready(function() {
 // AJAX in the data file
    $.ajax({
        type: "GET",
        url: "data.csv",
        dataType: "text",
        success: function(data) {processData(data);}
        });

    // Let's process the data from the data file
    function processData(data) {
        var lines = data.split(/\r\n|\n/);

        //Set up the data arrays
        var time = [];
        var data1 = [];
        var data2 = [];
        var data3 = [];

        var headings = lines[0].split(','); // Splice up the first row to get the headings

        for (var j=1; j<lines.length; j++) {
        var values = lines[j].split(','); // Split up the comma seperated values
           // We read the key,1st, 2nd and 3rd rows 
           time.push(values[0]); // Read in as string
           // Recommended to read in as float, since we'll be doing some operations on this later.
           data1.push(parseFloat(values[1])); 
           data2.push(parseFloat(values[2]));
           data3.push(parseFloat(values[3]));

        }

    // For display
    var x= 0;
    console.log(headings[0]+" : "+time[x]+headings[1]+" : "+data1[x]+headings[2]+" : "+data2[x]+headings[4]+" : "+data2[x]);
    }
})

¡Espero que esto ayude a alguien en el futuro!

FredFury
fuente
Hola desde el futuro, así que probé esta respuesta y me faltaba una )señal en la línea 45, así que la agregué, pero ahora en la línea 9 me está dando un error de consola Uncaught ReferenceError: $ is not defined at index.html:9¿Podría ayudarme en esto?
Lasagna Cat
1
function CSVParse(csvFile)
{
    this.rows = [];

    var fieldRegEx = new RegExp('(?:\s*"((?:""|[^"])*)"\s*|\s*((?:""|[^",\r\n])*(?:""|[^"\s,\r\n]))?\s*)(,|[\r\n]+|$)', "g");   
    var row = [];
    var currMatch = null;

    while (currMatch = fieldRegEx.exec(this.csvFile))
    {
        row.push([currMatch[1], currMatch[2]].join('')); // concatenate with potential nulls

        if (currMatch[3] != ',')
        {
            this.rows.push(row);
            row = [];
        }

        if (currMatch[3].length == 0)
            break;
    }
}

Me gusta que la expresión regular haga todo lo posible. Esta expresión regular trata todos los elementos como entre comillas o sin comillas, seguidos por un delimitador de columna o un delimitador de fila. O al final del texto.

Es por eso que la última condición, sin ella, sería un bucle infinito ya que el patrón puede coincidir con un campo de longitud cero (totalmente válido en csv). Pero como $ es una afirmación de longitud cero, no progresará a una no coincidencia y finalizará el ciclo.

Y para su información, tuve que hacer que la segunda alternativa excluya las comillas que rodean el valor; parece que se estaba ejecutando antes de la primera alternativa en mi motor javascript y considerando las comillas como parte del valor sin comillas. No preguntaré, solo lo hice funcionar.

Gerard ONeill
fuente
Lamentablemente me metí en un bucle infinito con esta función.
Hauke
@Hauke: si pudieras dividir los datos en un par de columnas y líneas que aún producen el bucle infinito, te lo agradecería, puede darme una idea de por qué estaba fallando antes.
Gerard ONeill
1

Por la respuesta aceptada ,

Obtuve esto para trabajar cambiando el 1 a 0 aquí:

for (var i=1; i<allTextLines.length; i++) {

cambiado a

for (var i=0; i<allTextLines.length; i++) {

Calculará un archivo con una línea continua que tiene una longitud allTextLines.length de 1. Entonces, si el ciclo comienza en 1 y se ejecuta siempre que sea menor que 1, nunca se ejecuta. De ahí el cuadro de alerta en blanco.

Adam Grant
fuente
0

Si desea resolver esto sin usar Ajax , use la FileReader()API web .

Implementación de ejemplo:

  1. Seleccionar .csvarchivo
  2. Ver salida

function readSingleFile(e) {
  var file = e.target.files[0];
  if (!file) {
    return;
  }

  var reader = new FileReader();
  reader.onload = function(e) {
    var contents = e.target.result;
    displayContents(contents);
    displayParsed(contents);
  };
  reader.readAsText(file);
}

function displayContents(contents) {
  var element = document.getElementById('file-content');
  element.textContent = contents;
}

function displayParsed(contents) {
  const element = document.getElementById('file-parsed');
  const json = contents.split(',');
  element.textContent = JSON.stringify(json);
}

document.getElementById('file-input').addEventListener('change', readSingleFile, false);
<input type="file" id="file-input" />

<h3>Raw contents of the file:</h3>
<pre id="file-content">No data yet.</pre>

<h3>Parsed file contents:</h3>
<pre id="file-parsed">No data yet.</pre>

Robin Rpr.
fuente
0
$(function() {

      $("#upload").bind("click", function() {
            var regex = /^([a-zA-Z0-9\s_\\.\-:])+(.csv|.xlsx)$/;
            if (regex.test($("#fileUpload").val().toLowerCase())) {
              if (typeof(FileReader) != "undefined") {
                var reader = new FileReader();
                reader.onload = function(e) {
                    var customers = new Array();
                    var rows = e.target.result.split("\r\n");
                    for (var i = 0; i < rows.length - 1; i++) {
                      var cells = rows[i].split(",");
                      if (cells[0] == "" || cells[0] == undefined) {
                        var s = customers[customers.length - 1];
                        s.Ord.push(cells[2]);
                      } else {
                        var dt = customers.find(x => x.Number === cells[0]);
                        if (dt == undefined) {
                          if (cells.length > 1) {
                            var customer = {};
                            customer.Number = cells[0];
                            customer.Name = cells[1];
                            customer.Ord = new Array();

                            customer.Ord.push(cells[2]);
                            customer.Point_ID = cells[3];
                            customer.Point_Name = cells[4];
                            customer.Point_Type = cells[5];
                            customer.Set_ORD = cells[6];
                            customers.push(customer);
                          }
                        } else {
                          var dtt = dt;
                          dtt.Ord.push(cells[2]);

                        }
                      }
                    }
rahul kulkarni
fuente
Si bien este código puede resolver la pregunta, incluir una explicación de cómo y por qué esto resuelve el problema realmente ayudaría a mejorar la calidad de su publicación, y probablemente resultaría en más votos positivos. Recuerde que está respondiendo la pregunta para los lectores en el futuro, no solo la persona que pregunta ahora. Por favor, editar su respuesta para agregar explicaciones y dar una indicación de lo que se aplican limitaciones y supuestos. De la opinión
doble pitido
0

En realidad, puede usar una biblioteca ligera llamada cualquier texto .

  • instalar dependencias
npm i -D any-text
  • use un comando personalizado para leer archivos
var reader = require('any-text');
 
reader.getText(`path-to-file`).then(function (data) {
  console.log(data);
});

o use async-await:

var reader = require('any-text');
 
const chai = require('chai');
const expect = chai.expect;
 
describe('file reader checks', () => {
  it('check csv file content', async () => {
    expect(
      await reader.getText(`${process.cwd()}/test/files/dummy.csv`)
    ).to.contains('Lorem ipsum');
  });
});
Abhinaba
fuente