2016-09-13 105 views
5

我拥有的最小文件大于850k行,每行的长度不明。目标是在浏览器中从此文件读取n行。完全阅读不会发生。阅读大文本文件的n行

下面是HTML <input type="file" name="file" id="file">和JS我有:

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

显然,这里的问题是,它会首先尝试真正的整个文件,然后换行分裂它。所以无论是n,它都会尝试来读取整个文件,并且当文件很大时最终什么都不读。

我应该怎么做?

注意:我愿意删除整个函数并从头开始,因为我可以每行都读console.log()


* “每一道线条都是未知长度的” - >表示该文件是这样的:

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

编辑:

去会是这样的方式像filereader api on big files,但我看不到我如何修改该文件的n行...

通过使用Uint8Array to string in Javascript也可以从那里做:

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

,但这可能无法读取最后一行作为一个整体,所以你怎么后来确定线路?例如这里是它印:

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

*“......但是这不应该的问题” *什么在天堂的名字使你认为不要紧?如果没有索引行的开始位置*和*在给定索引处递增读取文件的能力,那绝对是重要的。 –

+0

@ T.J.Crowder我通过澄清更新了我的问题,也许我应该删除该陈述,您是对的! – gsamaras

+0

这里需要更多的上下文。您正在使用HTML和JavaScript。这是在Web浏览器中运行的JavaScript吗?或者,这个JavaScript是作为HTML POST之类的回应执行的吗? – 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块不存在。然后,它应该很容易看出,只有当它已经读过文件末尾('offset> = file.size')时'seek'才会返回。 –

2

流是功能!
whatwg团队正在研究关于可写流+可读流的最新流量,并且很快就绪。但在此之前,您可以使用web-stream-polyfill。 他们正在努力获得blob的ReadableStream以及[1]。但我还创建了一个方法与获得以流方式将BLOB已经:Screw-FileReader

昨天我还创建了一个simpel的node-bylineport与网络的工作流,而不是

所以这可能是因为这很简单:

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

有趣的做法,neadLess说! :) – gsamaras

+0

谢谢,期待的功能:) – Endless

+1

请不要鼓励使用'innerHTML'与外部输入,因为它可能会引入安全漏洞。另外'document.body.innerHTML + ='不好,因为它强制重新整理整个文档。考虑使用'element.insertAdjacentText'或'document.createTextNode' +'element.appendChild'来代替。 –