[Python3] input()とsys.stdin

よく、オンラインのプログラミングテストとかで、
標準入力から値を受け取るときに使うinput()。

今まで僕も普通にこれを使ってきたんですが、
このinput()関数が、意外と時間がかかるみたいです。

例えば、
AOJ ALDS1_3_C : Doubly Linked List

標準入力から、最大200万行くらい読み取るんですが、
はじめ、input()を使ったコードで書いていたところ、
実行時間が、最大5秒近くかかっていました。

それが、他の人の結果を見てると、
実行時間1秒以内でクリアしてる人がいたので、
どんなコードなのか見てみたところ、
(他人の提出コードも見れる。)←これいい

input()関数は使っていなくて、
sys.stdinを使っているんですよね。

それを参考にして、自分も試しに、
sys.stdinを1行ずつ読み込むようにしたところ、
他の処理はほとんど変わってないにもかかわらず、
実行時間が、最大でも1秒ちょっとに縮まりました。

それくらいになると結構効いてくるようです。

というわけで、input()関数とsys.stdinの違い。
sys.stdinを使う上で気を付ける点を、
気づいた範囲でまとめます。

入力行数がわからないとき

sys.stdinの時は、入力行数がわからなくても困りません。

例えば、下記のような入力データを読み取って、
そのまま1行ずつ出力する場合。

test1
test2
test3
test4
test5

sys.stdinを使えばこれだけでOK。

import sys
for s in sys.stdin: print(s.rstrip())

これが、input()関数を使う場合は、
try~exceptでも書けばいいんですかね?

while True:
    try: s = input()
    except EOFError: break
    else: print(s)

でも、オンラインのプログラミングテストでは、
入力行数が与えられることがほとんどなので、
あんまりめんどくさくはならないです。

n = int(input()) # 1行目で入力行数が与えられる
for i in range(n): print(input())

改行コードが取り除かれない

上記コードで、sys.stdinでは、
さりげなくrstrip()を付けたんですが、

どうやら、input()では、
末尾の改行が取り除かれるのに対して、
sys.stdinで1行ずつ読み込んだ時には、
末尾の改行コードが入ったままのようです。

rstrip()を付けなかった場合にはこうなります。

import sys
for s in sys.stdin: print(s)

### 出力 #####
test1

test2

test3

test4

test5

print関数がつける行末の改行に加えて、
文字列sに含まれる改行コードもあるため、
2行ずつ改行されてしまっています。

なので、出力や計算に影響がある場合は、
この改行コードへの対策が必要。

rstrip()が確実ではあるが、
これはこれでそれなりに時間がかかる。
スライスを使うと速い。

import sys
for s in sys.stdin: print(s[:-1])

### 出力 #####
test1
test2
test3
test4
test5

また、開始位置を指定することで、
数字の部分だけを抜き出すこともできる。

import sys
for s in sys.stdin: print(s[4:-1])

### 出力 #####
1
2
3
4
5

ただし、この[:-1]は、
行末に改行が入っていることが前提。

例えば、入力データの最終行に改行がないとこうなる。

import sys
for s in sys.stdin: print(s[:-1])

### 出力 #####
test1
test2
test3
test4
test

問題文で、入力データの制約に、
「※最終行には改行が入る」
みたいなことが書いてあればいいですが、
書いてなければ注意が必要かもしれません。

それでも、rstrip()使いたくない場合は、

import sys
for s in sys.stdin: print(s[:-1] if s[-1] == "\n" else s)

だと、rstrip()を使うよりは速いっぽいです。

ただ、冒頭のリンク先の問題では通りましたが、
入力データがよくわからないときは、
改行コードが”\n”でいいかわからないので、
これはこれで危険かもですね。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です