ファイル読み取り

基本

with open(file_path, mode='r', encoding='cp932') as rh:
    for line in rh:
        # 1行ずつ処理
        pass

エンコード

デフォルトの文字エンコードはOSに依る。Windowsならcp932(MicrosoftによるShift-JISの独自拡張)

7.2. codecs — codec レジストリと基底クラス — Python 3.6.1 ドキュメント

エンコード指定
Shift-JIScp932, sjis, s_jis
UTF-8utf8
UTF-8 BOM付きutf_8_sig

っつってもファイル内でasciiの英数字記号だけしか使われてないならShiftJISもUTF-8も同じ。

UTF-8(BOM付)のファイルはきちんとutf_8_sigを指定してあげないとUnicodeDecodeErrorになるが、逆にUTF-8(BOM無し)のファイルをutf_8_sigで読んでも読める。

自動判定

chardetというパッケージで判定が行えるらしい。

ただし、数点の候補のみから絞れさえすればいいというのであれば、以下のように独自に全通り試した方が、変な誤判定の可能性が無くてよいような気もする。

def get_encoding(path, eachline=False, lookup=('cp932', 'utf_8', 'utf_8_sig')):
    """
    テキストファイルのエンコードを調べる。

    :param path: ファイルパス
    :param eachline: 1行ずつ確かめる(メモリに一度に載りきらないファイルなど。ただし時間かかる)
    :param lookup: 試すエンコード名候補のiterable
    :return: eachline=True の時は、エンコード名を返す
             eachline=False の時は、エンコード名と読み取ったデータを返す
    """
    encoding = None
    if eachline:
        for trying_encoding in lookup:
            try:
                with open(path, 'r', encoding=trying_encoding) as rh:
                    for line in rh:
                        pass
                encoding = trying_encoding
                break
            except UnicodeDecodeError:
                continue
        return encoding
    else:
        encoded = None
        with open(path, 'rb') as rh:
            data = rh.read()
        for trying_encoding in lookup:
            try:
                encoded = data.decode(trying_encoding)
                encoding = trying_encoding
                break
            except UnicodeDecodeError:
                continue
        return encoding, encoded

テキスト形式のバリエーション

CSV

PythonでCSVの読み書き - Qiita

CSVと一口に言っても公的にフォーマットが決まっているわけでは無く、バリエーションが様々ある。

改行コードはLFかCRLFかという所から始まり、データに“,”や改行が入ってたら“引用符”で囲うとか、さらに引用符の中で引用符を使う場合は2つ重ねるとか、細かいルールまで気にしなければならない場合は結構面倒。そんな場合は、pythonにはcsvライブラリがあるので、素直にそちらに任せよう。

import csv

with open(file_path) as rh:
    for records in csv.reader(rh):
        # recordsに既に分割された値が入っている
        # 型はstring
        pass

そうじゃなくて、対処に困る値が入っておらず単に“,”でsplitすればいいだけなら、自分でやった方が速い。

with open(file_path) as rh:
    for line in rh:
        records = line.strip().split(',')
        user_id = records[1]
        # ...

for line in rhで取得されるlineには、末尾に改行が残ったままとなっている点に注意。そのままsplitすると、最後のカラムの値に改行が付いてしまうので、必要ならstripしておく。

沢山あるカラムの最初の方のデータだけ必要なら、maxsplitを指定すると気持ち速くなる(プラシーボ効果)

with open(file_path) as rh:
    for line in rh:
        # 5カラム目だけ必要な場合
        records = line.split(',', maxsplit=5)
        # records[0]~[5]まで6つに分割
        # records[5]には6カラム目以降のデータがカンマ分割されずに入っている
        user_id = records[4]

ヘッダを読み飛ばすにはreadline()

with open(file_path) as rh:
    rh.readline()
    # 複数行読み飛ばすならその分だけ繰り返す
    rh.readline()
    rh.readline()
    for line in rh:
        records = line.strip().split(',')
        user_id = records[1]
        # ...

3カラムの数値のみからなるcsvを、1カラム目をキー、2・3カラム目のtupleを値に持つ辞書に変換

with open(file_path) as rh:
    data = {a: (b, c) for a,b,c in (map(int, line.split(',')) for line in rh)}
    # 無理に1行で書くと読みにくいので良い子は''for''で回そう

programming/python/tips/readfile.txt · 最終更新: 2018/05/23 by ikatakos
CC Attribution 4.0 International
Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0