Como ler dados do arquivo * .CSV usando javascript?

192

Meus dados csv são assim:

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 ....

Como você lê esses dados e converte para uma 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] ....

Eu tentei esse código, mas sem sorte !:

<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
fonte
Sem quebras de linha no seu arquivo CSV, será impossível para qualquer código JavaScript saber onde uma matriz (ou objeto) para e a outra começa (a menos que você saiba com antecedência que sempre existem exatamente cinco títulos). Foi uma supervisão de cortar e colar?
Blazemonger
Sim, eu sei de antemão que existem exatamente cinco campos.
Mahesh Thumar 15/09
1
Próxima pergunta: o jQuery é permitido na solução? Você usou a tag, mas seu código de amostra é JavaScript puro.
Blazemonger
sim, o jQuery é permitido, é por isso que eu o incluo no Tag.
Mahesh Thumar
1
Eu não acho que o uso de file://...é permitido XMLHttpRequest.
22412 Noel Llevares

Respostas:

118

NOTA: Eu inventei essa solução antes de me lembrar de todos os "casos especiais" que podem ocorrer em um arquivo CSV válido, como aspas de escape. Estou deixando minha resposta para quem quer algo rápido e sujo, mas recomendo a resposta de Evan para precisão.


Esse código funcionará quando o data.txtarquivo for uma sequência longa de entradas separadas por vírgula, sem novas linhas:

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);
}

O código a seguir funcionará em um arquivo CSV "verdadeiro" com quebras de linha 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
fonte
4
A propósito, isso pressupõe que o arquivo CSV tenha de fato várias linhas - é nisso que se allText.split(/\r\n|\n/)divide. Se todos os seus dados são de fato uma longa sequência de dados separados por vírgula sem novas linhas, não é um arquivo CSV real.
Blazemonger
1
Oi, eu uso este código: Mas não há saída. Apenas um alerta em branco é exibido. meu arquivo tem a seguinte aparência: cabeçalho1, cabeçalho2, cabeçalho3, cabeçalho4, cabeçalho5, valor1_1, valor2_1, valor3_1, valor4_1, valor5_1, valor1_2, valor2_2, valor3_2, valor4_2, valor5_2 csv.html e data.txt estão na mesma pasta
Mahesh Thumar
Se este arquivo não estiver correto (ou dados), como deve ser meu arquivo ??
Mahesh Thumar
7
O código pode não manipular todos os arquivos CSV padrão IETF válidos e pode falhar se houver seqüências de caracteres que tenham vírgulas incorporadas, quebras de linha ou aspas duplas. Por exemplo, o 1, "IETF allows ""quotes"", commas and \nline breaks"que é permitido, já que a string é cercada por aspas duplas e as aspas duplas são escapadas.
prototype
1
Eu estava tentando ler um arquivo .csv de um mac. Eu só consegui que esse script reconhecesse caracteres de nova linha quando mudei a primeira divisão para isso var allTextLines = allText.split("\r"); . Obrigado!
Joe
204

Não há necessidade de escrever o seu próprio ...

A biblioteca jQuery-CSV possui uma função chamada $.csv.toObjects(csv)que faz o mapeamento automaticamente.

Nota: A biblioteca foi projetada para lidar com todos os dados CSV compatíveis com RFC 4180 , incluindo todos os casos extremos desagradáveis ​​que a maioria das soluções "simples" ignora.

Como o @Blazemonger já afirmou, primeiro você precisa adicionar quebras de linha para tornar os dados válidos em CSV.

Usando o seguinte conjunto de dados:

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

Use o código:

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

A saída salva em 'dados' 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: Tecnicamente, a maneira como você escreveu o mapeamento de valores-chave é JavaScript inválido. Os objetos que contêm os pares de valores-chave devem ser colocados entre colchetes.

Se você quiser experimentar por si mesmo, sugiro que você dê uma olhada na Demonstração básica de uso na guia 'toObjects ()'.

Disclaimer: Eu sou o autor original do jQuery-CSV.

Atualizar:

Editado para usar o conjunto de dados que a operação forneceu e incluiu um link para a demonstração em que os dados podem ser testados quanto à validade.

Update2:

Devido à interrupção do Google Code. jquery-csv mudou-se para o GitHub

Evan Plaice
fonte
3
IOW, "toObject" é ou pode ser pensado como "toJSON", não? E, os dois pontos após a chamada para toObjects (csv) são um erro de digitação? IOW, não deveria ser um ponto e vírgula?
B. Clay Shannon
11
CSV é um nome de arquivo?
Bolha
10
Biblioteca fantástica. Para sua informação, o parâmetro csvpassado é uma string csv - leia o arquivo csv como texto para obter a string csv.
Callmekatootie
3
@Evan Plaice Como usar esta biblioteca para ler um arquivo csv?
Richa Sinha
1
@RichaSinha Leia o arquivo como um buffer de texto por meio da API de arquivos HTML5 ou AJAX. Em seguida, passe o buffer de cadeia para o analisador. Ele cuspirá uma matriz de dados como resultado. Veja a página do projeto para exemplos.
Evan Plaice
75

Não divida em vírgulas - ele não funcionará para a maioria dos arquivos CSV, e essa pergunta tem muitas visualizações para que o tipo de dado de entrada do solicitante seja aplicado a todos. A análise de CSV é meio assustadora, pois não existe um padrão verdadeiramente oficial e muitos redatores de texto delimitados não consideram casos extremos.

Essa pergunta é antiga, mas acredito que há uma solução melhor agora que o Papa Parse está disponível. É uma biblioteca que escrevi, com a ajuda de colaboradores, que analisa texto ou arquivos CSV. É a única biblioteca JS que conheço que suporta arquivos em tamanho de gigabytes. Ele também lida com entrada malformada normalmente.

Arquivo de 1 GB analisado em 1 minuto: Arquivo de 1 GB analisado em 1 minuto

( Atualização: com o Papa Parse 4, o mesmo arquivo levou apenas 30 segundos no Firefox. O Papa Parse 4 agora é o analisador CSV mais rápido conhecido para o navegador.)

A análise de texto é muito fácil:

var data = Papa.parse(csvString);

A análise de arquivos também é fácil:

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

O streaming de arquivos é semelhante (veja um exemplo que transmite um arquivo remoto):

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

Se sua página da web trava durante a análise, o Papa pode usar profissionais da web para manter seu site reativo.

O Papa pode detectar automaticamente delimitadores e combinar valores com colunas de cabeçalho, se uma linha de cabeçalho estiver presente. Também pode transformar valores numéricos em tipos de números reais. Ele analisa adequadamente quebras de linha, aspas e outras situações estranhas e até lida com entradas malformadas da maneira mais robusta possível. Eu me inspirei nas bibliotecas existentes para criar o Papa, então adoro outras implementações de JS.

Matt
fonte
Papa é fácil de usar e rápido! Obrigado!
Technotronic
+1 Bom trabalho no Papa Parse. Eu gostaria de estudá-lo em detalhes um dia para ver como você lida com arquivos grandes e com streaming. Estou muito feliz em ver outros desenvolvedores escrevendo analisadores com todos os recursos que continuam onde o jquery-csv parou.
Evan Solha
3
@EvanPlaice Thanks. Você pode gostar da apresentação que fiz ontem à noite em um encontro local: docs.google.com/presentation/d/…
Matt
1
@ Matt Foi uma apresentação incrível, que descreve sobre parse papa de forma mais compreensiva
siva
1
@ Malky.Kid Isso não é CSV válido (ou seja, espaços em um valor não delimitado não são bons). A implementação do formato CSV do MS Excel é péssima. Se você ainda tiver acesso ao arquivo de origem, deve haver uma opção para ativar os delimitadores de cotação. Depois de fazer isso, seus dados deverão funcionar com qualquer analisador csv.
Evan Plaice
9

Estou usando o d3.js para analisar o arquivo csv. Muito fácil de usar. Aqui estão os documentos .

Passos:

  • 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);
})

Consulte os documentos para obter mais informações.

Atualização - a solicitação d3 está obsoleta. você pode usar d3-fetch

Bimal Grg
fonte
4

Você pode usar o PapaParse para ajudar. https://www.papaparse.com/

Aqui está um 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
fonte
3

Aqui está uma função JavaScript que analisa dados CSV, contabilizando vírgulas encontradas entre aspas.

// 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;                                                      
}

Exemplo de uso da função para analisar um arquivo CSV parecido com este:

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

em matrizes:

// 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));

Veja como você pode transformar os dados em objetos, como o analisador csv do D3 (que é uma solução sólida de terceiros):

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));

Aqui está um violino funcional desse código .

Aproveitar! - Curran

curran
fonte
1

Aqui está outra maneira de ler um CSV externo em Javascript (usando jQuery).

É um pouco mais demorado, mas, ao ler os dados em matrizes, você pode seguir exatamente o processo e facilitar a solução de problemas.

Pode ajudar alguém.

O exemplo do arquivo de dados:

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

E aqui está o 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 isso ajude alguém no futuro!

FredFury
fonte
Olá do futuro, então tentei esta resposta e faltava um )sinal na linha 45, então a adicionei, mas agora na linha 9 está me dando um erro no console. Uncaught ReferenceError: $ is not defined at index.html:9Você poderia ajudar nisso?
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;
    }
}

Eu gosto de ter o regex fazer o máximo possível. Essa regex trata todos os itens como entre aspas ou não, seguidos por um delimitador de coluna ou delimitador de linha. Ou o fim do texto.

É por isso que essa última condição - sem ela, seria um loop infinito, pois o padrão pode corresponder a um campo de comprimento zero (totalmente válido em csv). Mas como $ é uma afirmação de comprimento zero, ele não progride para uma não correspondência e termina o loop.

E, para sua informação, eu tive que fazer a segunda alternativa excluir aspas em torno do valor; parece que estava sendo executado antes da primeira alternativa no meu mecanismo javascript e considerando as aspas como parte do valor não citado. Não vou perguntar - apenas fiz funcionar.

Gerard ONeill
fonte
Infelizmente, entrei em um loop infinito com essa função.
Hauke
@ Hauke ​​- se você pudesse dividir os dados em duas colunas e linhas que ainda produzem o loop infinito, eu apreciaria - isso pode me dar uma ideia do porquê de falhar antes.
precisa
1

Pela resposta aceita ,

Eu consegui isso, alterando o 1 para 0 aqui:

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

alterado para

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

Ele calculará o arquivo com uma linha contínua como tendo um allTextLines.length de 1. Portanto, se o loop iniciar em 1 e executar enquanto for menor que 1, ele nunca será executado. Daí a caixa de alerta em branco.

Adam Grant
fonte
0

Se você deseja resolver isso sem usar o Ajax , use a FileReader()API da Web .

Exemplo de implementação:

  1. Selecionar .csvarquivo
  2. Ver saída

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.
fonte
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
fonte
Embora esse código possa resolver a questão, incluindo uma explicação de como e por que isso resolve o problema realmente ajudaria a melhorar a qualidade da sua postagem e provavelmente resultará em mais votos positivos. Lembre-se de que você está respondendo à pergunta dos leitores no futuro, não apenas à pessoa que está perguntando agora. Por favor edite sua resposta para adicionar explicações e dar uma indicação do que limitações e premissas se aplicam. Da avaliação
double-beep