Python input()과 sys.stdin.readline() 차이: 개행·EOF·속도 기준

반응형

input()sys.stdin.readline()은 둘 다 표준 입력에서 한 줄을 읽을 수 있지만 반환 규칙이 다르다. input()은 줄 끝 개행을 제거하고 EOF에서 EOFError를 발생시킨다. sys.stdin.readline()은 개행을 보존하고 EOF에서 빈 문자열 ''을 반환한다.

코딩 테스트에서 입력이 많으면 sys.stdin.buffer.readline을 자주 사용하지만, “buffer를 쓰기 때문에 무조건 훨씬 빠르다”만으로 설명하면 부족하다. sys.stdininput()도 Python의 text I/O stack을 이용한다. 차이는 wrapper가 제공하는 prompt·개행 제거·EOF 처리와 호출 overhead까지 함께 봐야 한다.

세 가지 입력 방식 비교

방식 반환 type 줄 끝 개행 EOF 주된 용도
input() str 제거 EOFError 짧고 읽기 쉬운 일반 입력
sys.stdin.readline() str 있으면 보존 '' 여러 줄 text 입력, EOF loop
sys.stdin.buffer.readline() bytes 있으면 보존 b'' 변환 가능한 대량 byte 입력

input(prompt)는 prompt 문자열을 표준 출력에 표시할 수도 있다. 반면 readline()은 prompt 기능이 없다.

개행과 EOF를 직접 확인하기

text stream의 readline() 규칙은 io.StringIO로 재현할 수 있다.

import io


stream = io.StringIO("alpha\n\nomega")

assert stream.readline() == "alpha\n"
assert stream.readline() == "\n"
assert stream.readline() == "omega"
assert stream.readline() == ""

두 번째 결과 "\n"빈 줄을 읽었다는 뜻이고, 마지막 ""더 읽을 data가 없는 EOF라는 뜻이다. 둘을 같게 처리하면 blank line과 입력 종료를 구분하지 못한다.

마지막 줄에는 개행이 없을 수도 있다. 그래서 readline()이 반환한 모든 정상 line이 반드시 \n으로 끝난다고 가정하면 안 된다.

strip을 습관처럼 붙이지 않기

readline().strip()은 개행만 지우는 code가 아니다. 문자열 양끝의 space, tab과 여러 whitespace도 모두 제거한다.

line = "  value  \n"

assert line.strip() == "value"
assert line.rstrip("\n") == "  value  "

앞뒤 space가 data라면 rstrip("\n")처럼 의도를 좁혀야 한다. 숫자 하나를 int()로 바꾸거나 숫자 여러 개를 split()할 때는 둘 다 주변 whitespace를 처리하므로 먼저 strip할 필요가 없다.

number = int("42\n")
values = list(map(int, "10 20 30\n".split()))

assert number == 42
assert values == [10, 20, 30]

코딩 테스트에서 쓰는 형태

text가 필요한 일반 입력은 다음처럼 함수 reference를 한 번 받아 둘 수 있다.

import sys


read = sys.stdin.readline

n = int(read())
values = list(map(int, read().split()))

ASCII 숫자 중심의 대량 입력이라면 binary buffer를 사용할 수 있다.

import sys


read = sys.stdin.buffer.readline

n = int(read())
values = list(map(int, read().split()))

두 번째 read()의 결과는 bytes이고 split() 결과도 bytes list다. int()는 ASCII 형태의 bytes를 받을 수 있어 이 예제는 별도 decode 없이 동작한다. 한글이나 일반 text 처리가 필요하면 encoding을 알고 decode하거나 text stream을 사용하는 편이 안전하다.

실제 적용 예시는 백준 1753 다익스트라 풀이백준 2018 투 포인터 풀이에서 볼 수 있다.

속도는 같은 data로 측정한다

원래 글의 benchmark처럼 표준 입력을 첫 loop에서 모두 소비한 뒤 두 번째 loop에서 input()을 호출하면 두 방식을 비교한 것이 아니다. interactive terminal에서 사람이 입력하는 시간까지 섞이면 함수 overhead도 측정할 수 없다.

공정한 비교에는 다음 조건이 필요하다.

  1. 두 방식에 똑같은 input data를 각각 새 stream으로 제공한다.
  2. parsing과 newline 처리 범위를 동일하게 맞춘다.
  3. warm-up과 여러 반복을 두고 분산을 확인한다.
  4. 최종 판단은 실제 online judge나 production workload에서 측정한다.

입력이 적으면 input()의 가독성이 더 중요할 수 있다. 수십만 줄을 읽고 시간 제한이 빡빡하면 sys.stdin.buffer.readline이 유리한지 실제로 확인한다. 무조건 모든 code를 binary I/O로 바꿀 필요는 없다.

EOF까지 읽는 방법

sys.stdin은 iterable이므로 EOF까지 line을 처리할 때 직접 반복할 수 있다.

import sys


for line in sys.stdin:
    value = line.rstrip("\n")
    print(value)

직접 readline() loop를 쓴다면 빈 문자열을 EOF signal로 본다.

import sys


while True:
    line = sys.stdin.readline()
    if line == "":
        break

    print(line.rstrip("\n"))

if not line.strip()로 끝을 판단하면 whitespace만 있는 정상 line까지 종료로 오인할 수 있다.

자주 묻는 질문

input()은 내부에서 sys.stdin.readline()을 그대로 호출하나?

사용자 관점에서 둘이 비슷해 보여도 완전히 같은 wrapper라고 가정하면 안 된다. input()에는 prompt, trailing newline 제거, EOFError와 interactive readline hook 같은 자체 semantics가 있다. 구현 내부 호출 관계보다 문서화된 반환 규칙에 의존하는 편이 안전하다.

readline() 뒤에는 항상 strip()이 필요한가?

아니다. int()split()은 whitespace를 처리한다. 문자열의 줄바꿈만 지워야 한다면 rstrip("\n")을 쓰고, 양끝 whitespace 전체를 버려도 될 때만 strip()을 쓴다.

sys.stdin.buffer.readline()은 항상 가장 좋은가?

아니다. bytes 처리와 encoding 경계가 생기며 작은 입력에서는 가독성 이득이 거의 없다. 대량의 단순 입력에서 병목을 확인했을 때 선택한다.

참고 자료

반응형
KEEP READING
카테고리 전체 보기 →

댓글