11. ファイルの入出力#

11.1. ファイルへの書き込み#

ここでは、以下のオブジェクトの内容をテキストファイルとして書き出す例を示す。

d = {
    '東京': ['とうきょう', 'Tokyo'],
    '神奈川': ['かながわ', 'Kanagawa'],
    '千葉': ['ちば', 'Chiba'],
    '埼玉': ['さいたま', 'Saitama']
}

まず、インタプリタ上でこのオブジェクトの内容を表示するコードは以下の通り。

for ja, (yomi, en) in d.items():
    print(ja, yomi, en)
東京 とうきょう Tokyo
神奈川 かながわ Kanagawa
千葉 ちば Chiba
埼玉 さいたま Saitama

ファイルを書き込みモードで開くには、open関数の第1引数にファイル名、第2引数に'w'を指定し、戻り値としてファイルオブジェクトを受け取る。そして、print関数を呼び出すときに、fileという引数にファイルオブジェクトを渡す。ファイルへの書き出しが終わったら、close関数でファイルを閉じる。

fo = open('prefecture.txt', 'w')
for ja, (yomi, en) in d.items():
    print(ja, yomi, en, file=fo)
fo.close()

catコマンド(ファイルの内容を表示するコマンド)で出力内容を確認してみる。

!cat prefecture.txt
東京 とうきょう Tokyo
神奈川 かながわ Kanagawa
千葉 ちば Chiba
埼玉 さいたま Saitama

なお、close関数を呼び出すのを忘れると、ファイルに内容が書き込まれないことがある。

fo = open('prefecture2.txt', 'w')
for ja, (yomi, en) in d.items():
    print(ja, yomi, en, file=fo)

catコマンドを呼び出しても、書き出した内容が表示されない。

!cat prefecture2.txt

ここでclose関数を呼び出すと、ファイルに内容が書き込まれる。

fo.close()

今度はcatコマンドで書き出した内容が表示された。

!cat prefecture2.txt
東京 とうきょう Tokyo
神奈川 かながわ Kanagawa
千葉 ちば Chiba
埼玉 さいたま Saitama

ファイルの閉じ忘れを防ぐ構文としてwith文が便利。普段から以下のように記述しておくとよい。

with open('prefecture3.txt', 'w') as fo:
    for ja, (yomi, en) in d.items():
        print(ja, yomi, en, file=fo)
!cat prefecture3.txt
東京 とうきょう Tokyo
神奈川 かながわ Kanagawa
千葉 ちば Chiba
埼玉 さいたま Saitama

print関数の代わりに、ファイルオブジェクトのwrite関数を用い、文字列を書き出すこともできる。write関数を用いる場合は、改行が出力されないので、出力する文字列の中に改行を含める。

with open('prefecture4.txt', 'w') as fo:
    for ja, (yomi, en) in d.items():
        fo.write(f'{ja} {yomi} {en}\n')
!cat prefecture4.txt
東京 とうきょう Tokyo
神奈川 かながわ Kanagawa
千葉 ちば Chiba
埼玉 さいたま Saitama

11.2. ファイルからの読み込み#

ファイルを読み込みモードで開くには、open関数の第1引数にファイル名を指定するだけでよい(第2引数に'r'がデフォルトで指定される)。ファイルオブジェクトに対してfor文で反復処理を行うと、ファイルから1行ずつ読み出すことができる。ただし、読み出された各行(line)の末尾の改行は取り除かれない。

with open('prefecture.txt') as fi:
    for line in fi:
        print(line)
東京 とうきょう Tokyo

神奈川 かながわ Kanagawa

千葉 ちば Chiba

埼玉 さいたま Saitama

末尾の改行を取り除くには、strip関数などを用いる。

with open('prefecture.txt') as fi:
    for line in fi:
        print(line.strip('\n'))
東京 とうきょう Tokyo
神奈川 かながわ Kanagawa
千葉 ちば Chiba
埼玉 さいたま Saitama

各行はスペース区切りで値が格納されているので、split関数で値を取り出し、辞書にセットすることで、元のオブジェクトdを復元できる。

r = {}
with open('prefecture.txt') as fi:
    for line in fi:
        values = line.strip('\n').split(' ')
        r[values[0]] = values[1:]
r
{'東京': ['とうきょう', 'Tokyo'],
 '神奈川': ['かながわ', 'Kanagawa'],
 '千葉': ['ちば', 'Chiba'],
 '埼玉': ['さいたま', 'Saitama']}

11.3. CSV#

コンマ区切り形式(CSV)ファイルの読み書きには、csvモジュールが便利である。

import csv

以下のオブジェクトをCSV形式で書き出す例を示す。。

d = {
    '東京': ['とうきょう', 'Tokyo'],
    '神奈川': ['かながわ', 'Kanagawa'],
    '千葉': ['ちば', 'Chiba'],
    '埼玉': ['さいたま', 'Saitama']
}

CSVファイルに書き出すには、csv.writerオブジェクトを作成し、writerowメソッドなどを呼び出せばよい。

with open('prefecture.csv', 'w') as fo:
    writer = csv.writer(fo)
    for ja, (yomi, en) in d.items():
        writer.writerow((ja, yomi, en))
!cat prefecture.csv
東京,とうきょう,Tokyo
神奈川,かながわ,Kanagawa
千葉,ちば,Chiba
埼玉,さいたま,Saitama

CSVファイルを読み込むには、csv.readerを用いる。

r = {}
with open('prefecture.csv') as fi:
    reader = csv.reader(fi)
    for row in reader:
        r[row[0]] = row[1:]
r
{'東京': ['とうきょう', 'Tokyo'],
 '神奈川': ['かながわ', 'Kanagawa'],
 '千葉': ['ちば', 'Chiba'],
 '埼玉': ['さいたま', 'Saitama']}

11.4. JSON#

JavaScript Object Notation (JSON) は、オブジェクトの内容をJavaScriptとして解釈できる形式で表現するものである。オブジェクトをCSV形式で保存する場合、オブジェクトの構造と平坦化されたカンマ区切りのリストの間で読み書きの処理が必要であったが、JSON形式を用いると構造を保持したままオブジェクトのそのまま書き出すことができる。最近では、多くのプログラミング言語でJSONを読み書きできるようになっているため、プログラミング言語に依存せずにデータのやり取りを行うために用いられる。JSON形式の読み書きを行うには、jsonモジュールをロードする。

import json

以下のオブジェクトをJSON形式で書き出す例を示す。。

d = {
    '東京': ['とうきょう', 'Tokyo'],
    '神奈川': ['かながわ', 'Kanagawa'],
    '千葉': ['ちば', 'Chiba'],
    '埼玉': ['さいたま', 'Saitama']
}

オブジェクトをJSON形式でファイルに書き出すには、json.dump関数に書き出したいオブジェクトを渡せばよい。

with open('prefecture.json', 'w') as fo:
    json.dump(d, fo)

デフォルトでは日本語などのASCIIではない文字列がエスケープされてしまうので、書き出された内容が読みづらくなることがある。

!cat prefecture.json
{"\u6771\u4eac": ["\u3068\u3046\u304d\u3087\u3046", "Tokyo"], "\u795e\u5948\u5ddd": ["\u304b\u306a\u304c\u308f", "Kanagawa"], "\u5343\u8449": ["\u3061\u3070", "Chiba"], "\u57fc\u7389": ["\u3055\u3044\u305f\u307e", "Saitama"]}

そのようなときは、json.dump関数の引数にensure_ascii=Falseを渡せばよい。

with open('prefecture.json', 'w') as fo:
    json.dump(d, fo, ensure_ascii=False)

書き出されたJSONの表記は、ほぼPythonのオブジェクトの表記と同じである。

!cat prefecture.json
{"東京": ["とうきょう", "Tokyo"], "神奈川": ["かながわ", "Kanagawa"], "千葉": ["ちば", "Chiba"], "埼玉": ["さいたま", "Saitama"]}

さらに、indent引数にインデントの文字数を設定すると、書き出されたJSON表記が読みやすくなる。

with open('prefecture.json', 'w') as fo:
    json.dump(d, fo, ensure_ascii=False, indent=2)
!cat prefecture.json
{
  "東京": [
    "とうきょう",
    "Tokyo"
  ],
  "神奈川": [
    "かながわ",
    "Kanagawa"
  ],
  "千葉": [
    "ちば",
    "Chiba"
  ],
  "埼玉": [
    "さいたま",
    "Saitama"
  ]
}

JSONファイルを読み出すには、json.load関数を呼び出せばよい(これまでにJSON形式で書き出した全ファイルは、JSONとして解釈すれば同じ内容のオブジェクトとして読み出される)。

with open('prefecture.json') as fi:
    r = json.load(fi)
r
{'東京': ['とうきょう', 'Tokyo'],
 '神奈川': ['かながわ', 'Kanagawa'],
 '千葉': ['ちば', 'Chiba'],
 '埼玉': ['さいたま', 'Saitama']}

11.5. 文字コードを指定したファイルオブジェクト#

東京都のくらしと統計2019 運輸・観光のウェブサイトから主な駅の乗車人員数(平成28年度)のCSVファイルをダウンロードする。ダウンロードにはwgetというコマンドを利用している。

!wget -O station.csv https://www.toukei.metro.tokyo.lg.jp/kurasi/2019/csv/ku19rv1810.csv
--2021-11-23 16:36:38--  https://www.toukei.metro.tokyo.lg.jp/kurasi/2019/csv/ku19rv1810.csv
Resolving www.toukei.metro.tokyo.lg.jp (www.toukei.metro.tokyo.lg.jp)... 23.210.219.59, 23.210.219.17
Connecting to www.toukei.metro.tokyo.lg.jp (www.toukei.metro.tokyo.lg.jp)|23.210.219.59|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 847 [text/csv]
Saving to: ‘station.csv’

station.csv         100%[===================>]     847  --.-KB/s    in 0s      

2021-11-23 16:36:39 (147 MB/s) - ‘station.csv’ saved [847/847]

catコマンドを使ってダウンロードしたファイルの内容を確認する(文字化けすることはよくある)。

!cat station.csv
1,���炵�Ɠ��v2019,,,,
2,,�^�A�E�ό��|�P�@��ȉw�̏�Ԑl�����i����28�N�x�j,,,
3,,��1�j  �u�V�h�v�͐����V�h�w���܂ށB,,,
4,,��2�j  �u���E�~�c�v�̒n���S��Ԑl�����F�������i1���j��1�N�Ɋ��Z��������,,,
5,,����  �s�����Ǔ��v���u�����s���v�N�Ӂv�iJR�����{�A���S�e�ЁA�����n���S�A�s��ʋǒ񋟎����j�A���l�s�u��96�񉡕l�s���v���v�A���É��s�u����29�N�Ŗ��É��s���v�N�Ӂv�A���s�u����29�N���s���v���v,,,
6,�w��,JR�i��l�j,���S�i��l�j,�n���S�i��l�j,�v�i��l�j
7,����,160437,�\,36393,196830
8,�i��,135702,50555,�\,186257
9,�a�J,135538,274312,188656,598506
10,�V�h,280797,265673,131299,677769
11,�r��,204371,176983,99835,481189
12,���,66683,9329,37385,113397
13,�H�t��,90017,23085,22272,135374
14,���l(�_�ސ쌧),151359,236087,24923,412370
15,�����i���m���j,76253,80884,69206,226343
16,���E�~�c�i���{�j,157513,141110,129076,427699

ファイルの文字コードはおそらくShift_JIS (cp932) だろうと推測し、iconvで文字コードを変換してみる。

!cat station.csv | iconv -f cp932
1,くらしと統計2019,,,,
2,,運輸・観光-1 主な駅の乗車人員数(平成28年度),,,
3,,注1)  「新宿」は西武新宿駅を含む。,,,
4,,注2)  「大阪・梅田」の地下鉄乗車人員数:調査日(1日)を1年に換算したもの,,,
5,,資料  都総務局統計部「東京都統計年鑑」(JR東日本、私鉄各社、東京地下鉄、都交通局提供資料)、横浜市「第96回横浜市統計書」、名古屋市「平成29年版名古屋市統計年鑑」、大阪市「平成29年大阪市統計書」,,,
6,駅名,JR(千人),私鉄(千人),地下鉄(千人),計(千人)
7,東京,160437,―,36393,196830
8,品川,135702,50555,―,186257
9,渋谷,135538,274312,188656,598506
10,新宿,280797,265673,131299,677769
11,池袋,204371,176983,99835,481189
12,上野,66683,9329,37385,113397
13,秋葉原,90017,23085,22272,135374
14,横浜(神奈川県),151359,236087,24923,412370
15,名古屋(愛知県),76253,80884,69206,226343
16,大阪・梅田(大阪府),157513,141110,129076,427699

open関数では、ファイルの読み書き時に使用する文字コードをencodingパラメータで指定できる。

with open('station.csv', encoding='cp932') as fi:
    for line in fi:
        print(line.strip('\n'))
1,くらしと統計2019,,,,
2,,運輸・観光-1 主な駅の乗車人員数(平成28年度),,,
3,,注1)  「新宿」は西武新宿駅を含む。,,,
4,,注2)  「大阪・梅田」の地下鉄乗車人員数:調査日(1日)を1年に換算したもの,,,
5,,資料  都総務局統計部「東京都統計年鑑」(JR東日本、私鉄各社、東京地下鉄、都交通局提供資料)、横浜市「第96回横浜市統計書」、名古屋市「平成29年版名古屋市統計年鑑」、大阪市「平成29年大阪市統計書」,,,
6,駅名,JR(千人),私鉄(千人),地下鉄(千人),計(千人)
7,東京,160437,―,36393,196830
8,品川,135702,50555,―,186257
9,渋谷,135538,274312,188656,598506
10,新宿,280797,265673,131299,677769
11,池袋,204371,176983,99835,481189
12,上野,66683,9329,37385,113397
13,秋葉原,90017,23085,22272,135374
14,横浜(神奈川県),151359,236087,24923,412370
15,名古屋(愛知県),76253,80884,69206,226343
16,大阪・梅田(大阪府),157513,141110,129076,427699