node.js:テキストファイルを配列に読み込みます。(配列内の各行に項目。)


163

非常に大きなファイルをnode.jsのJavaScript配列に読み込みたいのですが。

したがって、ファイルが次のような場合:

first line
two 
three
...
...

私は配列を持っているでしょう:

['first line','two','three', ... , ... ] 

関数は次のようになります。

var array = load(filename); 

したがって、すべてを文字列としてロードしてから分割するという考えは受け入れられません。


この質問には、いくつかの深刻な編集とクリーンアップが必要です。それは言う配列にテキストファイルを読んでいますが、すべての答えとコメントを読んだとき、それは本当に手段が一度にテキストファイル1行を読んで。その質問については、@ zswangが今のところ最良の答えを持っています。
ジェス

yupはそのファイルを読み取り、各行を配列にプッシュします:stackoverflow.com/a/34033928/1536309
Blair Anderson

回答:


89

最終的なデータを配列に収めることができれば、提案されているように、文字列に収めて分割することもできませんか?いずれの場合も、一度に1行ずつファイルを処理する場合は、次のようなことも試すことができます。

var fs = require('fs');

function readLines(input, func) {
  var remaining = '';

  input.on('data', function(data) {
    remaining += data;
    var index = remaining.indexOf('\n');
    while (index > -1) {
      var line = remaining.substring(0, index);
      remaining = remaining.substring(index + 1);
      func(line);
      index = remaining.indexOf('\n');
    }
  });

  input.on('end', function() {
    if (remaining.length > 0) {
      func(remaining);
    }
  });
}

function func(data) {
  console.log('Line: ' + data);
}

var input = fs.createReadStream('lines.txt');
readLines(input, func);

編集:phopkinsによるコメントに応じて)サブストリングは(少なくとも新しいバージョンでは)データをコピーせず、特別なSlicedStringオブジェクトを作成すると思います(v8ソースコードを一目見ただけで)。いずれにせよ、ここで言及した部分文字列を回避する変更があります(数メガバイトに相当する「すべての作業とプレイなしのジャックは退屈な少年になる」というファイルでテストされています)。

function readLines(input, func) {
  var remaining = '';

  input.on('data', function(data) {
    remaining += data;
    var index = remaining.indexOf('\n');
    var last  = 0;
    while (index > -1) {
      var line = remaining.substring(last, index);
      last = index + 1;
      func(line);
      index = remaining.indexOf('\n', last);
    }

    remaining = remaining.substring(last);
  });

  input.on('end', function() {
    if (remaining.length > 0) {
      func(remaining);
    }
  });
}

ありがとう。あなたの質問に答えるには:いいえ、文字列が長すぎます。
chacko

7
私はこれを約2MB程度のファイルで試しましたが、ひどく遅く、ファイルを文字列に同期して読み込むよりもはるかに遅くなりました。私は問題が残りの=残りの部分文字列であると思います。ノードの「データ」は一度に多くを与える可能性があり、そのコピーをすべての行に対して行うとすぐにO(n ^ 2)になります。
フィオナホプキンス


442

同期:

var fs = require('fs');
var array = fs.readFileSync('file.txt').toString().split("\n");
for(i in array) {
    console.log(array[i]);
}

非同期:

var fs = require('fs');
fs.readFile('file.txt', function(err, data) {
    if(err) throw err;
    var array = data.toString().split("\n");
    for(i in array) {
        console.log(array[i]);
    }
});

11
ありがとう。残念ながら、質問を編集する必要がありました。非常に大きなファイルを読み取る方法を意味します。すべてを文字列で読み取ることはできません。
chacko

1
ちょうど私が必要としたもの。シンプルで迅速。
Hcabnettek 2014

16
Windowsで作成されたファイルでこれを実行していることがわかりました。\ r \ nを分割する必要がありましたが、Macが壊れました。より堅牢な; _array = string.replace(/ \ r \ n / g、 '\ n')。split( '\ n'); 両方のために働きました
ウィル・ハンコック

6
+1 Stackoverflowに問題があります。現在、下にスクロールしすぎると、投票数の多い回答が頻繁に見つかります。これもその一例です。投票数は最高ですが、ページの最後に配置されています。Stackoverflowはそれらの順序付けアルゴリズムを改善する必要があると思います。
shashwat

1
@shashwat質問をする人はどちらが正しい答えかを決めることになります。この場合、大きなファイル用のストリーミングソリューションが必要であり、ファイル全体を文字列に入れることは受け入れられません。SOには何の問題もありません。
2016

73

Node.js readlineモジュールを使用する。

var fs = require('fs');
var readline = require('readline');

var filename = process.argv[2];
readline.createInterface({
    input: fs.createReadStream(filename),
    terminal: false
}).on('line', function(line) {
   console.log('Line: ' + line);
});

1
悲しいことに、この解決策には問題があります。ファイルの最後にaがないと、最後の行が表示されません\n!参照:stackoverflow.com/questions/18450197/...
イヴ・M.


14

js:

var array = fs.readFileSync('file.txt', 'utf8').split('\n');

ts:

var array = fs.readFileSync('file.txt', 'utf8').toString().split('\n');

1
投げるために上記のを防ぐためにTypeError: fs.readFileSync(...).split is not a function、あなたはこのよう.toString()を使用する必要があります:var array = fs.readFileSync('file.txt', 'utf8').toString().split('\n');
Qua285

11

readline(documentation)を使用してください。これは、CSSファイルを読み取り、アイコンを解析してjsonに書き込む例です。

var results = [];
  var rl = require('readline').createInterface({
    input: require('fs').createReadStream('./assets/stylesheets/_icons.scss')
  });


  // for every new line, if it matches the regex, add it to an array
  // this is ugly regex :)
  rl.on('line', function (line) {
    var re = /\.icon-icon.*:/;
    var match;
    if ((match = re.exec(line)) !== null) {
      results.push(match[0].replace(".",'').replace(":",''));
    }
  });


  // readline emits a close event when the file is read.
  rl.on('close', function(){
    var outputFilename = './icons.json';
    fs.writeFile(outputFilename, JSON.stringify(results, null, 2), function(err) {
        if(err) {
          console.log(err);
        } else {
          console.log("JSON saved to " + outputFilename);
        }
    });
  });


5

BufferedReaderのが、この関数は非同期でなければなりません。

var load = function (file, cb){
    var lines = [];
    new BufferedReader (file, { encoding: "utf8" })
        .on ("error", function (error){
            cb (error, null);
        })
        .on ("line", function (line){
            lines.push (line);
        })
        .on ("end", function (){
            cb (null, lines);
        })
        .read ();
};

load ("file", function (error, lines){
    if (error) return console.log (error);
    console.log (lines);
});

4

@finbarrのすばらしい答えを追加したいだけです。非同期の例で少し修正します。

非同期:

var fs = require('fs');
fs.readFile('file.txt', function(err, data) {
    if(err) throw err;
    var array = data.toString().split("\n");
    for(i in array) {
        console.log(array[i]);
    }
    done();
});

@ MadPhysicist、done()は非同期を解放するものです。コール。


3

これは、@ mtomisによる上記の回答のバリエーションです。

ラインのストリームを作成します。'data'および 'end'イベントを発行し、ストリームの終了を処理できるようにします。

var events = require('events');

var LineStream = function (input) {
    var remaining = '';

    input.on('data', function (data) {
        remaining += data;
        var index = remaining.indexOf('\n');
        var last = 0;
        while (index > -1) {
            var line = remaining.substring(last, index);
            last = index + 1;
            this.emit('data', line);
            index = remaining.indexOf('\n', last);
        }
        remaining = remaining.substring(last);
    }.bind(this));

    input.on('end', function() {
        if (remaining.length > 0) {
            this.emit('data', remaining);
        }
        this.emit('end');
    }.bind(this));
}

LineStream.prototype = new events.EventEmitter;

これをラッパーとして使用します。

var lineInput = new LineStream(input);

lineInput.on('data', function (line) {
    // handle line
});

lineInput.on('end', function() {
    // wrap it up
});

1
イベントはインスタンス間で共有されます。 var EventEmitter = require('events').EventEmitter; var util = require('util'); function GoodEmitter() { EventEmitter.call(this); } util.inherits(GoodEmitter, EventEmitter);
CTAPbIu_MABP 2014年

正確にどのようなことを話しているのですか?
oferei 2014年

1
作成を試みてvar li1 = new LineStream(input1), li2 = new LineStream(input2);、それぞれに対して「終了」が発生した回数を数えます
CTAPbIu_MABP '29年

それを試してみました。「end」はインスタンスごとに1回発生しました。 var fs = require('fs'); var input1 = fs.createReadStream('text.txt'); var ls1 = new LineStream(input1); ls1.on('data', function (line) { console.log('1:line=' + line); }); ls1.on('end', function (line) { console.log('1:fin'); }); var input2 = fs.createReadStream('text.txt'); var ls2 = new LineStream(input2); ls2.on('data', function (line) { console.log('2:line=' + line); }); ls2.on('end', function (line) { console.log('2:fin'); }); 出力:テキストファイルの各行は、インスタンスごとに1回発生しました。「終わり」もそうだった。
oferei 2014年

2

私は同じ問題を抱えていました、そして私はそれを行ごとのモジュールで解決しました

https://www.npmjs.com/package/line-by-line

少なくとも私にとっては、同期モードと非同期モードの両方で魅力のように機能します。

また、行が終了しないという問題は、\ nで終了しないオプションで解決できます。

{ encoding: 'utf8', skipEmptyLines: false }

ラインの同期処理:

var LineByLineReader = require('line-by-line'),
    lr = new LineByLineReader('big_file.txt');

lr.on('error', function (err) {
    // 'err' contains error object
});

lr.on('line', function (line) {
    // 'line' contains the current line without the trailing newline character.
});

lr.on('end', function () {
    // All lines are read, file is closed now.
}); 

2

Node.js v8以降を使用すると、通常の関数を非同期関数に変換する新機能があります。

util.promisify

それは素晴らしい機能です。これは、txtファイルの10000の数値を配列に解析し、数値にマージソートを使用して反転をカウントする例です。

// read from txt file
const util = require('util');
const fs = require('fs')
fs.readFileAsync = util.promisify(fs.readFile);
let result = []

const parseTxt = async (csvFile) => {
  let fields, obj
  const data = await fs.readFileAsync(csvFile)
  const str = data.toString()
  const lines = str.split('\r\n')
  // const lines = str
  console.log("lines", lines)
  // console.log("str", str)

  lines.map(line => {
    if(!line) {return null}
    result.push(Number(line))
  })
  console.log("result",result)
  return result
}
parseTxt('./count-inversion.txt').then(() => {
  console.log(mergeSort({arr: result, count: 0}))
})

1

大きなファイルを配列に読み込むには、行単位またはチャンク単位で読み込むことができます。

ごとに私の答えをここで参照してください

var fs = require('fs'),
    es = require('event-stream'),

var lines = [];

var s = fs.createReadStream('filepath')
    .pipe(es.split())
    .pipe(es.mapSync(function(line) {
        //pause the readstream
        s.pause();
        lines.push(line);
        s.resume();
    })
    .on('error', function(err) {
        console.log('Error:', err);
    })
    .on('end', function() {
        console.log('Finish reading.');
        console.log(lines);
    })
);

チャンクごとこの記事を参照してください

var offset = 0;
var chunkSize = 2048;
var chunkBuffer = new Buffer(chunkSize);
var fp = fs.openSync('filepath', 'r');
var bytesRead = 0;
while(bytesRead = fs.readSync(fp, chunkBuffer, 0, chunkSize, offset)) {
    offset += bytesRead;
    var str = chunkBuffer.slice(0, bytesRead).toString();
    var arr = str.split('\n');

    if(bytesRead = chunkSize) {
        // the last item of the arr may be not a full line, leave it to the next chunk
        offset -= arr.pop().length;
    }
    lines.push(arr);
}
console.log(lines);
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.