Estou tentando ler um arquivo grande, uma linha por vez. Encontrei uma pergunta no Quora que tratava do assunto, mas estou perdendo algumas conexões para fazer a coisa toda se encaixar.
var Lazy=require("lazy");
new Lazy(process.stdin)
.lines
.forEach(
function(line) {
console.log(line.toString());
}
);
process.stdin.resume();
O pouco que eu gostaria de descobrir é como eu poderia ler uma linha de cada vez de um arquivo em vez de STDIN, como neste exemplo.
Eu tentei:
fs.open('./VeryBigFile.csv', 'r', '0666', Process);
function Process(err, fd) {
if (err) throw err;
// DO lazy read
}
mas não está funcionando. Eu sei que em uma pitada eu poderia voltar a usar algo como PHP, mas eu gostaria de descobrir isso.
Eu não acho que a outra resposta funcionaria, pois o arquivo é muito maior do que o servidor no qual estou executando ele tem memória.
javascript
node.js
file-io
lazy-evaluation
Alex C
fonte
fonte
fs.readSync()
. Você pode ler octetos binários em um buffer, mas não há maneira fácil de lidar com caracteres UTF-8 ou UTF-16 parciais sem inspecionar o buffer antes de convertê-lo em strings JavaScript e procurar EOLs. OBuffer()
tipo não possui um conjunto de funções tão rico para operar em suas instâncias quanto as cadeias nativas, mas as cadeias nativas não podem conter dados binários. Parece-me que a falta de uma maneira integrada de ler linhas de texto de manipuladores de arquivos arbitrários é uma lacuna real no node.js.if (line.length==1 && line[0] == 48) special(line);
node
's docs API github.com/nodejs/node/pull/4609Respostas:
Desde o Node.js. v0.12 e a partir do Node.js. v4.0.0, existe um módulo principal do readline estável . Aqui está a maneira mais fácil de ler linhas de um arquivo, sem módulos externos:
Ou alternativamente:
A última linha é lida corretamente (a partir do Nó v0.12 ou posterior), mesmo se não houver final
\n
.UPDATE : este exemplo foi adicionado à documentação oficial da API do Node .
fonte
rl.on('close', cb)
Para uma operação tão simples, não deve haver dependência de módulos de terceiros. Vá com calma.
fonte
line
eventos acontecem somente após o acerto\n
, ou seja, todas as alternativas são perdidas (consulte unicode.org/reports/tr18/#Line_Boundaries ). # 2, os dados após o último\n
são ignorados silenciosamente (consulte stackoverflow.com/questions/18450197/… ). Eu chamaria essa solução de perigosa porque ela funciona em 99% de todos os arquivos e em 99% dos dados, mas falha silenciosamente nos demais. sempre quefs.writeFileSync( path, lines.join('\n'))
você escrever um arquivo que será apenas parcialmente lido pela solução acima.readline
pacote se comporta de maneiras verdadeiramente bizarras para um programador Unix / Linux experiente.rd.on("close", ..);
podem ser utilizados como uma chamada de retorno (occurrs quando todas as linhas são lidas)Você não precisa
open
do arquivo, mas sim criar um arquivoReadStream
.fs.createReadStream
Depois passe esse fluxo para
Lazy
fonte
new lazy(fs.createReadStream('...')).lines.forEach(function(l) { /* ... */ }).join(function() { /* Done */ })
new lazy(...).lines.forEach(...).on('end', function() {...})
.on('end'...
depois.forEach(...)
, quando na verdade tudo se comportava como esperado quando liguei o evento primeiro .existe um módulo muito bom para ler um arquivo linha por linha, chamado leitor de linha
com isso você simplesmente escreve:
você pode até iterar o arquivo com uma interface "estilo java", se precisar de mais controle:
fonte
process/stdin
). Pelo menos, se puder, certamente não é óbvio lendo o código e tentando-o.readline
módulo principal .function(reader)
efunction(line)
deve ser:function(err,reader)
efunction(err,line)
.line-reader
lê o arquivo de forma assíncrona. A alternativa síncrona éline-reader-sync
fonte
Atualização em 2019
Um exemplo impressionante já foi publicado na documentação oficial do Nodejs. aqui
Isso requer que o Nodejs mais recente esteja instalado em sua máquina. > 11,4
fonte
await
s entre acreateInterface()
chamada e o início dofor await
loop, você misteriosamente perderá linhas desde o início do arquivo.createInterface()
imediatamente começa a emitir linhas nos bastidores, e o iterador assíncrono criado implicitamente comconst line of rl
não pode começar a ouvir essas linhas até que elas sejam criadas.Tópico antigo, mas isso funciona:
Simples. Não há necessidade de um módulo externo.
fonte
readline is not defined
oufs is not defined
, adicionevar readline = require('readline');
evar fs = require('fs');
faça com que isso funcione. Caso contrário, doce, doce código. Obrigado.Você sempre pode rolar seu próprio leitor de linha. Ainda não comparei esse snippet, mas ele divide corretamente o fluxo de pedaços de entrada em linhas sem o \ \ n 'final'
Eu criei isso ao trabalhar em um script rápido de análise de log que precisava acumular dados durante a análise de log e achei que seria bom tentar fazer isso usando js e node em vez de usar perl ou bash.
De qualquer forma, acho que os pequenos scripts do nodejs devem ser independentes e não contar com módulos de terceiros. Portanto, depois de ler todas as respostas para essa pergunta, cada um usando vários módulos para lidar com a análise de linha, uma solução nativa do 13 SLOC nodejs pode ser interessante.
fonte
stdin
... a menos que eu esteja sentindo falta de algo.ReadStream
comfs.createReadStream('./myBigFile.csv')
e usá-lo em vez destdin
readline
módulo principal .Com o módulo transportador :
fonte
var inStream = fs.createReadStream('input.txt', {flags:'r'});
Mas sua sintaxe é mais limpa que o método documentado de usar .on ():carrier.carry(inStream).on('line', function(line) { ...
\r\n
e\n
finais de linha. Se você precisar lidar com arquivos de teste no estilo MacOS anteriores ao OS X, eles usaram o\r
transportador e não tratam disso. Surpreendentemente, ainda existem esses arquivos flutuando na natureza. Talvez você também precise manipular explicitamente a BOM Unicode (marca de ordem de bytes), usada no início dos arquivos de texto na esfera de influência do MS Windows.readline
módulo principal .Acabei com um vazamento de memória maciço usando o Lazy para ler linha por linha ao tentar processar essas linhas e gravá-las em outro fluxo devido à maneira como a drenagem / pausa / retomada no nó funciona (consulte: http: // elegantcode .com / 2011/04/06 / dando-passos-de-bebê-com-nó-js-bombeando-dados-entre-fluxos / (eu amo esse cara btw)). Eu não olhei atentamente o Lazy para entender exatamente o porquê, mas não pude pausar meu fluxo de leitura para permitir um dreno sem a saída do Lazy.
Eu escrevi o código para processar enormes arquivos csv em documentos xml, você pode ver o código aqui: https://github.com/j03m/node-csv2xml
Se você executar as revisões anteriores com a linha Lazy, ele vazará. A revisão mais recente não vaza e você provavelmente pode usá-la como base para um leitor / processador. Embora eu tenha algumas coisas personalizadas lá.
Edit: Acho que também devo observar que meu código com o Lazy funcionou bem até que me vi escrevendo fragmentos xml grandes o suficiente para drenar / pausar / retomar por necessidade. Para pedaços menores, tudo bem.
fonte
readline
módulo principal .Editar:
Use um fluxo de transformação .
Com um BufferedReader, você pode ler linhas.
fonte
readline
módulo principal .Desde que postei minha resposta original, descobri que a divisão é um módulo de nó muito fácil de usar para leitura de linha em um arquivo; O que também aceita parâmetros opcionais.
Não foi testado em arquivos muito grandes. Deixe-nos saber se você.
fonte
Fiquei frustrado com a falta de uma solução abrangente para isso, então montei minha própria tentativa ( git / npm ). Lista de recursos copiados e colados:
NIH? Você decide :-)
fonte
fonte
data
na chamada parastream.on("data")
poder nunca começar ou terminar com apenas parte de um multibyte UTF-8 caracteres, comoა
que éU+10D0
, composta por três bytese1
83
90
readline
módulo principal .Eu queria resolver esse mesmo problema, basicamente o que em Perl seria:
Meu caso de uso era apenas um script autônomo, não um servidor; portanto, síncrona estava correta. Estes foram os meus critérios:
Este é um projeto para eu ter uma ideia do código de tipo de script de baixo nível no node.js e decidir o quão viável é como um substituto para outras linguagens de script como Perl.
Após uma quantidade surpreendente de esforço e algumas partidas falsas, esse é o código que eu criei. É bem rápido, mas menos trivial do que eu esperava: (bifurque no GitHub)
Provavelmente poderia ser mais limpo, foi o resultado de tentativa e erro.
fonte
Na maioria dos casos, isso deve ser suficiente:
fonte
Leitor de linha baseado em gerador: https://github.com/neurosnap/gen-readlines
fonte
Se você quiser ler um arquivo linha por linha e escrever isso em outro:
fonte
Eu tive o mesmo problema e a solução acima parece semelhante a outras pessoas, mas é aSync e pode ler arquivos grandes muito rapidamente
Espero que isso ajude
fonte
Eu tenho um pequeno módulo que faz isso bem e é usado por muitos outros projetos npm readline Observe que no nó v10 há um módulo nativo de readline, por isso republicou meu módulo como linebyline https://www.npmjs.com/package/ linha por linha
se você não quiser usar o módulo, a função é muito simples:
fonte
Outra solução é executar a lógica por meio do executor seqüencial nsynjs . Ele lê o arquivo linha por linha usando o módulo readline do nó e não usa promessas ou recursão, portanto, não irá falhar em arquivos grandes. Aqui está como o código se parecerá:
O código acima é baseado neste exemplo: https://github.com/amaksr/nsynjs/blob/master/examples/node-readline/index.js
fonte
Duas perguntas que devemos nos fazer durante essas operações são:
Soluções como
require('fs').readFileSync()
carregam o arquivo inteiro na memória. Isso significa que a quantidade de memória necessária para executar operações será quase equivalente ao tamanho do arquivo. Devemos evitá-los por algo maior que50mbs
Podemos rastrear facilmente a quantidade de memória usada por uma função colocando essas linhas de código após a chamada da função:
Neste momento, a melhor maneira de ler linhas específicas de um arquivo grande está usando nó readline . A documentação tem exemplos incríveis .
Embora não precisemos de nenhum módulo de terceiros para fazer isso. Mas, se você estiver escrevendo um código corporativo, precisará lidar com muitos casos extremos. Eu tive que escrever um módulo muito leve chamado Apick File Storage para lidar com todos esses casos extremos .
Módulo Apick File Storage: https://www.npmjs.com/package/apickfs Documentação: https://github.com/apickjs/apickFS#readme
Arquivo de exemplo: https://1drv.ms/t/s!AtkMCsWInsSZiGptXYAFjalXOpUx
Exemplo: módulo de instalação
Este método foi testado com sucesso com arquivos densos de até 4 GB.
big.text é um arquivo de texto denso com 163.845 linhas e tem 124 Mb. O script para ler 10 linhas diferentes desse arquivo usa aproximadamente apenas 4,63 MB apenas de memória. E analisa JSON válido para objetos ou matrizes gratuitamente. 🥳 impressionante !!
Podemos ler uma única linha do arquivo ou centenas de linhas do arquivo com muito pouco consumo de memória.
fonte
Eu uso isso:
use esta função em um fluxo e ouça os eventos de linha que serão emitidos.
gr-
fonte
Embora você provavelmente deva usar o
readline
módulo como a resposta principal sugere,readline
parece estar orientado para interfaces de linha de comando, e não para leitura de linha. Também é um pouco mais opaco em relação ao buffer. (Qualquer pessoa que precise de um leitor orientado a linhas de transmissão provavelmente desejará ajustar os tamanhos do buffer). O módulo readline possui ~ 1000 linhas, enquanto que, com estatísticas e testes, é 34.Aqui está uma versão ainda mais curta, sem as estatísticas, em 19 linhas:
fonte
fonte
Eu envolvo toda a lógica do processamento diário de linha como um módulo npm: line-kit https://www.npmjs.com/package/line-kit
fonte
Eu uso o código abaixo das linhas de leitura depois de verificar se não é um diretório e não está incluído na lista de arquivos não precisa ser verificado.
fonte
Eu examinei todas as respostas acima, todas elas usam uma biblioteca de terceiros para resolvê-la. É ter uma solução simples na API do Node. por exemplo
fonte