Python StringIOとは?文字列をファイルのように扱う方法

io.StringIOは、メモリ上でファイルのように読み書きできるオブジェクトです。

実際のファイルを作らずに、ファイル用の関数をそのまま使えます。


StringIOの基本

import io

s = io.StringIO()
s.write("1行目\n")
s.write("2行目\n")

print(repr(s.getvalue()))

実行結果:

'1行目\n2行目\n'

getvalue()で書き込んだ内容全体を取り出せます。

初期値を渡して読み取り用に使うこともできます。

s = io.StringIO("a\nb\nc\n")
print([line.rstrip() for line in s])

実行結果:

['a', 'b', 'c']

for文で1行ずつ回せます。ファイルとまったく同じ使い方です。


read()が空になる理由

s = io.StringIO()
s.write("1行目\n")

print(repr(s.read()))

実行結果:

''

書いた直後に読むと空です。

原因は読み書きの位置にあります。書き込み後、位置は末尾にあります。そこから読んでも何もありません。

s.seek(0)
print(repr(s.read()))

実行結果:

'1行目\n'

seek(0)で先頭に戻してから読みます。

これは実際のファイルでも同じ挙動です。getvalue()は位置に関係なく全体を返すため、書いた内容を取り出すだけならgetvalue()が簡単です。


ファイル用の関数をテストする

import io

def count_lines(f):
    return sum(1 for _ in f)

print(count_lines(io.StringIO("x\ny\nz\n")))

実行結果:

3

ファイルを引数に取る関数を、ファイルなしでテストできます。

テスト用のファイルを用意する必要がなく、後片付けも不要です。

  • 一時ファイルを作らない
  • テストが並行実行しても衝突しない
  • ディスクI/Oがないので速い

関数を書くときはファイル名ではなくファイルオブジェクトを受け取る設計にしておくと、テストしやすくなります。


csvと組み合わせる

import io
import csv

buf = io.StringIO()
w = csv.writer(buf)
w.writerow(["名前", "年齢"])
w.writerow(["田中", 30])

print(repr(buf.getvalue()))

実行結果:

'名前,年齢\r\n田中,30\r\n'

CSVを文字列として生成できます。

Webアプリでダウンロード用のCSVを返すときなど、ファイルに保存せず直接送りたい場面で使います。

読み込みも同様です。

r = csv.reader(io.StringIO("名前,年齢\n田中,30\n"))
print(list(r))

実行結果:

[['名前', '年齢'], ['田中', '30']]

APIから受け取ったCSV文字列を、保存せずにそのまま解析できます。

「Python csv.reader()とは?CSVを1行ずつ読み込む方法」も参照してください。


標準出力を捕まえる

import io
import sys

buf = io.StringIO()
old = sys.stdout
sys.stdout = buf

print("この出力は捕まえられる")

sys.stdout = old
print("捕まえた:", repr(buf.getvalue()))

実行結果:

捕まえた: 'この出力は捕まえられる\n'

print()の出力をテストできます。

ただし戻し忘れると以降の出力がすべて消えます。標準ライブラリに安全な書き方が用意されています。

import contextlib
import io

buf = io.StringIO()
with contextlib.redirect_stdout(buf):
    print("捕まえたい出力")

print(repr(buf.getvalue()))

withを抜ければ自動で元に戻ります。例外が起きても確実です。

「Python contextlibとは?with文を自作する方法を解説」を参照してください。


closeすると取り出せない

s = io.StringIO("x")
s.close()
s.getvalue()

実行結果:

ValueError: I/O operation on closed file

getvalue()はclose()の前に呼んでください。

withで囲むと、抜けた時点で閉じられます。

with io.StringIO() as s:
    s.write("data")
    value = s.getvalue()      # with の中で取り出す

実際のファイルと違い、StringIOは明示的に閉じなくても問題ありません。メモリ上のオブジェクトなので、参照がなくなれば解放されます。


bytesは扱えない

io.StringIO().write(b"abc")

実行結果:

TypeError: string argument expected, got 'bytes'

バイナリを扱うならBytesIOを使います。

「Python BytesIOとは?メモリ上でバイナリを扱う方法」で解説しています。


StringIOのまとめ

  • メモリ上でファイルのように読み書きできる
  • 書いた内容はgetvalue()で取り出す
  • read()が空ならseek(0)を忘れている
  • ファイル用の関数をテストしやすくなる
  • 標準出力の差し替えはredirect_stdout()が安全
  • bytesは扱えない——BytesIOを使う

あわせて読みたい

対面・無料|東京・永田町で毎週土曜日13時

1回の参加で、プログラミングの構造がわかる

ゼロからはじめるPython入門講座

8年間・累計600回開催/これまで全国で2万人以上が参加

「やはり対面で聞けるのはとても気づきがあり、価値があると思います」

参加者の声

  • 本当に初心者からの講座です。プログラミングに触ったことがなくて大丈夫です
  • 対面だから、その場で聞けます。つまずいた所を持ち帰らずに解決できます
  • 講座は1時間。そのあとも残って学習を続けられ、講師にも質問できます
  • 独学で挫折した方も。糸口が見つかって、講座のあと会員になる方が多くいます

無料で参加する|開催日程を見る

参加費は無料です