백준(BOJ) node.js 문제 풀이법
백준에서 Node.js로 입력을 받는 방법은 크게 두 가지가 있다.
- readline 모듈
- fs 모듈
- readline보다 fs모듈을 사용하는 것이 더 빠르다.
var fs = require('fs');
var input = fs.readFileSync('/dev/stdin').toString().split(' ');
var a = parseInt(input[0]);
var b = parseInt(input[1]);
console.log(a+b);
위 코드는 백준 언어 도움말에서 제공하는 코드다.
fs모듈(FileSystem Module)
파일 처리를 하는 모듈로, 직접 입력 파일을 읽어와서 처리한다. 위 코드는 입력값 전체를 하나의 문자열로 만들어 split 메소드로 배열로 만들어 그 안의 요소를 가져다 쓰는 방식으로 입력을 받는다. 하지만 백준에서 제공하는 위 코드를 사용했는데도 에러가 발생하는 경우가 잦다.
입력 값 받아오기
입력 값을 받아올 때 나는 IDE에서 동일 한 폴더에 input.txt라는 입력 값 파일을 만들어 문제풀이를 해보고 결과를 옮겨 적는 과정을 통해 풀어서 아래와 같이 fs모듈을 사용해서 풀이했다.
/* IDE에서 작성 한 입력 값 받아오는 코드 */
const fs = require('fs');
const filePath = process.platform === 'linux' ? '/dev/stdin' : '.input.txt';
let input = fs.readFileSync(__dirname + '/input.txt');
/* 백준에 제출하는 입력 값 받아오는 코드 */
const fs = require('fs');
const filePath = process.platform === 'linux' ? '/dev/stdin' : '.input.txt';
let input = fs.readFileSync(filePath);
입력 값 받아오는 코드 정리
참고한 블로그에서 상황별로 입력 값을 받아오는 코드를 정리한 것을 가져왔다.
// 1. 하나의 값을 입력받을 때
const input = require('fs').readFileSync(filePath).toString().trim();
// 2. 공백으로 구분된 한 줄의 값들을 입력받을 때
const input = require('fs').readFileSync(filePath).toString().trim().split(' ');
// 3. 여러 줄의 값들을 입력받을 때
const input = require('fs').readFileSync(filePath).toString().trim().split('\n');
// 4. 첫 번째 줄에 자연수 n을 입력받고, 그 다음줄에 공백으로 구분된 n개의 값들을 입력받을 때
const [n, ...arr] = require('fs').readFileSync(filePath).toString().trim().split(/\s/);
// 5. 첫 번째 줄에 자연수 n을 입력받고, 그 다음줄부터 n개의 줄에 걸쳐 한 줄에 하나의 값을 입력받을 때
const [n, ...arr] = require('fs').readFileSync(filePath).toString().trim().split('\n');
// 6. 하나의 값 또는 공백으로 구분된 여러 값들을 여러 줄에 걸쳐 뒤죽박죽 섞여서 입력받을 때
// ex) n 입력 - 공백으로 구분된 n개의 값 입력 - m 입력 - 여러 줄에 걸쳐 m개의 값 입력
const input = require('fs').readFileSync(filePath).toString().trim().split(/\s/);
const n = input[0];
const n_arr = input.slice(1, n+1);
const [m, ...m_arr] = input.slice(n+1);
// 2~6에서 입력받는 값들을 모두 String에서 Number로 바꾸려면 split()뒤에 .map(Number)를 추가
의문점
위 블로그에서는 몇 가지 의문점도 제시했다.
문자열 값을 입력받는 것이라면 toString()을 안 붙여도 되지 않나?
require("fs").readFileSync("/dev/stdin")의 반환값은 문자열이 아닌 Buffer 객체다. readFileSync의 인수로 인코딩을 지정해주지 않으면 Buffer 객체를 반환한다. 따라서 문자열로 바꾸어주지 않으면 예기치 못한 오류가 날 수 있다. 문자열로 바꾸기 위해서는 위의 코드처럼 toString() 메소드 또는 문자열 연결 연산을 통해 Buffer 객체를 문자열로 바꾸거나, readFileSync의 두 번째 인수로 인코딩을 지정해주면 된다.
const input = require('fs').readFileSync('/dev/stdin');
console.log(typeof input); // object
const input2 = require('fs').readFileSync('/dev/stdin').toString();
console.log(typeof input2); // string
const input3 = require('fs').readFileSync('/dev/stdin')+'';
console.log(typeof input3); // string
const input4 = require('fs').readFileSync('/dev/stdin', 'utf8');
console.log(typeof input4); // string
trim()은 왜 쓰는 건가?
일부 입력값의 마지막에 개행문자가 포함된 경우가 종종 있다. 이런 경우 split("\n")할 경우 공백문자 하나를 더 갖는 배열을 반환한다. 이를 방지하기 위해서 trim()을 사용한다.
const text = '가\n나\n다\n';
console.log(text.split('\n')); // ['가', '나', '다', '']
console.log(text.trim().split('\n')); // ['가', '나', '다']
4번이나 6번을 쓰면 속도가 너무 느린 것 같아요.
split('/\s/')는 속도가 느리다. 입력값이 엄청 크거나, 수행 시간을 단축하고 싶다면 split('/\s/')를 사용하지 말고 split('\n')로 한 줄 씩 나눈 뒤, 한 줄을 다시 한 번 더 나누는 방식으로 입력을 받아야 한다
백준 문제풀이는 입력 값 받아오는 것 부터 까다로운 것 같다. 난 그래서 프로그래머스를 단계별로 풀어보고 있다..
북마크 정리도 할 겸, 누군가에게 이 글이 도움이 되길!