- Python
PythonでCSVを読み書き|コピペ用テンプレートと文字化け対策
PythonでCSVを読み書きするには、標準ライブラリの csv モジュールを使い、open(ファイル名, newline="", encoding="utf-8") で開いたファイルを csv.reader / csv.writer に渡します。 インストールは不要です。
この記事では、読む・書く・追記・辞書で扱う(DictReader / DictWriter)のコピペ用テンプレートを最初にまとめ、そのあとで、つまずきやすい newline="" の意味、Excel向けの文字コード(utf-8-sig と cp932)、UnicodeDecodeError、クォート を、実際にファイルを書き出してバイト列で確認した結果つきで解説します。出力はすべて Python 3.14.7(pandasは3.0.6)で実行したものです。
Python CSV読み書きの早見表
| やりたいこと | 書き方 | ポイント |
|---|---|---|
| 1行ずつリストで読む | for row in csv.reader(f): |
値はすべて文字列 |
| 1行ずつ辞書で読む | for row in csv.DictReader(f): |
1行目がキーになる |
| リストで書く | csv.writer(f).writerows(rows) |
1行なら writerow |
| 辞書で書く | csv.DictWriter(f, fieldnames=[...]) |
writeheader() を忘れずに |
| 追記する | open(..., "a", newline="") |
ヘッダーは書かない |
| 開き方(共通) | open(p, newline="", encoding="utf-8") |
newline="" は必ず付ける |
| Excelで開くCSVを書く | encoding="utf-8-sig" |
先頭にBOMが付く |
| Excelで保存したCSVを読む | encoding="cp932" または "utf-8-sig" |
保存形式による |
| ヘッダーを飛ばす | header = next(reader) |
|
| TSVを読む・書く | delimiter="\t" |
|
すべての値を " で囲む |
quoting=csv.QUOTE_ALL |
|
| 大きいファイル | for で1行ずつ処理 |
list() にしない |
| 表として集計したい | pd.read_csv(p) |
pandas |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「PythonでCSVを読み書き|コピペ用テンプレートと文字化け対策」
コピペ用テンプレート:読む・書く・追記・辞書で扱う
どのテンプレートも import csv だけで動きます。newline="" と encoding= は毎回書くと、OSや環境による違いが出にくくなります。
CSVに書き込む(csv.writer)
import csv
rows = [
["id", "name", "qty"],
[1, "ペン", 120],
[2, "ノート", 45],
]
with open("items.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
writer.writerows(rows) # 1行ずつなら writer.writerow(row)
print(open("items.csv", "rb").read())
実行結果(Python 3.14.7、書き出したファイルのバイト列):
b'id,name,qty\r\n1,\xe3\x83\x9a\xe3\x83\xb3,120\r\n2,\xe3\x83\x8e\xe3\x83\xbc\xe3\x83\x88,45\r\n'
csvモジュールは行末に \r\n(CRLF)を書きます。 これはCSVの仕様(RFC 4180)に合わせた既定値で、lineterminator="\n" で変えられます。数値は自動で文字列になります。
CSVを読み込む(csv.reader)
import csv
with open("items.csv", newline="", encoding="utf-8") as f:
reader = csv.reader(f)
for row in reader:
print(row)
['id', 'name', 'qty']
['1', 'ペン', '120']
['2', 'ノート', '45']
読み込んだ値はすべて文字列(str)です。 数値として計算するには変換が必要です(後述)。
CSVに追記する(モード "a")
import csv
with open("items.csv", "a", newline="", encoding="utf-8") as f:
csv.writer(f).writerow([3, "クリップ", 300])
with open("items.csv", newline="", encoding="utf-8") as f:
print(f.read())
id,name,qty
1,ペン,120
2,ノート,45
3,クリップ,300
"w" は上書き、"a" は末尾に追加です。追記は元のファイルと同じ encoding で開きます。 utf-8-sig で書いたファイルに utf-8-sig で追記しても、途中にBOMは入りませんでした(実行結果:b'\xef\xbb\xbfid\r\n1\r\n'。BOMは先頭の1つだけ)。
辞書として読む(csv.DictReader)
import csv
with open("items.csv", newline="", encoding="utf-8") as f:
for row in csv.DictReader(f):
print(row["name"], row["qty"], row)
ペン 120 {'id': '1', 'name': 'ペン', 'qty': '120'}
ノート 45 {'id': '2', 'name': 'ノート', 'qty': '45'}
クリップ 300 {'id': '3', 'name': 'クリップ', 'qty': '300'}
1行目(ヘッダー)がキーになるので、列の順番が変わっても row["name"] で取り出せます。 実務では reader より DictReader のほうが読みやすく、列の追加にも強いです。
辞書から書く(csv.DictWriter)
import csv
members = [
{"id": 1, "name": "佐藤", "email": "sato@example.com"},
{"id": 2, "name": "鈴木"}, # emailがない
]
with open("members.csv", "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=["id", "name", "email"], restval="")
writer.writeheader()
writer.writerows(members)
print(open("members.csv", encoding="utf-8").read())
id,name,email
1,佐藤,sato@example.com
2,鈴木,
fieldnames で列の順番を決め、writeheader() でヘッダー行を書きます。 足りないキーは restval の値(既定は空文字)で埋まります。
逆に fieldnames にないキーがあるとエラーになります。
w = csv.DictWriter(f, fieldnames=["id", "name"])
w.writerow({"id": 1, "name": "佐藤", "age": 31})
ValueError: dict contains fields not in fieldnames: 'age'
→ extrasaction="ignore" を付けると余分なキーは無視されます(実行結果:1,佐藤)。
DictReaderで列が足りない・多い行を読む
import csv, io
s = "id,name,qty\n1,ペン\n2,ノート,45,余分\n"
for r in csv.DictReader(io.StringIO(s), restval="(なし)", restkey="extra"):
print(r)
{'id': '1', 'name': 'ペン', 'qty': '(なし)'}
{'id': '2', 'name': 'ノート', 'qty': '45', 'extra': ['余分']}
足りない列は restval(既定は None)、余った値は restkey(既定は None というキー)にリストで入ります。
newline=""を付けないと空行が入る理由
newline="" を付けないと、Windowsでは1行ごとに空行が入ったCSVになります。 公式ドキュメントでも、csvモジュールでファイルを開くときは newline="" を指定するよう書かれています。
仕組み:\r\n がさらに変換されて \r\r\n になる
- csvモジュールは行末に
\r\nを書く - Windowsのテキストモードは、書き込み時に
\nを\r\nに変換する - 結果、行末が
\r\r\nになり、Excelなどで開くと空行が入って見える
newline="" は「Pythonによる改行の変換をしない」という指定です。Linux上で、Windowsと同じ変換(newline="\r\n")を指定して再現し、バイト列を比べました。
import csv
row = ["id", "name"]
with open("ok.csv", "w", newline="", encoding="utf-8") as f: # 正しい書き方
csv.writer(f).writerow(row)
with open("ng.csv", "w", newline="\r\n", encoding="utf-8") as f: # Windowsで省略したときと同じ変換
csv.writer(f).writerow(row)
with open("linux.csv", "w", encoding="utf-8") as f: # Linuxで省略した場合
csv.writer(f).writerow(row)
print("ok:", open("ok.csv", "rb").read())
print("ng:", open("ng.csv", "rb").read())
print("linux省略:", open("linux.csv", "rb").read())
ok: b'id,name\r\n'
ng: b'id,name\r\r\n'
linux省略: b'id,name\r\n'
Linux・macOSでは省略しても変換されないため、問題が表に出ません。 そのため「手元では動いたのに、Windowsで作ったCSVにだけ空行が入る」ということが起こります。\r\r\n のファイルを読み込むと、空のリストが混ざりました。
[['1', 'ペン'], [], ['2', 'ノート'], []]
(ng.csv と同じ方法で2行書いたファイルを csv.reader で読んだ結果)
読み込み時も newline="" が必要:値の中の改行
値の中に改行を含むCSVは、読み込み時にも newline="" がないと改行が変わります。
import csv
with open("memo.csv", "w", newline="", encoding="utf-8") as f:
csv.writer(f).writerow([1, "1行目\r\n2行目"])
with open("memo.csv", newline="", encoding="utf-8") as f:
print("newline='' :", list(csv.reader(f)))
with open("memo.csv", encoding="utf-8") as f:
print("newline省略 :", list(csv.reader(f)))
newline='' : [['1', '1行目\r\n2行目']]
newline省略 : [['1', '1行目\n2行目']]
省略すると、値の中の \r\n が \n に変わりました。読むときも書くときも newline="" を付けると覚えておけば安心です。
文字コード:utf-8-sigとcp932の使い分け
Excelでそのまま開くCSVを作るなら utf-8-sig、Excelで「CSV(コンマ区切り)」として保存されたファイルを読むなら cp932 です。 3つの文字コードで同じ内容を書き出し、先頭のバイトを比べました。
import csv
row = [["id", "name"], [1, "佐藤"]]
for enc in ["utf-8", "utf-8-sig", "cp932"]:
fn = f"out_{enc}.csv"
with open(fn, "w", newline="", encoding=enc) as f:
csv.writer(f).writerows(row)
print(f"{enc:10}", open(fn, "rb").read())
utf-8 b'id,name\r\n1,\xe4\xbd\x90\xe8\x97\xa4\r\n'
utf-8-sig b'\xef\xbb\xbfid,name\r\n1,\xe4\xbd\x90\xe8\x97\xa4\r\n'
cp932 b'id,name\r\n1,\x8d\xb2\x93\xa1\r\n'
utf-8-sig は先頭に EF BB BF(BOM)の3バイトが付きます。 ExcelはこのBOMを見てUTF-8と判断するため、日本語が正しく表示されます。cp932 は「佐藤」が4バイト(8D B2 93 A1)で、UTF-8(6バイト)とは別のバイト列です。
| encoding | BOM | 主な用途 | 注意点 |
|---|---|---|---|
utf-8 |
なし | システム間の連携、Web、DB取り込み | Excelで直接開くと文字化けすることがある |
utf-8-sig(書き込み) |
付ける | Excelで開くCSVを出力 | BOMを想定しないシステムでは先頭に余分な文字が入る |
utf-8-sig(読み込み) |
あれば除く | BOM付き・なし両方のUTF-8を読む | BOMがなくても普通に読める |
cp932 |
なし | Excelで保存された日本語CSV、古いシステム | 絵文字など一部の文字は書けない |
文字化けの仕組みやExcel側での開き方は、「文字化けの原因と直し方」の記事で詳しく扱っています。ここではPythonでの書き方に絞ります。
BOM付きをutf-8で読むとKeyErrorになる
import csv
with open("out_utf-8-sig.csv", newline="", encoding="utf-8") as f:
r = next(csv.DictReader(f))
print(r)
print(r["id"])
{'id': '1', 'name': '佐藤'}
KeyError: 'id'
BOMが1列目のキー名にくっつき、'id' というキーになります。 画面上は id に見えるため気づきにくいエラーです。encoding="utf-8-sig" で開くとBOMが取り除かれ、r["id"] で 1 が取れました。utf-8-sig はBOMがないファイルもそのまま読めるので、UTF-8のCSVを読むときは utf-8-sig にしておくと安全です。
UnicodeDecodeErrorの原因と対処
UnicodeDecodeError は、ファイルの実際の文字コードと、encoding= で指定した文字コードが違うときに出ます。
import csv
with open("out_cp932.csv", newline="", encoding="utf-8") as f: # cp932のファイルをutf-8で読む
print(list(csv.reader(f)))
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x8d in position 11: invalid start byte
position 11 は先頭から11バイト目(id,name\r\n1, の直後、「佐」の1バイト目 0x8D)です。逆に、UTF-8のファイルを cp932 で読むと、エラーにならずに文字化けすることがあります。
with open("out_utf-8.csv", newline="", encoding="cp932") as f:
print(list(csv.reader(f)))
[['id', 'name'], ['1', '菴占陸']]
エラーが出ないぶん気づきにくいので、読み込んだ直後に数行を表示して確認すると安心です。
対処1:正しい文字コードを指定する(基本)
エラーの 'utf-8' codec の部分が「今の指定」です。日本語のCSVなら、まず utf-8-sig と cp932 を試します。候補を順に試す関数にしておくと便利です。
import csv
def open_csv(path, encodings=("utf-8-sig", "cp932")):
"""候補の文字コードを順に試して開く"""
for enc in encodings:
try:
with open(path, newline="", encoding=enc) as f:
return enc, list(csv.reader(f))
except UnicodeDecodeError:
continue
raise ValueError(f"{path}: 文字コードを判定できませんでした")
for p in ["out_utf-8.csv", "out_utf-8-sig.csv", "out_cp932.csv"]:
print(p, open_csv(p))
out_utf-8.csv ('utf-8-sig', [['id', 'name'], ['1', '佐藤']])
out_utf-8-sig.csv ('utf-8-sig', [['id', 'name'], ['1', '佐藤']])
out_cp932.csv ('cp932', [['id', 'name'], ['1', '佐藤']])
UTF-8を先に試すのがポイントです。UTF-8は規則が厳しく、別の文字コードのファイルを誤って読めてしまうことが少ないためです(上で見たとおり、cp932 を先にするとUTF-8のファイルが文字化けしたまま読めてしまいます)。
対処2:errors= で止めずに読む
どうしても読めない数文字だけを置き換えて先に進みたいときは、errors= を指定します。cp932のファイルをあえて utf-8 で読んだ結果です。
| errors | 読めない部分の扱い | 実行結果(2行目) |
|---|---|---|
"strict"(既定) |
エラーにする | UnicodeDecodeError |
"replace" |
� に置き換える |
['1', '����'] |
"ignore" |
捨てる | ['1', ''] |
"backslashreplace" |
\x8d のように残す |
['1', '\\x8d\\xb2\\x93\\xa1'] |
errors= はデータを失う方法なので、正しい文字コードがわからないときの調査用や、多少の欠けを許容できる場合に使います。
対処3:文字コードを推定するライブラリ
charset-normalizer(pip install charset-normalizer)で推定できます。ただし短いファイルは推定を誤ることがあります。
from charset_normalizer import from_path
print(from_path("out_cp932.csv").best().encoding)
「佐藤」1語だけのcp932ファイルでは big5hkscs(中国語の文字コード)と推定されました。同じcp932で20行ほどの日本語を含むファイルでは cp932 と正しく推定されました。推定結果は目安として使い、候補(utf-8-sig / cp932)が決まっているなら対処1の方法が確実です。
区切り文字を変える:TSV・セミコロン
import csv
rows = [["id", "name"], [1, "ペン"]]
with open("items.tsv", "w", newline="", encoding="utf-8") as f:
csv.writer(f, delimiter="\t").writerows(rows)
print(open("items.tsv", "rb").read())
with open("items.tsv", newline="", encoding="utf-8") as f:
print(list(csv.reader(f, delimiter="\t")))
data = "id;price\n1;1,5\n" # 小数点がカンマの地域でよく使われる形式
print(list(csv.reader(data.splitlines(), delimiter=";")))
b'id\tname\r\n1\t\xe3\x83\x9a\xe3\x83\xb3\r\n'
[['id', 'name'], ['1', 'ペン']]
[['id', 'price'], ['1', '1,5']]
delimiter= に区切り文字を指定するだけで、TSV(タブ区切り)やセミコロン区切りも同じ書き方で扱えます。区切り文字がわからないときは csv.Sniffer で推定できます。
sample = "id|name|qty\n1|ペン|120\n"
dialect = csv.Sniffer().sniff(sample)
print(repr(dialect.delimiter), csv.Sniffer().has_header(sample))
'|' True
カンマ・改行・引用符を含む値とクォート指定
値の中にカンマや改行、" があっても、csvモジュールが自動で " で囲んでくれます。 quoting= を変えると囲み方を指定できます。同じ1行をそれぞれの設定で書き出した結果です。
import csv, io
row = [1, "ペン,黒", '5" 定規', "1行目\n2行目", None, ""]
for name in ["QUOTE_MINIMAL", "QUOTE_ALL", "QUOTE_NONNUMERIC", "QUOTE_STRINGS", "QUOTE_NOTNULL"]:
buf = io.StringIO()
csv.writer(buf, quoting=getattr(csv, name)).writerow(row)
print(f"{name:17}", repr(buf.getvalue()))
QUOTE_MINIMAL '1,"ペン,黒","5"" 定規","1行目\n2行目",,\r\n'
QUOTE_ALL '"1","ペン,黒","5"" 定規","1行目\n2行目","",""\r\n'
QUOTE_NONNUMERIC '1,"ペン,黒","5"" 定規","1行目\n2行目","",""\r\n'
QUOTE_STRINGS '1,"ペン,黒","5"" 定規","1行目\n2行目",,""\r\n'
QUOTE_NOTNULL '"1","ペン,黒","5"" 定規","1行目\n2行目",,""\r\n'
| quoting | 囲むもの | None |
空文字 "" |
使いどころ |
|---|---|---|---|---|
QUOTE_MINIMAL(既定) |
必要な値だけ | 空 | 空 | ふつうはこれで十分 |
QUOTE_ALL |
すべて | "" |
"" |
取り込み先が「全項目囲み」を求めるとき |
QUOTE_NONNUMERIC |
数値以外 | "" |
"" |
読み込み時に数値を自動で float にしたいとき |
QUOTE_STRINGS(3.12+) |
文字列 | 空 | "" |
None と空文字を区別したいとき |
QUOTE_NOTNULL(3.12+) |
None 以外 |
空 | "" |
同上(数値も囲む) |
QUOTE_NONE |
囲まない | - | - | escapechar が必要 |
値の中の " は "" と2つ重ねて表します(5" 定規 → "5"" 定規")。書き出したものを csv.reader で読み戻すと、['1', 'ペン,黒', '5" 定規', '1行目\n2行目', '', ''] と元の値に戻りました(None は空文字になります)。QUOTE_STRINGS と QUOTE_NOTNULL は Python 3.12 で追加されたため、3.11で実行すると AttributeError: module 'csv' has no attribute 'QUOTE_STRINGS' になりました。
QUOTE_NONE でカンマを含む値を書くと、エスケープ文字の指定を求められます。
csv.Error: need to escape, but no escapechar set
→ escapechar="\\" を指定すると 1,ペン\,黒 のように書き出されます。
QUOTE_NONNUMERIC を読み込み時に指定すると、囲まれていない値が float になります。
s = '"id","name","qty"\n1,"ペン",120\n'
print(list(csv.reader(io.StringIO(s), quoting=csv.QUOTE_NONNUMERIC)))
[['id', 'name', 'qty'], [1.0, 'ペン', 120.0]]
ヘッダーをスキップする・行番号を取る
import csv
with open("items.csv", newline="", encoding="utf-8") as f:
reader = csv.reader(f)
header = next(reader) # 1行目を取り出してスキップ
print("header:", header)
for row in reader:
print(row)
header: ['id', 'name', 'qty']
['1', 'ペン', '120']
['2', 'ノート', '45']
['3', 'クリップ', '300']
next(reader) で1行目を取り出すと、ループは2行目から始まります。 エラーの場所を知らせたいときは reader.line_num でファイル上の行番号がわかります(DictReader でも使えます。上のファイルでは「ペン」が2行目でした)。
読み込んだ値を数値に変換する
import csv
with open("items.csv", newline="", encoding="utf-8") as f:
rows = list(csv.DictReader(f))
print(rows[0], type(rows[0]["qty"]))
items = [{"id": int(r["id"]), "name": r["name"], "qty": int(r["qty"])} for r in rows]
print(items[0])
print(sum(i["qty"] for i in items))
{'id': '1', 'name': 'ペン', 'qty': '120'} <class 'str'>
{'id': 1, 'name': 'ペン', 'qty': 120}
465
空欄を int() すると ValueError: invalid literal for int() with base 10: '' になります。 空欄がありうる列は、変換用の関数を用意しておきます。
def to_int(s, default=None):
return int(s) if s.strip() else default
print(to_int(""), to_int(" 12 "))
None 12
日付の列は datetime.strptime()、金額のように誤差を避けたい列は decimal.Decimal() で変換します。
大きいCSVは1行ずつ処理する
for row in reader: で1行ずつ処理すれば、ファイルが大きくてもメモリはほとんど増えません。 100万行(約17.6MB)のCSVで、qty列の合計を求めたときのメモリのピーク(tracemalloc で計測)を比べました。
| 読み方 | 合計 | Pythonが確保したメモリのピーク |
|---|---|---|
for row in csv.DictReader(f):(1行ずつ) |
24500000 | 0.2MB |
rows = list(csv.DictReader(f))(全部読み込み) |
24500000 | 320.8MB |
計測なしで処理時間を測ると、次のとおりでした(Python 3.14.7、実行環境によって変わります)。
| 方法 | 時間 |
|---|---|
csv.reader(リスト) |
0.57秒 |
csv.DictReader(辞書) |
1.49秒 |
pd.read_csv()(pandas 3.0.6) |
0.33秒 |
DictReader は行ごとに辞書を作るぶん reader より時間がかかります。 件数が多く速さが必要なら reader か pandas が向いています。なお、1つの値が非常に長いCSVで _csv.Error: field larger than field limit (131072) が出た場合は、csv.field_size_limit() で上限を上げられます。
pandasとの対応表
集計・結合・並べ替えなど「表として加工する」ならpandasの read_csv / to_csv が便利です。
import pandas as pd
df = pd.read_csv("items.csv")
print(df)
print(df.dtypes)
df.to_csv("items_pd.csv", index=False, encoding="utf-8-sig")
実行結果(pandas 3.0.6):
id name qty
0 1 ペン 120
1 2 ノート 45
2 3 クリップ 300
id int64
name str
qty int64
dtype: object
pandasは数値の列を自動で int64 などに変換します。 pandas 3.0 では文字列の列が str 型として表示されました。to_csv の出力は b'\xef\xbb\xbfid,name,qty\n1,...' で、BOMが付き、行末は \n でした(pandasの行末は既定で実行するOSの改行、この環境ではLinuxの \n)。
| やりたいこと | csvモジュール | pandas |
|---|---|---|
| 読む | csv.reader(f) / csv.DictReader(f) |
pd.read_csv(p) |
| 書く | csv.writer(f).writerows(rows) |
df.to_csv(p, index=False) |
| 文字コード | open(..., encoding="cp932") |
read_csv(p, encoding="cp932") |
| 区切り文字 | delimiter="\t" |
sep="\t" |
| ヘッダーなし | csv.reader をそのまま使う |
header=None, names=[...] |
| 先頭の行を飛ばす | next(reader) |
skiprows=1 |
| 使う列だけ | 自分で取り出す | usecols=["id", "qty"] |
| 型を指定 | 自分で int() など |
dtype={"id": str} |
| 日付として読む | datetime.strptime() |
parse_dates=["created"] |
| 大きいファイル | for row in reader: |
chunksize=100_000 |
| クォート | quoting=csv.QUOTE_ALL |
quoting=csv.QUOTE_ALL |
| 行番号列を書かない | (もともと書かない) | index=False |
| 行末 | 既定 \r\n(lineterminator=) |
既定はOSの改行(lineterminator=) |
pandasで to_csv するときは index=False を付けると、先頭に行番号の列が入りません。id のような先頭ゼロを残したい列は dtype={"id": str} で文字列として読みます。
csvモジュールとpandasの使い分け
| 判断ポイント | csvモジュール | pandas |
|---|---|---|
| インストール | 不要(標準) | 必要 |
| 向いている処理 | 1行ずつの変換・振り分け・書き出し | 集計、結合、並べ替え、欠損値の処理 |
| メモリ | 1行ずつなら少ない | 基本は全体を読み込む(chunksize で分割可) |
| 型 | すべて文字列(自分で変換) | 自動で推定(意図しない変換に注意) |
| 配布・軽さ | スクリプト1つで動く | 依存が増える |
「読んで、変換して、書く」だけならcsvモジュール、「表として分析・加工する」ならpandasが目安です。
よくある失敗と直し方
| 症状・エラー | 原因 | 直し方 |
|---|---|---|
| Windowsで1行おきに空行が入る | newline="" がない |
open(..., newline="") |
| 値の中の改行が変わる | 読み込みで newline="" がない |
読むときも newline="" |
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x8d |
Shift_JIS系のファイル | encoding="cp932" |
| エラーなしで日本語が化ける | UTF-8を cp932 で読んだ |
encoding="utf-8-sig" |
KeyError: 'id'('id' になっている) |
BOM付きを utf-8 で読んだ |
encoding="utf-8-sig" |
| Excelで開くと文字化け | BOMなしUTF-8で書いた | encoding="utf-8-sig" で書く |
ValueError: dict contains fields not in fieldnames |
fieldnames にないキー |
fieldnames に追加、または extrasaction="ignore" |
ValueError: invalid literal for int() |
空欄や文字を数値変換 | 空欄を判定してから変換 |
計算がおかしい('120' + '45') |
値が文字列のまま | int() / float() で変換 |
よくある質問
Q. with open() を使わないといけませんか?
A. 使わなくても読み書きはできますが、with を使うと処理が終わったとき(エラー時も含む)に自動でファイルが閉じられます。 閉じ忘れると書き込んだ内容が反映されないことがあるので、with がおすすめです。
Q. ヘッダーがないCSVを辞書で読むには?
A. csv.DictReader(f, fieldnames=["id", "name", "qty"]) のように列名を渡します。1行目もデータとして読まれます。
Q. Excelで開く予定があるCSVは、utf-8-sigとcp932のどちらで書けばよいですか?
A. 今のExcelで開くなら utf-8-sig がおすすめです。絵文字や一部の記号など、cp932 では書けない文字も扱えます。取り込み先のシステムがShift_JISを指定している場合は cp932 にします。
Q. 文字列でCSVを組み立てる(",".join(row))のではだめですか?
A. 値にカンマ・改行・" が含まれると列がずれます。csvモジュールはこれらを自動で " で囲んでくれるので、csv.writer を使うのが確実です。
まとめ
- CSVは
open(p, newline="", encoding=...)で開き、csv.reader/csv.writerに渡すのが基本形 - 列名で扱うなら
DictReader/DictWriter。書くときはwriteheader()を忘れずに newline=""がないとWindowsで\r\r\nになり空行が入る。読み込みでも値の中の改行が変わる- Excelで開くCSVは
utf-8-sigで書く(先頭にEF BB BF)。UTF-8を読むときもutf-8-sigにするとBOMの有無に関係なく読める UnicodeDecodeErrorは文字コードの不一致。utf-8-sig→cp932の順に試す- カンマや改行を含む値は自動でクォートされる。
QUOTE_ALLなどで囲み方を指定できる - 大きいファイルは1行ずつ、集計・加工はpandasを使い分ける
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「PythonでCSVを読み書き|コピペ用テンプレートと文字化け対策」
株式会社RJCでは、CSVの入出力のような身近な処理も、実際のバイト列まで確かめながら仲間と学び合っています。こうした知識を共有しながら、日々技術を磨いています。
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。