Search code examples
javascriptpythonnode.jsreadlineseek

How to translate this Python code to Node.js


I got a very nice answer on here about how to clear a line / delete a line in a file without having to truncate the file or replace the file with a new version of the file, here's the Python code:

#!/usr/bin/env python

import re,os,sys
logfile = sys.argv[1]
regex = sys.argv[2]

pattern = re.compile(regex)

with open(logfile,"r+") as f:
    while True:
        old_offset = f.tell()
        l = f.readline()
        if not l:
            break
        if pattern.search(l):
            # match: blank the line
            new_offset = f.tell()
            if old_offset > len(os.linesep):
                old_offset-=len(os.linesep)
            f.seek(old_offset)
            f.write(" "*(new_offset-old_offset-len(os.linesep)))

this script can be called like:

./clear-line.py <file> <pattern>

For educational purposes, I am trying to figure out if I can write this in Node.js. I can certainly read a file with Node.js line-by-line. But I am not sure if Node.js has the equivalent calls for tell/seek in this case.

the equivalent for write is surely

https://nodejs.org/api/fs.html#fs_fs_write_fd_buffer_offset_length_position_callback

Here is my attempt

#!/usr/bin/env node

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

const file = process.argv[2];
const rgx = process.argv[3];

const fd = fs.openSync(file, 'r+');

const rl = readline.createInterface({
    input: fs.createReadStream(null, {fd: fd})
});

let position = 0;

const onLine = line => {

    position += line.length;

    if (String(line).match(rgx)) {

        let len = line.length;

        rl.close();
        rl.removeListener('line', onLine);

        // output the line that will be replaced/removed
        process.stdout.write(line);

        fs.write(fd, new Array(len + 1).join(' '), position, 'utf8', err => {
            if (err) {
                process.stderr.write(err.stack || err);
                process.exit(1);
            }
            else {
                process.exit(0);
            }

        });

    }

};

rl.on('line', onLine);

It's not quite right - I don't think I am calculating the offset/position correctly. Perhaps someone who know both Python and Node can help me out. I am not very familiar with calculating position/offset in files, especially in terms of buffers.

Here is the data in a text file that I am working with. All I want to do is read the first line that is not empty, and then remove that line from the file and write that line to stdout.

This could really any non-whitespace data, but here is the JSON that I am working with:

{"dateCreated":"2016-12-26T09:52:03.250Z","pid":5371,"count":0,"uid":"7133d123-e6b8-4109-902b-7a90ade7c655","isRead":false,"line":"foo bar baz"}
{"dateCreated":"2016-12-26T09:52:03.290Z","pid":5371,"count":1,"uid":"e881b0a9-8c28-42bb-8a9d-8109587777d0","isRead":false,"line":"foo bar baz"}
{"dateCreated":"2016-12-26T09:52:03.390Z","pid":5371,"count":2,"uid":"065e51ff-14b8-4454-9ae5-b85152cfcb64","isRead":false,"line":"foo bar baz"}
{"dateCreated":"2016-12-26T09:52:03.491Z","pid":5371,"count":3,"uid":"5af80a95-ff9d-4252-9c4e-0e421fd9320f","isRead":false,"line":"foo bar baz"}
{"dateCreated":"2016-12-26T09:52:03.595Z","pid":5371,"count":4,"uid":"961e578f-288b-413c-b933-b791f833c037","isRead":false,"line":"foo bar baz"}
{"dateCreated":"2016-12-26T09:52:03.696Z","pid":5371,"count":5,"uid":"a65cbf78-2ea1-4c3a-9beb-b4bf56e83a6b","isRead":false,"line":"foo bar baz"}
{"dateCreated":"2016-12-26T09:52:03.799Z","pid":5371,"count":6,"uid":"d411e917-ad25-455f-9449-ae4d31c7b1ad","isRead":false,"line":"foo bar baz"}
{"dateCreated":"2016-12-26T09:52:03.898Z","pid":5371,"count":7,"uid":"46f8841d-c86c-43f2-b440-8ab7feea7527","isRead":false,"line":"foo bar baz"}
{"dateCreated":"2016-12-26T09:52:04.002Z","pid":5371,"count":8,"uid":"81b5ce7e-2f4d-4acb-884c-442c5ac4490f","isRead":false,"line":"foo bar baz"}
{"dateCreated":"2016-12-26T09:52:04.101Z","pid":5371,"count":9,"uid":"120ff45d-74e7-464e-abd5-94c41e3cd089","isRead":false,"line":"foo bar baz"}

Solution

  • Ok, I think I got it, but if someone has any beef with this please feel free to critique. It's close, but it needs some fine tuning I think, there seems to be an off-by-one error or something like that.

    #!/usr/bin/env node
    
    const readline = require('readline');
    const fs = require('fs');
    
    const file = process.argv[2];
    const rgx = new RegExp(process.argv[3]);
    
    const fd = fs.openSync(file, 'r+');
    
    const rl = readline.createInterface({
        input: fs.createReadStream(null, {fd: fd})
    });
    
    let position = 0;
    
    const onLine = line => {
    
        if (String(line).match(rgx)) {
    
            let len = line.length;
    
            rl.close();
            rl.removeListener('line', onLine);
    
            // output the line that will be replaced/removed
            process.stdout.write(line + '\n');
    
            fs.write(fd, new Array(len + 1).join(' '), position, 'utf8',
    
                (err, written, string) => {
    
                if (err) {
                    process.stderr.write(err.stack || err);
                    return process.exit(1);
                }
                else {
                    process.exit(0);
                }
    
            });
    
        }
    
       position += (line.length + 1);  // 1 is length of \n character
    
    };
    
    rl.on('line', onLine);