4

I have a "simple" problem that I cant seem to get around. Im just going to read input from standard input (terminal) and then save the input to a variable (here an array) and then print it all out to the terminal. When I try to paste in more than 55 lines of text, the program just print out the first 55 lines (of my input) and then exit. I cant understand why it stops and how to fix this.

My code is:

let readline = require('readline');
let rl = readline.createInterface({
    input: process.stdin,
    terminal: true
})

let inputLines = []

const pushLinesToArray = function (line) {
    inputLines.push(line)
  }

const readOneLine = function () {
    return new Promise((resolve, reject) => {
        rl.on('line', resolve)  
        }).then(rl.on('line', pushLinesToArray))
}

const start = async () => {

    // Save all input to inputLines array
    await readOneLine()

    // Print inputLines array to terminal/console
    inputLines.forEach((line) => console.log(line))
    process.exit()
}

start()

How to get around this problem? I should also mention that when I change the function (readOneLine) above to the code down below it works IF I close the program with ctrl+C (Mac OSX). This makes me think It doesn't have anything with maxLineLength or memory buffer to do. The only thing I changed down below is close instead of line the first rl.on

const readOneLine = function () {
    return new Promise((resolve, reject) => {
        rl.on('close', resolve)  
        }).then(rl.on('line', pushLinesToArray))
}

I should also mention that I'm going to process the input a bit before I actually print it out. In the code above I just took all that away because I realize there was a problem already with the reading of the input. i.e I want to read input of more than 55 lines of text. Then save the text to an array (line by line). Then do my other code (to calculate how much each person has in debt). And lastly print it out.

Example input will be like this: Note that there is new line after line 12

When I copy --> paste this as input (and mark every line (1-13)) this work. If I, on the other hand, have 57 lines like this (where line nr 1-56 is with text and line nr 57 is empty) it doesn't work. Then my program exits after printing the first 55 lines with text (line nr 56 will never get printed)

Edit: I need this to work with Kattis (online site for programming problems ) and the only thing I know is they are running my code with the above mention flags: -c {files}. I need to modify my code so it can handles different inputs of everything between 3 lines and up to 100 000 lines. The input will always come in a chunk (a string with all the lines copy--> pasted into the terminal) and I need to attach listener or read this input and do something with it (calculate everything) before I print out the answer to the terminal. So I still looking for an answer to solve this input problem.

Zencha
  • 43
  • 1
  • 7

2 Answers2

1

I think the problem you are facing is because readline comes with default 4K buffer size beyond which it won't read. You will have to set maxLineLength in its options.

Refer this.

Rohini
  • 244
  • 1
  • 8
  • That should not be the problem because it I dont have long lines. Each line of my test code is less than 50 characters long. – Zencha Mar 15 '19 at 11:54
  • This `readline` is from node.js standard module. https://nodejs.org/api/readline.html#readline_readline_createinterface_options – André Werlang Mar 15 '19 at 11:55
1

The usage of promise in this case needs to be changed. Because the promise resolves in the first line read, the await in start() unblocks and gets ready to process how many lines have been read by that time. Input is being cut likely because you didn't gave enough time for read all input.

Either remove the promise and handle lines as they come:

rl.on('line', (line) => console.log(line));

Or then use the close event to resolve the promise and process all input once everything is read.

const readOneLine = function () {
    return new Promise((resolve, reject) => {
        rl.on('line', pushLinesToArray)
        rl.on('close', () => resolve(inputLines))  
    })
}

As stated in the node.js docs for the readline standard module [1], the close event executes when all available input is read or closed by the client.

As you want to also handle an empty line as the closing mark for the stream, this can be accomplished in the line event.

const readOneLine = function () {
    return new Promise((resolve, reject) => {
        rl.on('line', (line) => {
            if (line === '') {
                resolve(inputLines);
                rl.close();
                return;
            }
            pushLinesToArray(line)
        })
        rl.on('close', () => resolve(inputLines))  
    })
}

The first resolve() that gets a chance to run unblocks the await. Nothing harmful happens if the resolve() runs twice, after an empty line and a Ctrl+C.

[1] https://nodejs.org/api/readline.html#readline_event_close

André Werlang
  • 5,839
  • 1
  • 35
  • 49
  • But I need to save them to a variable to do some more logic before I actually print the lines out (I use the information on the lines and then calculate how much each person need to pay). The code below is just the version where I took all other code away to ask a simpler question. This means that I need to have it asynchronously so I read and save all input before I handle the data and then print it out. – Zencha Mar 15 '19 at 12:07
  • After the last character on the last line there will be an New line (Like when someone is hitting "Enter"). Sorry for bad explanation – Zencha Mar 15 '19 at 12:15
  • @Zencha, also, attach the `line` event before the `close` event. – André Werlang Mar 15 '19 at 12:15
  • @Zencha I guess you mean an empty line. Instead of resolving in the close event, check whether an empty line was read in the `line` event then resolve the promise at that point. – André Werlang Mar 15 '19 at 12:17
  • @Zencha welcome to stack overflow btw. You'll learn how to use it better, like giving all important details when first posting the question. This avoids useless attempts at answering the question (like the other answer here). – André Werlang Mar 15 '19 at 12:19
  • I tried your code above but the thing is the program wont continue if I dont manually hit ctrl+C (I guess to activate the `close` event) And I want it to continue even when I dont actually close it manually. – Zencha Mar 15 '19 at 12:20
  • Thanks for welcoming me :) Im a newbie both here and on javascript (and node.js) So I don't really understand how to write the code where I "...check whether an empty line was read in the line event then resolve the promise at that point." Could you please write it? Much thanks in advance! – Zencha Mar 15 '19 at 12:23
  • Thanks a lot for your effort! Unfortunately it still doesn't work. I updated my question with clearer information on how the input is. I can't understand why the `rl.on('line', (line) => { if (line === '') { resolve(inputLines); return;` never fires. – Zencha Mar 15 '19 at 14:24
  • @Zencha maybe `if (line.trim() === '') {...}` – André Werlang Mar 15 '19 at 14:29
  • unfortunately still not working. I need to manually press enter to get your code to work. Or hit crtl+C to close. Do you think I need streams instead? I have no experience setting that up though. – Zencha Mar 15 '19 at 14:40
  • @Zencha not sure why that happens. I updated the code to close the readline once it's done. This is better than exiting the process, it will exit naturally. – André Werlang Mar 15 '19 at 15:50
  • Have you tried to copy ---> paste a few lines of text to see what happens? When I use your code I, unfortunately, need to press enter one extra time to fire the `close` event. Is there any other way than using readline to handle this problem? I will still have standard input from terminal. – Zencha Mar 15 '19 at 16:29
  • @Zencha I took your code, replace with my `readOneLine`, and it exits automatically running it like `type input.txt | node index` – André Werlang Mar 15 '19 at 16:55
  • sorry my little experience with this, but what exactly are you typing in to run it? Did you call the file index.js and typed this in the terminal to run it: `type input.txt | node index.js`? I couldn't manage to make it work. To be perfectly clear about my problem; the problem is form a Kattis test and when the run the code they just run it like this: "For JavaScript (Node.js), we use Node.js version v8.15.0 with the following flags: -c {files}." – Zencha Mar 15 '19 at 18:15
  • @Zencha yes, that's what I did. Consider it might be an issue with this Kattis thing, run on your local environment, I believe it should work. – André Werlang Mar 15 '19 at 21:47
  • Thanks for all energy you put into trying to help me! Unfortunately I need this to work with [Kattis](https://www.kattis.com/) (online site for programming problems ) and the only thing I know is they are running my code with the above mention flags: -c {files}. I need to modify my code so it can handles different inputs of everything between 3 lines and up to 100 000 lines. – Zencha Mar 16 '19 at 14:43
  • The input will always come in a chunk (a string with all the lines copy--> pasted into the terminal) and I need to attach listener or read this input and do something with it (calculate everything) before I print out the answer to the terminal. So I still looking for an answer to solve this input problem. – Zencha Mar 16 '19 at 14:43