백준에서 Node.js로 입력을 받는 방법은 크게 두 가지가 있다. 첫 번째는 readline 모듈을 사용하는 것이고, 두 번째는 fs 모듈을 사용하는 것이다. (이 글에서는 fs 모듈에 대해서만 다루겠다.)
Python으로 백준 문제를 풀 때 시간 초과가 나는 것을 막기 위해 input() 대신 sys.stdin.readline()을 쓰는 것처럼, Node.js도 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);
위 코드는 백준 언어 도움말에서 제공하는 코드다.
FileSystem의 약자인 fs 모듈은 파일 처리를 하는 모듈로, 직접 입력 파일을 읽어와서 처리한다. 위 코드는 입력값 전체를 하나의 문자열로 만들어 split 메서드로 배열로 만들어 그 안의 요소를 가져다 쓰는 방식으로 입력을 받는다. 하지만 백준에서 제공하는 위 코드를 사용했는데도 에러가 발생하는 경우가 잦다.
지금까지 계속 런타임에러에 시달리면서 터득한 내 나름대로의 값을 입력받는 코드들을 정리해보았다.
// 1. 하나의 값을 입력받을 때
const input = require('fs').readFileSync('/dev/stdin').toString().trim();
// 2. 공백으로 구분된 한 줄의 값들을 입력받을 때
const input = require('fs').readFileSync('/dev/stdin').toString().trim().split(' ');
// 3. 여러 줄의 값들을 입력받을 때
const input = require('fs').readFileSync('/dev/stdin').toString().trim().split('\n');
// 4. 첫 번째 줄에 자연수 n을 입력받고, 그 다음줄에 공백으로 구분된 n개의 값들을 입력받을 때
const [n, ...arr] = require('fs').readFileSync('/dev/stdin').toString().trim().split(/\s/);
// 5. 첫 번째 줄에 자연수 n을 입력받고, 그 다음줄부터 n개의 줄에 걸쳐 한 줄에 하나의 값을 입력받을 때
const [n, ...arr] = require('fs').readFileSync('/dev/stdin').toString().trim().split('\n');
// 6. 하나의 값 또는 공백으로 구분된 여러 값들을 여러 줄에 걸쳐 뒤죽박죽 섞여서 입력받을 때
// ex) n 입력 - 공백으로 구분된 n개의 값 입력 - m 입력 - 여러 줄에 걸쳐 m개의 값 입력
const input = require('fs').readFileSync('/dev/stdin').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')로 한 줄 씩 나눈 뒤, 한 줄을 다시 한 번 더 나누는 방식으로 입력을 받아야 한다.
각 경우에 해당하는 백준 문제를 하나씩 뽑아 보았다.
1. 하나의 값을 입력받을 때
10988번: 팰린드롬인지 확인하기
첫째 줄에 단어가 주어진다. 단어의 길이는 1보다 크거나 같고, 100보다 작거나 같으며, 알파벳 소문자로만 이루어져 있다.
www.acmicpc.net
2. 공백으로 구분된 한 줄의 값들을 입력받을 때
2609번: 최대공약수와 최소공배수
첫째 줄에는 입력으로 주어진 두 수의 최대공약수를, 둘째 줄에는 입력으로 주어진 두 수의 최소 공배수를 출력한다.
www.acmicpc.net
3. 여러 줄의 값들을 입력받을 때
1100번: 하얀 칸
체스판은 8*8크기이고, 검정 칸과 하얀 칸이 번갈아가면서 색칠되어 있다. 가장 왼쪽 위칸 (0,0)은 하얀색이다. 체스판의 상태가 주어졌을 때, 하얀 칸 위에 말이 몇 개 있는지 출력하는 프로그램
www.acmicpc.net
4. 첫 번째 줄에 자연수 n을 입력받고, 그 다음줄에 공백으로 구분된 n개의 값들을 입력받을 때
10871번: X보다 작은 수
첫째 줄에 N과 X가 주어진다. (1 ≤ N, X ≤ 10,000) 둘째 줄에 수열 A를 이루는 정수 N개가 주어진다. 주어지는 정수는 모두 1보다 크거나 같고, 10,000보다 작거나 같은 정수이다.
www.acmicpc.net
이 문제는 n 말고 x도 있어서 const [n, x, ...arr]로 풀면 된다.
5. 첫 번째 줄에 자연수 n을 입력받고, 그 다음줄부터 n개의 줄에 걸쳐 한 줄에 하나의 값을 입력받을 때
10773번: 제로
첫 번째 줄에 정수 K가 주어진다. (1 ≤ K ≤ 100,000) 이후 K개의 줄에 정수가 1개씩 주어진다. 정수는 0에서 1,000,000 사이의 값을 가지며, 정수가 "0" 일 경우에는 가장 최근에 쓴 수를 지우고, 아닐 경
www.acmicpc.net
6. 하나의 값 또는 공백으로 구분된 여러 값들을 여러 줄에 걸쳐 뒤죽박죽 섞여서 입력받을 때
2167번: 2차원 배열의 합
첫째 줄에 배열의 크기 N, M(1 ≤ N, M ≤ 300)이 주어진다. 다음 N개의 줄에는 M개의 정수로 배열이 주어진다. 배열에 포함되어 있는 수는 절댓값이 10,000보다 작거나 같은 정수이다. 그 다음 줄에는
www.acmicpc.net
위의 코드와는 상황이 조금 다르지만 split(/\s+/)로 분리하여 적절하게 변수를 지정해주면 된다.
이렇게 입력에 신경을 썼음에도 런타임에러가 나거나 기타 다른 이유로도 문제가 통과되지 않는 경우가 있다.
1. 특정 문제에서 readFileSync가 먹히지 않는 경우
14681번: 사분면 고르기
점 (x, y)의 사분면 번호(1, 2, 3, 4 중 하나)를 출력한다.
www.acmicpc.net
위 문제는 readline 모듈을 사용하면 풀리지만 어떤 이유에서인지 fs모듈의 readFileSync를 사용하면 런타임에러가 발생한다.
최근에 백준에 이와 관련된 공지가 올라오기도 하였다.
www.acmicpc.net/board/view/66736
글 읽기 - node.js 런타임 에러
댓글을 작성하려면 로그인해야 합니다.
www.acmicpc.net
2. Node.js로는 해당 문제의 메모리제한을 넘기지 않고 풀 수 없는 경우
10989번: 수 정렬하기 3
첫째 줄에 수의 개수 N(1 ≤ N ≤ 10,000,000)이 주어진다. 둘째 줄부터 N개의 줄에는 숫자가 주어진다. 이 수는 10,000보다 작거나 같은 자연수이다.
www.acmicpc.net
https://www.acmicpc.net/problem/11723
11723번: 집합
첫째 줄에 수행해야 하는 연산의 수 M (1 ≤ M ≤ 3,000,000)이 주어진다. 둘째 줄부터 M개의 줄에 수행해야 하는 연산이 한 줄에 하나씩 주어진다.
www.acmicpc.net
https://www.acmicpc.net/problem/2293
2293번: 동전 1
첫째 줄에 n, k가 주어진다. (1 ≤ n ≤ 100, 1 ≤ k ≤ 10,000) 다음 n개의 줄에는 각각의 동전의 가치가 주어진다. 동전의 가치는 100,000보다 작거나 같은 자연수이다.
www.acmicpc.net
https://www.acmicpc.net/problem/10886
10886번: 0 = not cute / 1 = cute
준희는 자기가 팀에서 귀여움을 담당하고 있다고 생각한다. 하지만 연수가 볼 때 그 의견은 뭔가 좀 잘못된 것 같았다. 그렇기에 설문조사를 하여 준희가 귀여운지 아닌지 알아보기로 했다.
www.acmicpc.net
[10989번 수 정렬하기 3], [11723번 집합], [2293번 동전 1]
위 세 문제는 현재 Node.js로 통과한 사람이 단 한 명도 없다... (2021.09.10 확인)
[10886번 0 = not cute / 1 = cute]
이 문제는 현재(2022.10.14 확인) 2명이 통과한 기록이 있으나, 이는 각각 4년 전, 2년 전 기록으로 Node.js의 버전이 높아짐에 따라 현재는 통과가 되지 않는다. (3 ~ 4년전 Node.js로 푼 기록이 압도적으로 수행 시간이 짧은 이유)
현재 몇몇 문제에 대한 Node.js 메모리 제한 변경 논의가 이루어지고 있다.
댓글에 위 문제들 중 일부가 언급되어 있는데, 조만간 메모리 제한이 완화될 가능성이 보인다.
(2021.07.30 확인)
https://www.acmicpc.net/board/view/71957
글 읽기 - Swift, node.js 추가 시간/메모리 제한을 위한 소스 코드를 구합니다.
댓글을 작성하려면 로그인해야 합니다.
www.acmicpc.net
하지만 실제로 불가능한 것인지는 알 수 없다.
15552번: 빠른 A+B
첫 줄에 테스트케이스의 개수 T가 주어진다. T는 최대 1,000,000이다. 다음 T줄에는 각각 두 정수 A와 B가 주어진다. A와 B는 1 이상, 1,000 이하이다.
www.acmicpc.net
이 문제 역시 Node.js로 풀 수 없다는 말이 게시판에 올라왔었다. (www.acmicpc.net/board/view/32852)
그러나 1년 후부터 이 문제를 Node.js로 통과한 사람들이 등장하기 시작했다.
여하튼 Node.js로 백준에서 문제 풀기가 참 까다로운 것 같습니다.
혹시 틀린 내용이 있다면 지적 환영합니다!
'IT > JavaScript' 카테고리의 다른 글
[JS] 자바스크립트로 엑셀 파일(xlsx) 만들어서 다운받기 (0) | 2022.08.05 |
---|---|
[JS] Invalid regular expression: invalid group specifier name (0) | 2022.06.21 |
[JS] Selection API로 선택된 텍스트 정보 가져오기 (0) | 2022.06.01 |
[JS] 자바스크립트의 배열 생성 방법 (0) | 2021.07.29 |
[JS] 함수의 매개변수와 RangeError (0) | 2021.07.23 |