Part 2: テキストファイルの取り扱い

列オブジェクト

列オブジェクトは、添字で参照される要素から成るオブジェクトです:リスト (list)、配列 (array)、テキスト文字列、などがあります。 列オブジェクト seq の要素は、添字により得られます: seq[0], seq[1] などです。 また、終端の方から添字付けすることも可能です:すなわち、seq[-1] は列の最後の要素、seq[-2] は最後から2番目の要素、といった具合です。

例:

text = 'abc'
print text[1]
print text[-1]

部分列は、スライス (slicing) によって取り出せます: seq[0:5] は、最初の五つ(添字 0, 1, 2, 3, 4)の要素から成る部分列であり、添字 5 の要素は含みません。 負の添字も使えます:seq[1:-1] は、列の最初と最後の要素を取り除いたものです。

例:

text = 'A somewhat longer string.'
print text[2:10]
print text[-7:-1]

列の長さは len(seq) によって得られます。

リスト

リストは、任意のオブジェクト(数値、文字列、ベクトル、他のリストなど)を要素とすることのできる列です:

some_prime_numbers = [2, 3, 5, 7, 11, 13]
names = ['Smith', 'Jones']
a_mixed_list = [3, [2, 'b'], Vector(1, 1, 0)]

リストの要素や部分列は、新たに値を代入することによって変更可能です:

names[1] = 'Python'
some_prime_numbers[3:] = [17, 19, 23, 29]

list.append(new_element) によって、新しい要素をリストの最後に追加することができます。リストは、list.reverse() によって反転したり、list.sort() によってソートしたりできます。

テキスト文字列の場合と同様に、二つのリストを結合できます:[0, 1] + ['a', 'b'][0, 1, 'a', 'b'] となります。

リストの反復も、テキスト文字列の場合と同様です:3*[0][0, 0, 0] を与えます。

タプル

タプルはリストと非常に良く似ていますが、変更ができないという点が異なります。 タプルは一度生成されたら同じ要素を保ちます。したがって、変更可能な列を使っても意味のない場合に有用です(例えば、データベースのキー (key) として)。

例:

tuple_1 = (1, 2)
tuple_2 = ('a', 'b')
combined_tuple = tuple_1 + 2*tuple_2

列に関するループ

列の各要素について、ある演算を繰り返す必要がしばしば生じます。これを、列に関するループと呼びます。

for prime_number in [2, 3, 5, 7]:
    square = prime_number**2
    print square

注意:字下げ (indentation) によって、一連の演算行がそのループ内にあることが示されます。

ループは任意の列に対して使えます。これは、テキスト文字列を用いた例です:

for vowel in 'aeiou':
    print 10*vowel

ある数範囲に関するループは、一つの特別な場合に過ぎません:

from Numeric import sqrt

for i in range(10):
    print i, sqrt(i)
range(n) という関数は、初めの n 個(すなわち 0 から n-1)の整数を要素とするリストを返します。range(i, j) は i から j-1 までの全ての整数を、range(i, j, k) は i, i+k, i+2*k, などを j-1 まで生成して返します。

条件判断

最も多く用いられる条件判断は、一致と大小関係です:
equal a == b
not equal a != b
greater than a > b
less than a < b
greater than or equal a >= b
less than or equal a <= b

複数の条件を andor で結合したり、not で否定したりできます。条件判断の結果は、"真" の場合は 1、"偽" の場合は 0 を返します。

条件判断は、分岐において最も多く使われます:

if a == b:
    print "equal"
elif a > b:
    print "greater"
else:
    print "less"

elif 分岐は幾つあっても(無くても)良いし、else 分岐はあっても無くても構いません。

条件判断は、ループの制御にも使えます:

x = 1.
while x > 1.e-2:
    print x
    x = x/2

テキストファイル

テキストファイルは、Scientific.IO.TextFile モジュール内でオブジェクトとして定義されます。

読み込み

テキストファイルは、行 (lines) の列 (sequences) として扱うことができます。ただし、1行ずつ順に読まなくてはならないという制限があります。 次のプログラムは、一つのファイル中の全行を印字します:

from Scientific.IO.TextFile import TextFile

for line in TextFile('some_file'):
    print line[:-1]

何故 line[:-1] なのでしょう? 各行の最終文字は改行 (new-line) 文字なので、印字したくないからです(それを含めると、各行の間に空行が余分に入ります)。

テキストファイルオブジェクトは、圧縮ファイルを扱うこともできます。 ファイル名が ".Z" または ".gz" で終るものは全て圧縮ファイルと見なされます。 もちろん、プログラムが受け取るのは解凍されたデータです。 ファイル名の代りに、URL (ウエブアドレスで良く知られている Universal Resource Locator) を用いる、つまり、インターネット経由でデータを直接読み込むことさえも出来ます。

書き出し

テキストファイルは、読み込みだけでなく、書き出しのために開くこともできます:

from Scientific.IO.TextFile import TextFile

file = TextFile('a_compressed_file.gz', 'w')
file.write('The first line\n')
file.write('And the')
file.write(' second li')
file.write('ne')
file.write('\n')
file.close()

書き出しに開いたファイルは、最後に close で閉じなくてはなりません。これによって、全てのデータがファイルに書き出されたことが確認されます。 プログラム終了時には、開かれた全てのファイルが自動的に close されるのですが、これをあてにしない方が良いでしょう。

書き出しにおいても、圧縮が自動的になされることに注意して下さい。ただし、URL の場合は駄目です。当然ながら、インターネット上の殆どのサーバーは、書き込みアクセスを許可しないからです!

いくつかの便利な文字列操作

string モジュールには、よく使われる文字列操作が含まれており、テキストファイルを読み書きする際に特に便利です。 ここでは、最も重要なものだけいくつか取り上げます;Python Library Reference で全体を一覧できますので、そちらを参照して下さい。

文字列からデータを取り出す

strip(string) 関数は、文字列から先頭と終末の空白を取り除きます。 split(string) 関数は、文字列中の単語をリストにして返します。ここで、"単語" とは空白の間にあるものを指します。split(string, separator) とすれば、任意の文字列を単語の区切り (separator) に用いることができます。

文字列から数値を取り出すには、整数を返す atoi(string) と、実数を返す atof(string) を使います。

文字列中からある特定の文を探すには、find(string, text) を使います。 これは、string 中で text が見つかった最初の添字を返します。 見つからなかった場合の値は -1 です。

例:次のプログラムは、一つのファイルを読み込み、その第2カラムにある全ての数値の和を印字します。

from Scientific.IO.TextFile import TextFile
import string

sum = 0.
for line in TextFile('data'):
    sum = sum + string.atof(string.split(line)[1])

print "The sum is: ", sum

例:次のプログラムは、あなたのコンピュータに登録されている全ての利用者名を印字します:

from Scientific.IO.TextFile import TextFile
from string import split

for line in TextFile('/etc/passwd'):
    print split(line, ':')[0]

データを文字列に変換する

任意の Python オブジェクト(数値、文字列、ベクトルなど)は、逆アポストロフィー中に挿入して書くことによって、文字列に変換できます:

from Scientific.Geometry import Vector

a = 42
b = 1./3.
c = Vector(0, 2, 1)

print `a` + ' ' + `b` + ' ' + `c`
このプログラムは、"42 0.333333333333 Vector(0,2,1)" と印字します。

文字列に変換する別の方法に、str(data) 関数があります。 (これは、string モジュールに含まれているのではなく、言語の標準関数の一つです。) これら二つの方法は、必ずしも同じ結果を与えません。ただし、数値に関しては同じ結果になります。 一般的に言って、str(data) は値を "綺麗に" 表示しますが、逆アポストロフィーは値だけでなくそのデータの型を示すような表示を返します。 例えば、s を文字列とすると、str(s)s と同じですが、`s`s をアポストロフィーで囲んだものを返し、そのデータが文字列であることを明示します。 実際には、両方を試してみて好きな方を使うのが良いでしょう。

関数 join(words) は、文字列のリストを受け取り、空白を区切りにして結合したものを返します。 したがって、前のプログラムの最後の行は、単に print string.join(`a`, `b`, `c`) とできます。 ここでも、任意の文字列を単語の区切りに使うことができます。

関数 lower(string)upper(string) は、文字列を小文字あるいは大文字に変換します。

関数 ljust(string, width) は、少なくとも width 文字分の長さの幅に、文字列 string を左詰めにした文字列を返します。 同じ様に、関数 rjust は右詰めに、center は中央に揃えます。

ここでは取り上げなかった便利な関数について

Python には、もっと特殊なものからそうでないものまで、様々な形式のテキスト処理をするためのプログラムが膨大に集められています。 ここでは、それらを全て記述することはもとより、一覧することさえ不可能です。 必要な情報は全て、Python Library Reference から探し出すことができます。

まず第一に、string モジュールには、ここで取り上げなかった多くの関数があります。

正規表現と呼ばれる重要な一組の関数群は、あるパターンに従ってデータを検索したり変更したりします。 これらの関数は、re モジュールにあります。 これらは非常に強力ですが、正規表現の文法は(grep のような UNIX ツールや、viemacs といったエディターでも使われていますが)少し複雑です。

htmllib モジュールには、HTML ファイルからデータを取り出す関数があり、通常 World-Wide Web において使用されます。 formatter モジュールは、HTML ファイルを作成する道具を提供します。

Fortran フォーマットのファイル

Fortran プログラムでは、印字カードの代用 (emulation) としてテキストファイルを用いることから、特異なフォーマットが使われます。 各々のデータは、空白などの区切り文字によってではなく、行中の位置で指定されます。 行の割付けは、フォーマット仕様によって定義されます。 例えば、2I4, 3F12.5 は、4文字分の領域を割付けられた整数が二つ、その次に12文字分の領域を割付けられた小数点以下5桁の実数が三つです。

Scientific.IO.FortranFormat モジュールは、Python のデータオブジェクトと Fortran フォーマットのテキスト文字列との間の変換を扱います。 まず第一のステップで、Fortran のフォーマット仕様を表すフォーマットオブジェクトを生成します。 第二ステップで、データ値のリストから Fortran フォーマットの行を生成します(出力の場合)。入力の場合には、その逆の操作をします。

下の例は、PDB ファイルを読み込み、各原子の名前と位置を印字します。 各行を2度ずつ解析しなければならない点に注意してください:最初は先頭の6文字だけを取り出して記録の種類を見分けます。それが原子の定義であった場合に、一定のフォーマットを用いて実際のデータを取り出します。

from Scientific.IO.TextFile import TextFile	
from Scientific.IO.FortranFormat import FortranFormat, FortranLine
from Scientific.Geometry import Vector

generic_format = FortranFormat('A6')

atom_format = FortranFormat('A6,I5,1X,A4,A1,A3,1X,A1,I4,A1,' +
                            '3X,3F8.3,2F6.2,7X,A4,2A2')

# 各領域の内容:
# 記録種類、連番号、原子名、代替位置指標、残基名、鎖名、残基配列番号、挿入記号、
# x座標、y座標、z座標、占有数、温度因子、
# 区分名、元素記号、電荷

for line in TextFile('protein.pdb'):
    record_type = FortranLine(line, generic_format)[0]
    if record_type == 'ATOM  ' or record_type == 'HETATM':
        data = FortranLine(line, atom_format)
        atom_name = data[2]
        position = Vector(data[8:11])
        print "Atom ", atom_name, " at position ", position

次の例は、Fortran 形式でデータを書き出す方法を示します。出力ファイルには、第一列と第二列に各々一連の数値とその平方根が書き出されます。

from Scientific.IO.TextFile import TextFile	
from Scientific.IO.FortranFormat import FortranFormat, FortranLine
from Numeric import sqrt

format = FortranFormat('2F12.5')

file = TextFile('sqrt.data', 'w')
for n in range(100):
    x = n/10.
    file.write(str(FortranLine([x, sqrt(x)], format)) + '\n')
file.close()

練習問題


目次