getline for node.js

The getline package provides two JavaScript classes, GetlineSync and GetlineAsync, for use with node.js which enables line-terminated file text or other data to be read line by line by a node.js script. They require the file to be in utf8 or ascii encoding. They have two public methods, next() and close().

The GetlineSync class uses synchronous open and read functions, which won't block on a file on a regular file system, and is useful for such things as reading configuration files on setting up a program. For other uses, GetlineAsync will usually fit better into node.js's event based approach to program design, and is particularly important where reading might block the event loop because the file is, say, a file on a networked file system. The constructor will throw an exception if the file name passed in doesn't exist or cannot be opened for some other reason.

The class's next() method will cause a line of the file to be returned as a string (for which purpose it is assumed that the file is in utf8/ascii encoding). Further calls to next() will advance through the file, line by line. The terminating '\n' character is omitted. The string "EOF" will be thrown by next() after end-of-file has been reached and the last line has been extracted, so that iteration can stop.

The GetlineAsync class is an asynchronous version of GetlineSync, and often fits better into node.js's event based approach to program design than GetlineSync. The constructor of this class takes a file descriptor, not a filename, otherwise a user may be presented with a partially constructed object if implemented with asynchronous fs.open(). Use fs.open()/fs.openSync() to obtain a file descriptor to pass in, as appropriate to the usage case.

As in the case of GetlineSync, the file concerned must be in utf8 or ascii encoding.

Like GetlineSync, GetlineAsync has a next() method to iterate through the file line by line. However, next() does not return anything. Instead, GetlineAsync derives from EmitterEvent, and emits "line" following a call to next(), once the line has been fully received and assembled. The "line" event callback is passed three arguments: first an Error value (which will be null if there is no Error), secondly a boolean value indicating whether end-of-file has been reached, as end-of-file is not an error (in which case the third argument can be ignored, as it will be an empty string), and thirdly a string containing the line extracted, without terminating '\n'

After the initial call to the next() method of GetlineAsync, further calls to that method should only be made once the "line" event callback for the previous call has begun executing: in other words, further calls should be made within the "line" event callback function or, say, in a callback chained to asynchronous calls made by that function.

The classes can deal with DOS/windows style line endings ('\r\n') as well as unix style endings ('\n').

The underlying file descriptor of these classes will be closed and the buffer released on end-of-file being reached or an error arising. In addition, to release other resources, any "line" event callbacks will automatically be removed from a GetlineAsync object once relevant emissions have been made informing listeners of end-of-file or an error. If the user finishes iterating before either of those events, the classes' close() method can be called to achieve the same result. The only public methods that the classes have are next() and close().

The package can be obtained with 'npm install getline', and the tarball of the latest version is also available here: getline-1.0.2.tar.gz

The following executable demonstration script for the use of these classes is included in the package:

// put a copy of getline.js in the same directory as this file

var Getline = require("./getline.js");
var fs = require("fs");

console.log("First, use GetlineSync to iterate through this file\n");

// read our own demonstration file
var gl = new Getline.GetlineSync(__filename);
try {
    for (var count = 1;; ++count) {
	console.log(count + ":\t" + gl.next());
    }
}
catch (e) {
    if (e === "EOF") console.log("End of file reached");
    else console.log(e.toString());
}

console.log("\nNow, use GetlineAsync\n");

fs.open(__filename, "r", 0644,
	function (err, fd) {
	    if (err) console.log(err.toString());
	    else {
		var count = 1;
		var gl = new Getline.GetlineAsync(fd);
		gl.on("line", function (err, eof, line) {
		    if (err) console.log(err.toString());
		    else if (eof) console.log("End of file reached");
		    else {
			console.log(count + ":\t" + line);
			++count;
			gl.next(); // carry on stepping through file
		    }
		});
		gl.next(); // kick it off
	    }
	});