2018年02月07日

言語処理100本ノックでPython入門 #10 - テキストファイルの入力

  
今回から、言語処理100本ノック 2015の第2章: UNIXコマンドの基礎に入ります。

第2章の問題は、Pythonで解いたプログラムの出力結果とUNIXコマンドで同様のことをやった結果を比較して、プログラムの正しさを確認せよ、というものなのですが、UNIIXコマンドでの確認は省略します。

■ 問題
hightemp.txtは,日本の最高気温の記録を「都道府県」「地点」「℃」「日」のタブ区切り形式で格納したファイルである.以下の処理を行うプログラムを作成し,hightemp.txtを入力ファイルとして実行せよ.さらに,同様の処理をUNIXコマンドでも実行し,プログラムの実行結果を確認せよ.

10. 行数のカウント
行数をカウントせよ.確認にはwcコマンドを用いよ.

■ Pythonのコード
def countLine(filepath):
    with open(filepath, 'r', encoding='utf8') as f:
        count = 0
        for _ in f:
            count += 1
    return count

def main():
    count = countLine('hightemp.txt')
    print(count)

if __name__ == '__main__':
    main()


■ テキストファイルの入力

このプログラムで使ったファイル(UTF-8 BOM無し)では、エンコーディングの指定をしないとWindowsの環境では以下の例外が出ます。
UnicodeDecodeError, 'cp932' codec can't decode byte 0x8c in position 8: illegal multibyte sequence
Macだと、以下のメッセージです。
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe9 in position 0: ordinal not in range(128)
UTF-8をデフォルトにしてくれてもいいのになーと思います。。

ちなみに、withを使わない以下のような書き方もありますが、closeを明示的に呼ぶ必要があります。
withを使ったほうが安全ですね。C#のusingと同じです。
def countLine(filepath):
    f = open('hightemp.txt', 'r', encoding='utf8')
    count = 0
    for _ in f:
        count += 1
    f.close()   
    return count

readlinesというメソッドもあるから、以下のようにも書けます。
今は、PCのメモリ豊富にあるから、たいていの場合はこれでもいいかも。
def countLine(filepath):
    with open(filepath, encoding='utf8') as f:
        return len(f.readlines())

■ 改行コードの扱い 

試しに、以下のようなコードを書いてみました。
with open('hightemp.txt', 'r', encoding='utf8') as f:
    for line in f:
        print(line)
そしたら、結果が、
高知県	江川崎	41	2013-08-12

埼玉県	熊谷	40.9	2007-08-16

岐阜県	多治見	40.9	2007-08-16
って、空白行ができてしまいます。

この動きをみると、改行コードが行末についたままなのですね。
C#に慣れた僕としては、ちょっと不満です。 改行コードを消してみました。
with open('hightemp.txt', 'r', encoding='utf8') as f:
    for line in f:
        print(line.rstrip('\n'))
rstrip関数は、文字列の末尾から指定した文字列を削除してくれます。
C#のTrimEndメソッドみたいなもんですね。

試しに、hightemp.txt の改行コードを CR/LFに変更してみても、意図通りに動作しました。
ライブラリ側で '\n'一文字に置き換えてくれているようです。

引数のない以下のようなコードでもOKです。
print(line.rstrip())
この場合は、行末の空白(改行コード含む)を削除してくれるようです。
rstripのrは、rightのrのようです。lstrip, stripというメソッドもありました。


■ 利用しない変数 


それと、行数をカウントするコードですが、
for line in f:
    count += 1
と書くと、以下のpylintの警告が出ます。確かにline変数は使われていません。
W0612:Unused variable 'line'
こんな時は、_ を使えば警告が消えます。 
for _ in f:
    count += 1