概要
PythonでCSVを読むコードを実装していたら以下のエラーになりました。
本記事では原因と解決方法を記載します。
_csv.Error: iterator should return strings, not bytes (did you open the file in text mode?)
原因
発生する原因は、csv
モジュールのreader
オブジェクトに対して、バイト列を返してしまっているため。
文字列を返す必要があります。以下、どちらでも上述のエラーになります。
csvfile = request.FILES['file']
reader = csv.DictReader(csvfile)
for row in reader:
csvfile = request.FILES['file']
decoded_file = csvfile.read() # バイト列として読み込んでいる
reader = csv.reader(decoded_file)
for row in reader:
ちなみに、open()
関数で'rb'
というモードを指定している場合もファイルがバイナリモードで開かれているので、同じくエラーが発生します。csv.reader
は文字列を期待しているからですね。
解決方法
エラーメッセージの通り、ファイルがテキストモードで開かれていないのが原因なので、csv.reader
に渡す前にテキストモードで開けば解決します。以下のように変更するとエラーがなくなりました。
csvfile = request.FILES['file']
decoded_file = csvfile.read().decode('utf-8-sig').splitlines()
reader = csv.reader(decoded_file)
for row in reader:
上記では、.decode('utf-8-sig')
を使用してバイト列をUTF-8エンコーディングでデコードし、テキストに変換します。
decode('utf-8-sig')
については、BOM除去という点で一つ詰まった点があります。
BOM除去については以下の記事で記載していますので参考ください。
【Python】CSVを読み込むと文字列の中に\ufeffが入ってしまう場合の原因と解決方法
補足:request.FILES['file']
とは
Djangoでは、HTTPリクエストに添付されたファイルを処理するために、request.FILES
という属性を提供しています。
request.FILES
は、ユーザーがフォームからファイルをアップロードした場合や、APIリクエストの一部としてファイルを送信した場合など、リクエストに含まれるファイルデータを取り扱うための特別なデータ構造です。
'file'
部分は、アップロードされたファイルのフィールド名を指定します。
例えば、hogehoge.csv
をアップロードした場合、request.FILES['file']
をデバッグすればhogehoge.csv
と出力されます。また、データの型は<class 'django.core.files.uploadedfile.InMemoryUploadedFile'>
というInMemoryUploadedFile
クラスになります。ファイルのデータをメモリ内に一時的に保存し、後続の処理で利用することができます。
詳細はDjangoドキュメントからどうぞ。
ファイルのアップロード — Django 4.0.6 ドキュメント