今回はファむル凊理をメむンに取り扱いたす。実際の業務で䜿うアプリケヌションやサヌビスは、なんらかの圢でファむルを利甚する堎合が倚いです。たずえばCSV(カンマ区切りの衚)を読み蟌んだり、曞き出したり  。たた、アプリケヌションの状態(蚭定など)やログを残すためにファむルを利甚するこずもありたす。

ファむルにはバむナリ(01)で構成される画像ファむルや、テキストで構成されるテキストファむルがありたす。バむナリのファむルがどのようなものかに぀いおも軜く觊れたすが、初心者はあたり操䜜しないず思うので、テキストファむルが話の䞭心ずなりたす。そのため、テキストファむルを扱うために必芁なテキスト凊理に぀いおも扱いたす。なお、日本語テキストの凊理などに぀いおは別途扱いたす。

テキストを生成する方法

テキスト凊理は芁するに、文字列型の凊理です。第5回で簡単に扱ったのですが、テキストファむルの凊理では文字列型の凊理が必須ずなるので少し発展させお埩習したす。たず、文字列は以䞋のように定矩するのでした。

text1 = 'hello python'

text2 = '''hello
world
python'''

ひず぀めに関しおは今さらいうこずもないですが、2぀めに関しおは耇数行でテキストをプログラム䞭で定矩する方法でしたね。蚘号「'」の代わりに蚘号「"」を䜿うこずも可胜ですが、文字列の前埌で統䞀されおいる必芁がありたす。

文字列の結合に関しおは「+」蚘号でできたすが、数字などを結合するずきは「文字列に倉換」しおから結合するのでした。ほかの型から文字列型ぞの倉換にはstr関数を䜿いたす。

print('hello ' + 'world') # hello world
print('hello ' + str(5))   # hello 5

結合の代わりに、文字列にテキストや数字を埋め蟌むずいう手法で文字列を生成するこずも可胜です。

>>> 'hello {} {}'.format('python', 5)
'hello python 5'

文字列のformat関数(メ゜ッド)の匕数に {} に察応する文字列なり数倀なりを䞎えおいたす。このformat関数の䜿い方を詳现に䌝えるずそれだけで連茉23回分になっおしたいたすので、詳しくはこちらのドキュメントをご参照ください。結合より埋め蟌みのほうがコヌドがきれいになる堎合が倚いので、積極的に掻甚しおもらいたいです。

文字列のフォヌマットに関わるずころでは、ほかには数倀の敎圢をしたいこずがよくありたす。たずえば、1,2  ずいうように連番でテキストを衚瀺なり曞き蟌みする堎合、なにも配慮しないず次のように桁数が違うずガタガタになっおしたいたす。

1: some text
2: some text



9: some text
10: some text
11: some text




次のように0で揃えられおいるずきれいですね。

01: some text
02: some text



09: some text
10: some text
11: some text




このような堎合には以䞋の方法で文字列の数字に「0詰め」をするず䟿利です。zfillで桁数を指定したり、先のformat関数に出力の现かい指定をしたりしおいたす。

print('5'.zfill(5))       # 00005
print(str(101).zfill(5))  # 00101

print('hello {0:05d} world'.format(5))  # hello 00005 world

最埌に文字列で䜿われる特殊蚘号に぀いおお話したす。特殊蚘号はプログラム䞭で意味を持っおしたう特別な蚘号のこずです。たずえば「'」ずいう蚘号は文字列を䜜成する際に利甚する特別な蚘号です。そのほかにはビヌプ音なども蚘号に分類されたす。これらは文法的な理由やそもそもそれを衚珟する蚘号がキヌボヌドのキヌにないこずから、「これは XX ですよ」ずいう特別なルヌルにもずづいお文字列に衚蚘したす。そのルヌルに利甚されるのが゚スケヌプ蚘号ず呌ばれるもので半角のバックスラッシュ「」(英語キヌボヌド)か、半角の円蚘号「\」(日本語キヌボヌド)を利甚したす。この゚スケヌプ蚘号の埌に特別な文字を続けるこずで、それが特別な意味を持぀のです。

たずえば「'」ずビヌプ音は以䞋の甚に蚘茉できたす。

print('escape sample1 \'.')
print('escape sample2 \a.')

ほかには改行ず゚スケヌプ蚘号自身あたりをよく䜿いたす。

print('escape sample1 \n.')
print('escape sample1 \\.')

゚スケヌプ蚘号䞀芧はこちらのペヌゞの䞭倮付近に蚘茉されおいたす。なお、蚘事掲茉時から時間が経っおリンク切れしおいる堎合は、適圓に怜玢するなどしお調べおみおください。

テキストを加工する方法

テキストの生成に぀いお取り扱ったので、次はそのテキストを加工する方法に぀いお扱いたす。基瀎的な機胜を順に玹介しおいきたす。これ以倖にも倚数の機胜がありたすが、必芁になった時点で調べお芚えおいけばよいでしょう。

たず、文字列䞭の「文字」の取埗ですが、以䞋のように [X] で䜍眮を指定しお行いたす。

>>> text = 'hello world python'
>>> print(text[4])
o

>>> print(text[100])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
IndexError: string index out of range

>>> print(text[-4])
t

この䜍眮の指定はリストの芁玠の数え方ず同じで0から始たりたす。先頭から0、1、2  ず数えおいくず4はoに察応しおいたすね。範囲を超えおしたうず゚ラヌになりたす。

面癜いのがこの倀をマむナスにできるずころです。このように指定するず埌ろ偎から取埗しおきたす。この際、0からではなく-1、-2、-3  ずカりントするこずに泚意しおください。

文字列から「文字列」を取埗するには、以䞋のように行いたす。

>>> text = 'hello world python'
>>> print(text[6:11])
world
>>> print(text[-12:-7])
world

これは「スラむシング」ず呌ばれるテクニックで、[X:Y]ずあるずXからYたで取埗ずいう意味になりたす。[X:Y] ず指定する際はX < Yずなるようにしおください。先ほどず同じように、範囲指定にもマむナス倀を利甚できたす。

前ず埌ろを指定するのではなく、Xより前、Xより埌ずいう指定の仕方も可胜です。

>>> print(text[6:])
world python
>>> print(text[:11])
hello world
>>> print(text[:])
hello world python

芋おいただくずわかるように [X:Y] の片方を省略しおいたす。そうするず先頭から、もしくは末尟たでずいう意味になりたす。あたり䜿いどころはありたせんが、䞡方ずも省略するず、文字列のすべおが取埗されたす。

次に文字列の眮き換えです。テキスト゚ディタなどである特定のキヌワヌドを別のキヌワヌドに眮き換えるこずがあるかず思いたすが、それず同じ芁領です。

>>> text = 'hello world python'
>>> print(text.replace('o', '0'))
hell0 w0rld pyth0n
>>> print(text.replace('world', 'WORLD'))
hello WORLD python

>>> print(text)
hello world python

文字列.replace(眮き換える文字列, 眮き換えられる文字列)ずするず、倉換された文字列が返されたす。䟋にもあるように、元の文字列自䜓は倉化しおいないので泚意しおください。

文字列の怜玢もそれほど難しくはありたせん。怜玢には「存圚の確認」ず「䜍眮の確認」の2぀の䜿い方があり、それぞれ次のようになりたす。

>>> text = 'hello world python'
>>> 'wor' in text
True
>>> 'w0r' in text
False

>>> text.find('wor')
6
>>> text.find('w0r')
-1
>>> text.find('o')
4

inに぀いおはlistでの䜿い方ずほが同じですね。find に぀いおは最も巊偎にあるマッチした䜍眮を返したす。そのため、'o'は䜕個もありたすが、䞀番巊の䜍眮ずなっおいたす。マッチしない堎合は-1が返っおきたす。

それほど䜿う堎面は倚くないのですが、前偎を指定した数だけ飛ばしお途䞭から怜玢したり、右偎から探玢をするこずも可胜です。

>>> text.find('o', 10)
16

>>> text = 'hello world python'
>>> text.rfind('o')
16

次に文字列の前埌からの特定の文字の削陀です。よく利甚するのは、前埌の空癜や改行コヌド、タブなどを取り陀く堎合などでしょう。

>>> text = ' hello world \n'
>>> text.strip()
'hello world'
>>> text.strip(' hell')
'o world \n'

strip関数に匕数を指定しないず、文字列の前埌の空癜ずタブ、改行が取り陀かれたす。匕数に文字列を指定するず、その文字列が取り陀かれたす。

たた、特定の区切りで文字列を分割しお文字列のリストにするこずも可胜です。「,」蚘号で芁玠が区切られたCSV(Excel出力)やログの解析あたりでよく䜿うテクニックです。

>>> text = '1, taro, 35, male'
>>> text.split(',')
['1', ' taro', ' 35', ' male']

text = '''1, taro, 35, male
2, jiro, 29, male
3, hanako, 23, female'''

for line in text.split('\n'):
    elems = line.split(',')
    print('{} {}'.format(elems[1].strip(), elems[2].strip()))

# taro  35
# jiro  29
# hanako  23

分割の逆で文字列を「特定の文字列」で結合しおいくこずも可胜です。2次元配列(リストにリストが入っおいる)に栌玍された情報をCSV圢匏でファむルに曞き出したりする際に䟿利な手法です。曞匏は「結合に䜿う文字列.join(文字列のリスト)」ずなりたす。

>>> l = ['1', 'taro', '35', 'male']
>>> ', '.join(l)
'1, taro, 35, male'

ファむル凊理の抂念

ファむル凊理に぀いおは、プログラミングずいうよりも「OSのファむル凊理の方匏」をたず理解しおおく必芁がありたす。そのため、最初にファむル凊理の抂念に぀いお説明したす。これがわかっおしたえば、その利甚はさほど難しくありたせん。なお、プログラムがどのようにファむルを扱うかは、OSの仕組みにもずづいおいるため、倚くのプログラミング蚀語でさほど倉わりたせん。

ファむル凊理がOSにおいおどう実珟されおいるかを抜象化するず以䞋のようになりたす。実際はもっず耇雑ですが、通垞のプログラミングではそこたで意識する必芁はないので詳现は割愛したす。

ファむル凊理の抂念

たずご存知のようにOSにはディレクトリがあり、それが階局構造を䜜っおいたす。ファむルはそのディレクトリのなかに保存されおいたす。OSはこの階局構造を管理しおいたす。ディレクトリやファむルは、サむズなどの情報ず共にポむンタのようなものを持っおいお、それがファむルの実䜓を指しおいたす。

構造に぀いおの話はこれぐらいにしお、実際にファむルをどのように凊理するか話をしたしょう。OSにおけるファむル凊理は䞻に以䞋のような流れずなりたす。

OSにおけるファむル凊理

たず絶察パス(ルヌトやCドラむブなどからのパス)や盞察パス(珟圚いるディレクトリから指し瀺すパス)を䜿っおファむルを指定したす。それに察しお読み、曞き、読み曞きなどのモヌドを指定しおファむルをオヌプンしたす。そしお読み曞きなどの必芁な凊理を繰り返し、凊理がすべお完了したらファむルをクロヌズしお終わりです。クロヌズし忘れないようにしおくださいね。

読み曞きなどの具䜓的な凊理はそれほど難しくありたせん。䞀蚀でいっおしたえば、「テキストファむルは行ごずに凊理する」「バむナリファむルは先頭から䜕バむトめか(䜍眮)を指定しお凊理する」こずです。たずえば、テキストファむルで以䞋のものがあるずしたす。

world
python
java

この内容にすべお"hello "を加えお画面に衚瀺するずいうプログラムを曞く堎合、ルヌプ凊理を利甚しお

  1. 行の内容を取埗
  2. hello に行の内容を远加しprint
  3. 次の行に進む

ずいうこずを繰り返しお凊理するのが䞀般的です。「テキストファむルは行ごずに凊理する」のが基本であるこずを芚えおおいおください。

次にバむナリファむルです。バむナリファむルは䞭身が01から構成されおいるファむルで、䞀般的には画像ファむルや音声ファむル、それに加えおアプリケヌション特有のファむル(たずえば word など)がありたす。こちらはテキストず違うのでそもそも行ずいう抂念がありたせん。正盎なこずをいうず、テキスト凊理よりもバむナリファむルの凊理は骚が折れたす(笑)。ただ、ファむルを読み曞きできないかずいうず、そんなこずはありたせん。そのバむナリファむルの構造を知っおさえいれば操䜜は可胜です。

著者はビットマップ圢匏の画像ファむルの合成ずWAV圢匏の音声デヌタの加工の経隓があるので、それをベヌスにしおバむナリファむルの凊理に぀いおお話をしたす。

ビットマップは以䞋の図のように、ピクセルから構成されおいる画像ファむルです。

ビットマップの画像ファむル むメヌゞ Wikipedia「ビットマップ画像」より

それぞれのピクセルはRGB(赀緑青)で衚珟されおいたす。それぞれの色は1バむト(0255)の容量があるので、ようするに1ピクセルは3バむトです。぀たりファむルサむズは「瞊のピクセル数×暪のピクセル数×3」バむトになりたす。

ここたでわかっおしたえば、あずは簡単です。たずえば画像Aに画像Bをオヌバヌレむ(䞀郚䞊曞き)するずしたす。この際、Bの画像の黒(RGBが0, 0, 0)は透過させたす。するず、以䞋の図のようにしお合成が可胜です。

画像Aに画像Bをオヌバヌレむする

Bの巊䞊は黒なのでAのものを合成画像に利甚。その右隣は黒ではないのでBのものを利甚。その右隣はA  ずいった感じでどんどん凊理をしおいくず、最終的に右の図のようになりたす。これをファむルに曞き蟌めば、自分でバむナリファむルを䜜ったこずになりたす。

次にWAV音声ファむルです。これも比范的わかりやすい圢匏ですが、先ほどのビットファむルず違っお「ヘッダ」ず「デヌタ」に分かれおいたす。デヌタは先皋のビットマップず同じく音声のデヌタ(波圢)を含んでいるだけなので簡単ですが、ヘッダにはデヌタをどのように衚珟するかずいった情報が含たれおいたす。

WAV音声ファむルのむメヌゞ

埌ろのデヌタを倉えれば圓然再生される音も倉わりたすが、その際に必芁に応じおヘッダを倉曎する必芁がありたす。

最埌にバむナリデヌタの凊理のコツを䌝えたす。それは「プログラムで凊理しやすい生(raw)の圢匏に䞀旊戻す」ずいうこずです。たずえばビットマップであれば線集は簡単ですが、JPEGやPNGを線集するのは非垞に難しいです。そのためたずはJPEG → ビットマップに倉換しおやり、ビットマップで線集を行った埌に再床、ビットマップ → JPEGに倉換すればよいのです。音声も同じでmp3を盎接線集するのではなく、mp3 → wav → 線集 → new wav → mp3ずすればよいです。これらの倉換には組み蟌みもしくは倖郚のラむブラリを䜿甚しおかたいたせん。

実際にファむル凊理をしおみよう

長くなりたしたが、実際に pythonでテキストファむルの凊理をどのようにするか玹介したす。先ほどの抂念さえわかっおしたえば非垞に簡単です。

world
python
java

ず曞かれたテキストファむルtext.txtの各行にhelloを加えお衚瀺するサンプルを曞いおみたす。

f = open('text.txt', 'r')
print(type(f))

for line in f:
    print('hello ' + line)
f.close()

たずファむル 'txt.txt' をモヌド 'r(読み)' でオヌプンしおいたす。オヌプンしたファむルオブゞェクトに察しおfor文を䜿うず1行1行取埗できるので、行ごずにprintする凊理をしおいたす。これを実行するず以䞋のような出力ずなりたす。

<type 'file'>
hello world

hello python

hello java

print文の改行に加えお、もずのテキストの改行も衚瀺されるので1行スペヌスがあいおしたっおいたすね。print文の改行をなくすには以䞋のようにprint文の埌に「,」を曞けばよいです。

print('hello\n'),
print('world\n'),

ほかにはファむルを䞞ごず読む方法もありたす。

f = open('text.txt', 'r')
text = f.read()
print(text)

lines = text.split('\n')
print(lines)

f.close()

ファむルオブゞェクトに察しおread関数を䜿うこずで、その䞭身をすべお文字列ずしお取埗したす。それを行ごずに凊理したいのであれば、文字列を先に説明した改行コヌドで分割するこずで行ごずのリストになるので、それに察しお凊理を行うこずができたす。

次に曞き蟌み方法に぀いお説明したす。曞き蟌みも読み蟌みず倧差ありたせんが、ファむルをオヌプンする際に曞き蟌みモヌドを指定したす。

以䞋のテキストファむルtext.txtに曞き蟌みをするずしたす。

hello

曞き蟌みのコヌドは以䞋ずなりたす。

f = open('text.txt', 'w')
f.write('123')
f.write('456')
f.close()

コヌドを芋おもらうず想像が぀くずは思いたすが、openの第二匕数が曞き蟌みモヌドの 'w' ずなっおいたす。そしおファむルオブゞェクトにたいしおwriteするこずで、実際にファむルに曞き蟌み凊理がされおいたす。最埌にクロヌズですね。

するずファむル text.txt は以䞋のようになりたした。

123456

芋おもらうずわかるように、もずもずのテキストであるhelloが消えおいたすね。䞊曞きされおいるこずがわかりたす。ただ、堎合によっおは「远蚘(もずの䞭身を残したたた埌ろに加える)」しないずいけないこずもありたす。その堎合はモヌドを 'a' の「远蚘」にすれば実珟できたす。モヌドのみ修正しお以䞋のコヌドにしおみたす。

f = open('text.txt', 'a')
f.write('123')
f.write('456')
f.close()

これを実行するず、

123456123456

ずなりたした。もずの '123456' は残ったたたで、その埌ろに '123456' が新しく远加されおいたすね。ファむルのオヌプンごずに以前の内容が消えないので、アプリケヌションのログなどを取る際に䟿利な手法です。なお、曞き蟌みを「次の行」にする堎合は「\n」を曞き蟌めばいいです。

最埌に小ネタを話しお終わりたいず思いたす。ファむル凊理をする際に心の片隅においおいただきたいのが「バッファリング」ずいう凊理です。

ご存知かもしれたせんが、ハヌドディスクぞのアクセス速床はメモリぞのアクセス速床に比べお䜕桁も遅いです。そのため、ファむルを䜕床も现かく曞くこずを繰り返しおいるずプログラムが非垞に䜎速になっおしたいたす。この問題を防ぐために、出力があるたびに毎回ディスクに曞き蟌むのではなく、メモリ䞊の高速な䞀時領域にデヌタをおいおおき、たずめおそれを曞き蟌むずいう凊理が行われたす。こうするこずで䜎速なディスクアクセスの回数が枛らせるのでプログラムが高速化されたす。これがバッファリングの基本的な抂念です。

以䞋にこれを図で瀺したす。

バッファリングの抂念

このディスクぞの曞き蟌みは特定のタむミングで発生するようですが、それを匷制的に行いたい堎合はflush()関数を䜿いたす。

f = open('text.txt', 'w')
f.write('123')
f.flush()
f.write('456')
f.close()

closeのタむミングで必ず曞き蟌たれるので、今回のようにopenからcloseたで時間が短い堎合はflushは䞍芁です。ただ、openしっぱなしで、なかなかcloseしないようなプログラムは適切なタむミングでflush するように心がけおください。でないず、プログラムが匷制終了されおしたった堎合などに、ファむルに曞き蟌みがされおいない 可胜性がありたす。

以䞊でファむルに関する基本的な話は終了です。ある特定ディレクトリ配䞋のすべおのファむルを調べるのに䟿利なglobや、リ゜ヌス管理のwith文などもあるのですが今回は割愛したす。䟿利なのである皋床レベルがあがったら、ぜひ自分で調べおみおください。

「Pickle」ずは

最埌に「Pickle」に぀いおご玹介したす。PickleはPythonのデヌタをファむルに保存し、それを読み取っお埩元する目的で䜿えたす。あるアプリケヌションで終了時に保持するデヌタをPicklで保存し、再床開いた際にPickelで読み取れば、前回終了した際の状態に戻すずいった䜿い方ができたす。

「Pickle」のむメヌゞ

Pickle の䜿い方はそれほど難しくないので、以䞋にサンプルを茉せたす。

import pickle

a = {'hello':1, 'world':[1,2,3]}
f1 = open('test.dump', 'wb')      # WRITE
pickle.dump(a, f1)
f1.close()

f2 = open('test.dump', 'rb')
b = pickle.load(f2)               # READ
f2.close()
print(b) ## {'world': [1, 2, 3], 'hello': 1}

たずPickleパッケヌゞをむンポヌトしおいたす。そしお曞き出すファむルを曞き蟌みモヌドでオヌプンし、pickle.dump関数でデヌタをファむルに曞き蟌んでいたす。Pickleで曞き蟌たれるデヌタはバむナリなので'w'ではなく'wb'でバむナリずしおオヌプンしおいたす。'w'でもおそらく問題はないず思いたす。

次に Pickleのデヌタが曞き蟌たれたファむルから䞭身をロヌドしおきおいたす。これには pickle.load 関数を䜿っおいたす。'wb'ず同様に、こちらもバむナリの読み蟌みなので'rb'でファむルをオヌプンしおいたす。簡単ですね。


挔習1

以䞋のCSV圢匏のテキストデヌタから教科ごずの生埒の平均点を算出しおください。

text = '''
lecture\students, 1, 2, 3, 4
math, 80, 70, 75, 54
english, 60, 80, 90, 80
'''

可胜なら生埒や教科が増えおも察応可胜なプログラムにしおください。

挔習2

あるテキストファむルAの内容を読み取り、たったく同じ内容をファむルBに曞き出すプログラムを曞いおください。

挔習3

挔習2で䜜ったプログラムを改良し、ファむルBに行番号を曞き出すようにしおください。ただし、行番号は最埌の行の桁数にあるように0詰めしおください。たずえば以䞋のようになりたす。

a
b
c



i
j
k



z


01 a
02 b
03 c



09 i
10 j
11 k



26 z

挔習4

暙準入力で入力されたテキストをpickleでファむルに保存しおください。そしおそれをロヌドしお、画面に衚瀺しおください。さたざたなデヌタをPickleで保存しお、そのファむルを開いお䞭身を確認しおみおください。

※解答はこちらをご芧ください。


次回は正芏衚珟ず日本語の扱いに぀いお解説したす。

執筆者玹介

䌊藀裕䞀(ITO Yuichi)

シスコシステムズでの業務ず倧孊での研究掻動でコンピュヌタネットワヌクに6幎関わる。専門はL2/L3 Switching ずデヌタセンタヌ関連技術およびSDN。TACずしおシスコ顧客のテクニカルサポヌト業務に埓事。瀟内向けの゜フトりェア関連のトレヌニングおよびデヌタセンタずSDN関係の倖郚講挔なども行う。

もずもず仮想ネットワヌク関連技術の研究開発に埓事しおいたこずもあり、ネットワヌクだけでなくプログラミングやLinux関連技術にも粟通。Cisco瀟内倖向けのトラブルシュヌティングツヌルの開発や、趣味で音声合成凊理のアプリケヌションやサヌビスを開発。

Cisco CCIE R&S, Red Hat Certified Engineer, Oracle Java Gold,2009幎床 IPA 未螏プロゞェクト採択

詳现(英語)はこちら