Python初心者のお勉強ノート(6)標準入力と標準出力、ファイルの読み書き (original) (raw)

標準入力と標準出力

標準入力と標準出力の考え方はUnixオペレーティング・システム(OS)からきています。 プログラムを実行するとき、そのプログラム(より正確にはプロセスという)に対して、標準入力と標準出力がシステムによって用意されます。 特にプログラム起動時に何もしなければ

となっています。

例えば、Hello world.を画面出力するプログラムhelloworld.pyは

print("Hello world.")

でした。 print関数は標準出力に対して引数を出力しますので、デフォルトでは画面に表示されます。

標準出力の出力先はコマンドラインで変更ができます。 たとえば、ファイルに出力したければ、

py helloworld.py >hy.txt cat hy.txt Hello world.

最初のコマンドでhelloworld.pyを実行していますが、「>hy,txt」の部分が標準出力をファイルhy.txtに切り替えています。 そのために、print関数の出力先がhy.txtになり、ファイル書き込みをすることになるのです。

コマンドラインからcatコマンドでファイルの内容を表示できます(Powershell ver7やLinuxの場合)。 ファイルhy.txtの内容がHello world.であることが確認できます。

このようにプログラムが標準出力を使っていれば、コマンド起動時にその出力先を変えることができ、柔軟性が増します。

標準入出力は、OSがサポートするものなので、プログラミング言語自体が提供するものではありません。 しかし、どの言語も何らかのOS上で動くので、OSの仕組みのいくつかの部分はその言語でのプログラミングに影響します。 標準入出力は、そういうOSの影響下にある問題です。

標準入力

標準入力はデフォルトでキーボードに割り当てられています。 最初に例としてあげるのは、input関数です。

x = input("x = ")

この例では、

pythonを引数なしで起動して、input関数を試してみます。

x = input("x = ") x = 100 x '100'

最初に「x = input("x = ")」を実行すると、プロンプト「x =」が表示されます。 それに続けて、100と入力し、エンターキーを押すと、プロンプトと入力をあわせて、2行目の

x = 100

という一行になります。 次に変数xを表示すると、文字列'100'が現れます。

このようにして、ユーザと対話しながら処理をすることができます。 以前の足し算のプログラムをinut関数を使って書き直してみましょう。

x = input("x = ") y = input("y = ") print(f"x + y = {float(x)+float(y)}")

このプログラムは、xとyの2つの数字をユーザに入力させ、その和を表示する対話型プログラムです。

print関数の中の文字列はf-stringと呼ばれる文字列リテラルです。 「フォーマット済み文字列リテラル」ということもありますが、長いのでf-stringが良いでしょう。 このリテラルを詳しく説明すると長くなるので、ここでは次の2点だけ確認しておきます。

実行してみましょう。 ファイル名をadd_input.pyとします。

py add_input.py x = 100 y = 250 x + y = 350.0

標準入力も標準出力同様に入力先をコマンドから変更することができます。 これをリダイレクトといいます。 しかし、上記のinput関数はキーボード入力を想定したものであり、リダイレクトすると動作がおかしくなるでしょう。

リダイレクトの例として、標準入力からのデータをそのまま標準出力に送るプログラムstdin2stdout.pyを考えてみましょう。

import sys

for line in sys.stdin: print(line, end="")

sys.stdinは、標準入力のストリームを直接扱うことができるオブジェクトです。 これをforループと組み合わせると、変数lineに1行ずつ入れてfor文のスイートを繰り返し実行することができます。

print関数の引数で「end=""」の部分は文字列の最後に空文字列を入れる、つまり何も入れないということを意味します。 「end=""」を省略すると、改行"\n"が入ります。 文字列lineには標準入力から入ってくる改行も含まれているので、print関数で新たに改行を入れるべきではありません。

このプログラムを動かしてみます。

py stdin2stdout.py a a b b c c ^Z

同じ文字が繰り返されていますが、上がキーボード入力、下がプログラムが入力をコピーして出力したものです。 CTRL-Zで入力を終わらせることができます。LinuxMacの場合はCTRL-Dです。

次に標準入力と標準出力を繋ぎ変えてみましょう。Linuxでは(bashでは)<を使って標準入力を直接切り替えられますが、Powershell ver7ではこれはサポートされていません。 代わりに、catでファイル内容を出力し、その出力とPythonプログラムの入力をパイプで繋ぎます。

cat stdin2stdout.py | py stdin2stdout.py >stdin2stdout-copy.py cat stdin2stdout-copy.py import sys

for line in sys.stdin: print(line, end="")

1行目でコピーをしています。 2行目ではコピー先のファイルをcatで表示しています。 これでコピーが正しくできていたことがわかります。

ファイルの読み込み

標準入出力は最初からオープンされているので、簡単に使えますが、自分でファイルを新たに使う場合は、オープンとクローズが必要です。 しかし、with文を使うと、オープンのみの記述ですみ、クローズは自動的にやってくれるので楽になります。 引数で与えられたファイルを標準出力に出力するプログラムcat-p.pyを書いてみましょう。 プログラム名は標準で使われているプログラムのcatからとりました。

import sys

if len(sys.argv) != 2: print("Usage: python cat-p.py ") else: filename = sys.argv[1] with open(filename, "r", encoding="utf-8") as f: content = f.read() print(content, end="")

まず、引数の数をチェックします。 引数は1個ですが、sys.argvにはプログラム名も入るので、そのサイズは2でなければなりません。

with文のところがメインです。

実行してみます。

py cat-p.py cat-p.py import sys

if len(sys.argv) != 2: print("Usage: python cat-p.py ") else: filename = sys.argv[1] with open(filename, "r", encoding="utf-8") as f: content = f.read() print(content, end="")

正しく動作していることが確認できました。

標準出力をリダイレクトすれば、このプログラムをファイルコピーのプログラムとして活用することができます。

ファイルへの書き込み

ファイルを読み込むには、はじめにopen関数を使いました。 そのときopen関数に"r"という文字列を渡したのは、「読み込み専用」という意味です。 これに対して、「書き込み専用」「追記」には"w"や"a"を指定します。 これらの"r"、"w"、"a"をモードと呼びます。

次は、ファイルに文字列を書き込むコードの例です。

with open("出力先ファイル名", "w", encoding="utf-8") as f: f.write("ファイルに書き込む文字列")

f.writeは文字列をそのまま書き込みます。printのように改行を付け加えたりはしません。

次の例は、既存のファイルに文字列を追記します。

with open("追記先ファイル名", "a", encoding="utf-8") as f: f.write("追記されるテキスト")

関数に与えた「追記先ファイル名」がディスク内に存在しない場合は、ファイルを新規作成して書き込みます。 この場合は、書き込みモードと追記モードの振る舞いは同じです。 もっとも、追記モードを使う場合は、そのファイルがすでに存在することが分かっている場合が多いですが。

ここでは、書き込みノードの例として、ファイルコピープログラムを作ってみましょう。 引数に、コピー元、コピー先の2つのファイル名を与えます。

import sys

if len(sys.argv) != 3: print("Usage: python cp-p.py ") sys.exit(1)

src = sys.argv[1] dst = sys.argv[2]

try: with open(src, "r", encoding="utf-8") as src_file: content = src_file.read()

with open(dst, "w", encoding="utf-8") as dst_file:
    dst_file.write(content)

except FileNotFoundError: print(f"Error: The file '{src}' was not found.") except IOError: print("Error: An error occurred during file read/write.")

試してみます。

py cp-p.py cp-p.py cp-p2.py cat cp-p2.py import sys

if len(sys.argv) != 3: print("Usage: python cp-p.py ") else: src = sys.argv[1] dst = sys.argv[2]

try:
    with open(src, "r", encoding="utf-8") as src_file:
        content = src_file.read()

    with open(dst, "w", encoding="utf-8") as dst_file:
        dst_file.write(content)

except FileNotFoundError:
    print(f"Error: The file '{src}' was not found.")
except IOError:
    print("Error: An error occurred during file read/write.")

コピーの元は、このプログラム自身(cp-p.py)です。 プログラムの実行後、catコマンドでコピー先のファイルを表示しました。 コピーができていたことが確認できました。

最後に、文字コードについて補足しておきます。 現在、事実上の標準文字コードUTF-8です。 しかし、UTF-8は比較的新しく、その前は様々な文字コードが使われていました。 open関数で文字コードを省略すると、システムの標準文字コードが採用されます。LinuxMacではそれがUTF-8なので何の問題もありませんが、WindowsではCP932というShift-JISの拡張である可能性が高いです。 現在ではUTF-8が主流で、それにしておけばまず混乱はありません。 したがって、open関数には必ず encoding="utf-8" をつけるようにしましょう。