2011-09-25 137 views
8

我有一个大文件(utf8)。我知道fs.createReadStream可以创建流来读取大文件,但不能同步。所以我尝试使用fs.readSync,但是读取文本像"迈�"一样被破坏。nodejs同步逐行读取大文件?

var fs = require('fs'); 
var util = require('util'); 
var textPath = __dirname + '/people-daily.txt'; 
var fd = fs.openSync(textPath, "r"); 
var text = fs.readSync(fd, 4, 0, "utf8"); 
console.log(util.inspect(text, true, null)); 
+0

你确定该文件是用utf8编码的吗? – thejh

+0

是的,Unicode(UTF-8)和Unix(LF) – nroe

+0

我认为你的意思是“同步”而不是“同步”。 – hippietrail

回答

4

readFileSync使用: fs.readFile的

fs.readFileSync(文件名,[编码])同步版本。返回文件名的内容。

如果指定了encoding,那么这个函数返回一个字符串。 否则它返回一个缓冲区。

在附注中,由于您使用的是节点,因此我建议使用异步功能。

+2

文件很大,我认为fs.readFileSync是不是一个好主意。 – nroe

+1

@nroe,那么你为什么要求同步读取?当然,这不适用于大文件。 – Tom

+2

@Tom,nroe可能希望能够接收这些行作为读取调用的返回值。一些代码可以在[本博客文章]中找到(http://blog.jaeckel.com/2010/03/i-tried-to-find-example-on-using-node.html)(不是我的)。 –

1

两个潜在的问题,

  1. 开头3字节BOM你没有跳过
  2. 第4字节不能很好地格式化为UTF8的字符(UTF8是不固定长度)
9

对于大文件,readFileSync可能不方便,因为它将整个文件加载到内存中。一种不同的同步方法是迭代地调用readSync,一次读取少量的数据,并且在它们到来时处理这些行。下面的代码位实现了这一做法,并同步处理的一行从文件“的test.txt”时间:

var fs = require('fs'); 
var filename = 'test.txt' 

var fd = fs.openSync(filename, 'r'); 
var bufferSize = 1024; 
var buffer = new Buffer(bufferSize); 

var leftOver = ''; 
var read, line, idxStart, idx; 
while ((read = fs.readSync(fd, buffer, 0, bufferSize, null)) !== 0) { 
    leftOver += buffer.toString('utf8', 0, read); 
    idxStart = 0 
    while ((idx = leftOver.indexOf("\n", idxStart)) !== -1) { 
    line = leftOver.substring(idxStart, idx); 
    console.log("one line read: " + line); 
    idxStart = idx + 1; 
    } 
    leftOver = leftOver.substring(idxStart); 
} 
+0

太好了,谢谢! – Benvorth

7

使用https://github.com/nacholibre/node-readlines

var lineByLine = require('n-readlines'); 
var liner = new lineByLine('./textFile.txt'); 

var line; 
var lineNumber = 0; 
while (line = liner.next()) { 
    console.log('Line ' + lineNumber + ': ' + line.toString('ascii')); 
    lineNumber++; 
} 

console.log('end of line reached'); 
2

我建了一个简单的版本JB科恩的回答在缓冲区上使用split()。它适用于我尝试的较大文件。

/* 
* Synchronously call fn(text, lineNum) on each line read from file descriptor fd. 
*/ 
function forEachLine (fd, fn) { 
    var bufSize = 64 * 1024; 
    var buf = new Buffer(bufSize); 
    var leftOver = ''; 
    var lineNum = 0; 
    var lines, n; 

    while ((n = fs.readSync(fd, buf, 0, bufSize, null)) !== 0) { 
     lines = buf.toString('utf8', 0 , n).split('\n'); 
     lines[0] = leftOver+lines[0];  // add leftover string from previous read 
     while (lines.length > 1) {   // process all but the last line 
      fn(lines.shift(), lineNum); 
      lineNum++; 
     } 
     leftOver = lines.shift();   // save last line fragment (may be '') 
    } 
    if (leftOver) {       // process any remaining line 
     fn(leftOver, lineNum); 
    } 
} 
+0

也许可以改变'split('\ n')'为split(/ \ r?\ n /)'来支持可能的窗口行尾? – rob3c