ファイルの扱い(前半)

教科書 Lesson 10

これ以降は、教科書の Lesson 10 の作業を進めてください。ただし 10.1 だけ、です。

以下に各節ごとに細かく書き出しますので、それに沿って作業してください。

10.1 テキストファイル

テキストファイルを読み書きする

(ここは概念説明のみ)

テキストファイルに書き込む

p.286 からは実際に手を動かして確かめながら読み進めてください。記述に従ってファイルを作り、中身を確認してください。

補足1. write() における改行の扱い

write() の行は f.write(“文字列”) というように改行文字をつけています。p.288 真ん中あたりでシレッと書いてあるだけでほぼ説明がありませんが、つまり print() と違って write() は改行をつけません。

以下のように改行をつけないと、

f.write("test")
f.write("1234")

出力されたファイルの中では、「test1234」とつながってしまいます。

with 文でオープンする

ここは参考までに読む、という事にしておいてください。with文はよく使われるのですが、教科書では(おそらくは分かりやすさのために敢えて)使っていません。ここでは使わないにせよ、どこかで with 文を使ったコードを見ても驚かない程度には把握しておいてください。

テキストファイルを読み込む

p.289 から実際に手を動かして確かめながら読み進めてください。

補足2. read() における改行の扱い

read() では、(補足1.に示したように)テキストファイル write( ) による書き込みでつけた改行が、そのまま読み込まれます。以下のようにすると確認できるでしょう。

>>> f = open("Sample.txt", "r")
>>> lines = f.readlines()
>>> lines
['こんにちは\n', 'さようなら\n']  <<< 改行が各行の行末についていることがわかる
>>> 

読み込んだデータは、各行ごとに文字列化されリストの一つの要素として格納されています。文字列がリストの要素として扱えることは一つ前の単元 String.md で触れましたね。

この lines リストの要素を一つ一つ単純に print( ) すると、改行が二つ付いてしまいます。データの中の改行文字そのものと、print文がつける改行の二つで、だから結果は「1行おき」の出力になります。これを防ぐために p.289 の Sample2.py プログラムは print(line, end=““) として、print( ) 文は改行文字をつけないように指定しています。ただ、このやり方はちょっと不自然で、普通は「読み込んだデータから改行を外す」方を選ぶでしょう。

文字列末尾の改行を外す方法は幾つかありますが、簡単な方法は「文字列の末尾(右側)から余分な文字を取り外す」機能を持つ .rstrip() メソッドを使うことです。つまり s1 = “test\n” とした後で、s2 = s1.rstrip() とすると、s2 は “test” となります。適当に検索すると各種の例が見つかるかと。(なお rstrip() は改行だけでなく空白なども削除してしまうので要注意。)

補足3. close() の位置

教科書に出てくる readlines( ) 関数は、一気にファイルの中身を全部読んでリストの中に入れています。全部読むのなら、p.289 の Sample2.py プログラムは print( ) し終わるのを待って .close( ) する必要はなく、readline() のすぐ次に close( ) すれば良いはずです。以下のようにしても問題なく動作します。

f = open("Sample.txt", "r")
lines = f.readlines()     <<< ファイルから全データを一気に読み込み終わったので、
f.close()                 <<< ここで close() して構わない
for line in lines:
    print(line, end="")
# f.close()               <<< なぜか教科書はここで close() しているが、ここまで待つ理由はない

補足4. readline() で1行ずつ読む

readlines( ) 関数と違って readline( ) 関数は( s がない)ファイルの中身を一行ぶんだけ読み込みます。例えば以下のプログラムは、各行を読んで行番号を付けて出すものです。

f = open("Sample2.py", "r")    <<< 対象ファイルを p.289 の Sample2.py とする
n = 0
while True:
    line = f.readline()
    if line == "":           <<< 結果が空文字列なのは「もう読める行がない」ことを意味する
        break
    n += 1
    print("{:3d}: {}".format(n, line), end="")  <<< フォーマット指定は p.261 を参照
f.close()

実行結果はこんな格好になります。

yasuda@Slack 13 % python3 readline1.py
  1: f = open("Sample.txt", "r")
  2: lines = f.readlines()
  3: for line in lines:
  4:     print(line, end="")
  5: f.close()
yasuda@Slack 13 % 
フォーマット指定
上の実行結果をよく見ると、行番号が ” 1:” などと「数字部分に 3 桁ぶん」を使っている( 1 を3桁で「右揃え」している)ことが分かるでしょうが、これはフォーマット文字列に {:3d} と桁数を指定したためです。このあたりのことは p.261 あたりには出てきません。興味のある人は フォーマット指定 を見ると良いでしょう。

なお readlines( ) 関数の引数として一度に読み込む行数を指定することで一行ずつ読ませることもできます。

>>> f = open("Sample.txt", "r")
>>> lines = f.readlines(1)       <<< 引数に 1 を指定
>>> lines
['こんにちは\n']                   <<< 結果は一行分だけ(だがリスト形式にはなる)
>>> 

確認課題

教材 Web に「不思議の国のアリス」の冒頭のテキスト(*1)を「 Alice.txt 」として置いてあります。このテキストファイルを読み、中の単語を取り出して、単語の「文字数ごとの分布」を調べてヒストグラムにしてください。以下の要件を満たすようにプログラムを作って下さい。

先に実行結果を示しておきます。まずファイル名を echo とパイプによって与えています。出力は各行ごとに左から文字数、出現数、棒グラフ、の表示になっており、第一行目を見ると、長さ 1 文字の単語(例えば a ?)が 3 つ含まれていたことが分かります。最終行は10文字「以上」の単語が 5 つあったことを示しています。

yasuda@Slack 13 % echo Alice.txt | python3 wordcount.py 
len count | 
 1    3   | *** 
 2   23   | *********************** 
 3   34   | ********************************** 
 4   17   | ***************** 
 5    9   | ********* 
 6    9   | ********* 
 7    8   | ******** 
 8    8   | ******** 
 9    1   | * 
10    5   | ***** 
yasuda@Slack 13 %

プログラムを書く前に、開発作業用のテストデータを作りましょう。つまりデータ量が多いと正しく数えられているかどうかわからなくなるので、初めは数単語しかないテキストファイルを手作業で作って動作確認をするのです。

プログラムのアルゴリズム(処理手順)としては以下のようになるでしょう。

いきなりコンピュータに向かってコードを書いたりせず、見通しがハッキリしてから(不明なところが無くなってから)書きましょう。.split( ) の動きなどはインタラクティブモードで確認してからやると安心です。また一度に全部書いてしまわず、ファイルを読んでリストに入れるところまで出来たら実行して動作確認する、といった感じで少しずつ動かしながら書くように。

物足りない人はスクリーニングを

これでは物足りないなあ、と思う人は、単語のスクリーニングをすると良いです。例えば「word.」のピリオドは取り除いて単語としてカウントすべきでしょう。「 “sample” 」の引用符なども同様です。そうした「ノイズ」になるような情報を取り除いてきれいにする作業のことをデータクレンジングなどと呼ぶときがあります。データ分析処理では必要な前処理です。

作成上の注意

上の箇条書きにした各段階ごとに少しずつ作成するように。また、データ量が多いと正しく数えられているかどうかわからなくなるので、初めは数単語しかないテキストファイルを手作業で作って動作確認をしましょう。

小さなファイルで正しく最後まで動くことが確認できれば、Alice.txt を使って動作させ、結果が上のものと同一になることを確認してください。

*1) ソース: Alice’s Adventures in Wonderland, Lewis Carroll, Project Gutenberg から取得して多少加工しています。(含まれる二重引用符がちょっと特殊なものだったので変更した)

Counter クラス
Python には要素をカウントするための Counter クラスが用意されています。しかし教材作成者が試したところ、課題の仕様を満たすプログラムには不向きなところがあり、上に示したアルゴリズム(処理手順)で実装した方が簡素なコードになりました。興味のあるひとは挑戦しても良いとは思います。