2016-09-13 7 views
5

私が持っている最小のファイルは> 850k行で、すべての行の長さは不明です。目標は、ブラウザでこのファイルからn行を読み込むことです。完全にそれを読むことは起こりません。大きなテキストファイルのn行を読む

var n = 10; 
var reader = new FileReader(); 
reader.onload = function(progressEvent) { 
    // Entire file 
    console.log(this.result); 

    // By lines 
    var lines = this.result.split('\n'); 
    for (var line = 0; line < n; line++) { 
    console.log(lines[line]); 
    } 
}; 

もちろん、ここでの問題は、それが最初の本当のファイル全体を試み、次に改行でそれを分割することです:

は、ここで私が持っているHTML <input type="file" name="file" id="file">とJSです。したがって、nの場合でも、を試してみると、ファイル全体が読み取られ、最終的にはファイルが大きくなると何も読み取られません。

どうすればよいですか?

注:私が読んでいるすべての行をconsole.log()にすることができれば、関数全体を削除して最初から開始したいと思います。


* "すべての行が未知の長さである" - >は、ファイルは、このようなものであることを意味します

(0, (1, 2)) 
(1, (4, 5, 6)) 
(2, (7)) 
(3, (8)) 

編集:どこへ行く

やり方ものになるだろうfilereader api on big filesのように、私はファイルのn行を読むためにそれを修正する方法を見ることができません...

var view = new Uint8Array(fr.result); 
var string = new TextDecoder("utf-8").decode(view); 
console.log("Chunk " + string); 

をが、これは、全体としての最後の行を読まない場合がありますので、どのように後から行を決定しようとしている:あまりにUint8Array to string in Javascriptを使用することによって、そこから行うことができますか?例えば、ここでそれが印刷されたものです:

((7202), (u'11330875493', u'2554375661')) 
((1667), (u'9079074735', u'6883914476', 
+0

*「...しかし、それは問題ではないはず。」*天国の名前であなたはそれが問題ではありませんと思わせる何か?行がどこから始まるかのインデックスがなく、与えられたインデックスでファイルをインクリメンタルに読み取る能力がなければ、絶対に重要です。 –

+0

@ T.J.Crowder私は明確化して私の質問を更新しました、おそらく私はその声明を削除する必要があります、あなたは正しいです! – gsamaras

+0

もう少し文脈が必要です。あなたはHTMLとJavaScriptを使用しています。このJavaScriptはウェブブラウザで動作しますか?または、HTML POSTのようなものからの応答として実行されるこのJavaScriptですか? – Alan

答えて

7

あなたがこれまでに(とも処理した行数を追跡する必要がある以外ロジックは、私がfilereader api on big filesに私の答えに書いたものと非常によく似ていますこれまでに読み込まれた最後の行はまだ終了していない可能性があるため)。次の例は、UTF-8と互換性のあるすべてのエンコーディングで動作します。 TextDecoderコンストラクタのオプションを別のエンコーディングにする必要がある場合。

入力がASCII(または他のシングルバイトエンコーディング)であることが確かである場合は、TextDecoderの使用をスキップして、FileReader's readAsText methodを使用して入力を直接テキストとして読み取ることもできます。

// This is just an example of the function below. 
 
document.getElementById('start').onclick = function() { 
 
    var file = document.getElementById('infile').files[0]; 
 
    if (!file) { 
 
     console.log('No file selected.'); 
 
     return; 
 
    } 
 
    var maxlines = parseInt(document.getElementById('maxlines').value, 10); 
 
    var lineno = 1; 
 
    // readSomeLines is defined below. 
 
    readSomeLines(file, maxlines, function(line) { 
 
     console.log("Line: " + (lineno++) + line); 
 
    }, function onComplete() { 
 
     console.log('Read all lines'); 
 
    }); 
 
}; 
 

 
/** 
 
* Read up to and including |maxlines| lines from |file|. 
 
* 
 
* @param {Blob} file - The file to be read. 
 
* @param {integer} maxlines - The maximum number of lines to read. 
 
* @param {function(string)} forEachLine - Called for each line. 
 
* @param {function(error)} onComplete - Called when the end of the file 
 
*  is reached or when |maxlines| lines have been read. 
 
*/ 
 
function readSomeLines(file, maxlines, forEachLine, onComplete) { 
 
    var CHUNK_SIZE = 50000; // 50kb, arbitrarily chosen. 
 
    var decoder = new TextDecoder(); 
 
    var offset = 0; 
 
    var linecount = 0; 
 
    var linenumber = 0; 
 
    var results = ''; 
 
    var fr = new FileReader(); 
 
    fr.onload = function() { 
 
     // Use stream:true in case we cut the file 
 
     // in the middle of a multi-byte character 
 
     results += decoder.decode(fr.result, {stream: true}); 
 
     var lines = results.split('\n'); 
 
     results = lines.pop(); // In case the line did not end yet. 
 
     linecount += lines.length; 
 
    
 
     if (linecount > maxlines) { 
 
      // Read too many lines? Truncate the results. 
 
      lines.length -= linecount - maxlines; 
 
      linecount = maxlines; 
 
     } 
 
    
 
     for (var i = 0; i < lines.length; ++i) { 
 
      forEachLine(lines[i] + '\n'); 
 
     } 
 
     offset += CHUNK_SIZE; 
 
     seek(); 
 
    }; 
 
    fr.onerror = function() { 
 
     onComplete(fr.error); 
 
    }; 
 
    seek(); 
 
    
 
    function seek() { 
 
     if (linecount === maxlines) { 
 
      // We found enough lines. 
 
      onComplete(); // Done. 
 
      return; 
 
     } 
 
     if (offset !== 0 && offset >= file.size) { 
 
      // We did not find all lines, but there are no more lines. 
 
      forEachLine(results); // This is from lines.pop(), before. 
 
      onComplete(); // Done 
 
      return; 
 
     } 
 
     var slice = file.slice(offset, offset + CHUNK_SIZE); 
 
     fr.readAsArrayBuffer(slice); 
 
    } 
 
}
Read <input type="number" id="maxlines"> lines from 
 
<input type="file" id="infile">. 
 
<input type="button" id="start" value="Print lines to console">

+0

私は 'maxlines'がユーザによって提供されていないとき、これがファイル全体をどのように読むのか実際にはわかりません。それ以外は、素晴らしい! – gsamaras

+1

@gsamaras 'maxlines'を任意に高い値(例えば' Infinity')に設定すると、 'maxlines'を含むすべての条件はfalseと評価されます。したがって、それらを含む' if'ブロックは存在しないと想像することができます。それから、 'seek>はファイルの終わりを過ぎて読み込んだときにだけ返ることも分かります(' offset> = file.size')。 –

2

ストリームは機能です!
whatwgチームは書き込み可能な+可読ストリームに関する最後の流れを整えており、すぐに準備ができています。しかしそれまではweb-stream-polyfillがあります。 彼らはBLOBからReadableStreamを得る方法にも取り組んでいます。[1]。しかし、私はまたしてすでにストリーミング方式でブロブを取得する方法を作成しました:私はまた、ウェブで動作するようにnode-bylineのsimpel portを作成昨日Screw-FileReader

代わり

ストリームだから、これはこのように非常に簡単かもしれません:

// Simulate a file 
 
var csv = 
 
`apple,1,$1.00 
 
banana,4,$0.20 
 
orange,3,$0.79` 
 

 
var file = new Blob([csv]) 
 

 
var n = 0 
 
var controller 
 
var decoder = new TextDecoder 
 
var stdout = new WritableStream({ 
 
    start(c) { 
 
     controller = c 
 
    }, 
 
    write(chunk, a) { 
 
     // Calling controller.error will also put the byLine in an errored state 
 
     // Causing the file stream to stop reading more data also 
 
     if (n == 1) controller.error("don't need more lines") 
 
     chunk = decoder.decode(chunk) 
 
     console.log(`chunk[${n++}]: ${chunk}`) 
 
    } 
 
}) 
 

 
file 
 
    .stream() 
 
    .pipeThrough(byLine()) 
 
    // .pipeThrough(new TextDecoder) something like this will work eventually 
 
    .pipeTo(stdout)
<script src="https://cdn.rawgit.com/creatorrr/web-streams-polyfill/master/dist/polyfill.min.js"></script> 
 
<script src="https://cdn.rawgit.com/jimmywarting/Screw-FileReader/master/index.js"></script> 
 

 
<!-- after a year or so you only need byLine --> 
 
<script src="https://cdn.rawgit.com/jimmywarting/web-byline/master/index.js"></script>

+1

面白いアプローチは、ネアレスは言う! :) – gsamaras

+0

ありがとう、機能を楽しみにしています:) – Endless

+1

セキュリティ上の脆弱性が導入される可能性があるため、外部入力で 'innerHTML'を使用することを推奨しないでください。また、 'document.body.innerHTML + ='は、文書全体の再解析を強制するので、悪いです。代わりに、 'element.insertAdjacentText'または' document.createTextNode' + 'element.appendChild'を使用することを検討してください。 –

関連する問題