카테고리 없음

[node.js] 텍스트 파일로부터 /dev/stdin 입력 받기

노새두마리 2024. 1. 2. 21:41

 

요약

command < input.txt > output.txt

명령어 뒤에 < 파일명을 붙이면 해당 파일로부터 내용을 가져와 입력하며, > 파일명을 붙이면 프로그램 수행중에 출력되는 내용들을 기록합니다.

터미널이 제공하는 리다이렉션 기능이라고 합니다.


node.js 입력 받기(linux)

BOJ에 코드를 제출할 때에 fs 모듈의 readFileSync를 사용한다면 아래와 같은 형식으로 작성하여 제출하게 됩니다.

const input = require('fs').readFileSync('/dev/stdin').toString().trim();

console.log(input);

 

저는 주로 로컬 환경보다 JDoodle이라는 온라인 IDE를 활용해 코드를 작성했었는데, 그 이유는 로컬 환경에서 테스트케이스를 받아오는 게 매끄럽지 않았기 때문입니다. 

만약 로컬 환경에서 '/dev/stdin'으로 그대로 입력을 받으면 아래와 같이 되었습니다.

Ctrl + C를 누르면 그대로 프로그램이 종료되었죠.

키 상호작용 관련한 부분을 조작해서 Ctrl + C를 눌렀을 때 종료되는 대신 입력만 중단되도록 하는 방법도 있다는 것 같지만 다음의 사유로 시도하지 않았습니다.

  • 입력할 수 있게 되더라도 모든 테스트 케이스를 CLI에 직접 입력해야 한다는 점
  • 입력을 완료한 뒤에 수동으로 종료해야 한다는 점(실수할 수 있음)

 

한동안 로컬에서 작업해야 하는 경우 미봉책으로써 readFileSync의 파일 경로를 수정하여 input.txt로부터 받아오도록 하는 방법을 사용하였습니다.

const input = require('fs').readFileSync('./input.txt').toString().trim();

이제 로컬에서 테스트 케이스를 입력받을 수 있게 되었습니다. 하지만, 코드를 제출할 때마다 파일 경로를 수정해야 하는 번거로움이 남았습니다. 파일 경로를 수정하는 것을 잊어버리면 이렇게 되었습니다. 

유효한 파일명 또는 경로명이 아니어서 발생하는 오류입니다.


번외: 윈도우 환경에서 입력 받기

윈도우 환경은 처음부터 /dev/stdin을 사용할 수 없기 때문에 현재 실행중인 플랫폼(process.platform)이 win32인지 linux인지 여부에 따라 파일 경로를 다르게 적용하신 경우도 있었습니다.

const filePath = process.platform === 'win32' ? './input.txt' : '/dev/stdin';
const input = require('fs').readFileSync(filePath).toString().trim();

https://velog.io/@rookieand/Node.js-%EC%BD%94%EB%94%A9-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%9E%85%EB%A0%A5


터미널의 도움 받기

linux 환경에서 '/dev/stdin' 대신 'input.txt' 경로를 지정할 필요가 없었습니다. 그것은 왜인가 하면...

 

터미널은 파일을 읽어 와서 입력해 주는 기능을 가지고 있기 때문이죠.

node test.cjs < input.txt

const input = require('fs').readFileSync('/dev/stdin').toString().trim().split('\n');

for (let line of input){
  console.log(line);
}

위의 방식으로 입력을 받아오니 무한으로 입력받는 현상없이 깔끔하게 input.txt의 내용만을 가지고 프로그램을 수행합니다. 이로써 온라인 IDE 보다 다루기 편해졌습니다. 실행 커맨드만 따로 단축 명령어로 등록해도 좋겠네요.


파일 입력만이 끝이 아니다!

하지만 여기서 끝이 아닙니다. '< 파일명'으로 입력을 받아올 수 있었다면 '> 파일명'으로 프로그램 수행중에 출력되는 내용들을 새롭게 쓸 수도 있습니다.

개인적으로 vs code 안의 터미널에 출력해 놓으면 스크롤 간격이 너무 커서 보기에 불편한데 txt 파일로 내보내서 보니 훨씬 편합니다.

node test.cjs > debug.txt
const fib = [0, 1];

for (let i = 2; i <= 50; i++){
  fib.push(fib[i-1] + fib[i-2]);
}

for (let i = 0; i <= 50; i++){
  console.log(fib[i]);
}


리다이렉션 여러 번 사용 가능 여부

const input1 = require('fs').readFileSync('/dev/stdin').toString().trim().split('\n');
console.log(input1);

const input2 = require('fs').readFileSync('/dev/stdin').toString().trim().split('\n');
console.log(input2);

< 파일명 여러 번 사용 시 첫 input에 전부 입력되는 것을 확인

> 파일명 여러 번 기록 시 동일한 내용으로 여러 번 기록되는 것을 확인

서로 다른 파일에서 데이터를 가져올 필요가 있다면 파일 경로를 명시합시다.


/dev/stdin 대신 0 사용하기

https://www.acmicpc.net/board/view/137718

백준 온라인 저지 일부 문제는 파일 접근 권한이 없어서 런타임 에러가 발생하기도 합니다. 이와 같은 특수한 상황에서는 '/dev/stdin' 대신 0을 사용해야 할 수도 있습니다. 0은 표준입력을 나타내는 파일 디스크립터로써, 0을 읽어오게 되면 '/dev/stdin'과 마찬가지로 표준입력을 사용하게 됩니다.

const input = require('fs').readFileSync(0).toString().trim().split('\n');

// 'utf-8' 인코딩 옵션을 넣으면 파일을 읽어올 때에 문자열로 변환되므로 toString 생략 가능
// toString은 생략하되 trim은 반드시 해줄 것
const input = require('fs').readFileSync(0, 'utf-8').toString().trim().split('\n');