Help us understand the problem. What is going on with this article?

python3で複数文字コードが含まれるファイルをUnicodeDecodeErrorが出る行を飛ばして処理する

More than 1 year has passed since last update.

先日、python3でファイルを読み込んで各行を処理するプログラムを書いてる時に UnicodeDecodeError と出会いました。ある行にutf-8で(decodeでき)ない行が紛れ込んでいることが原因でした。

処理としては UnicdeDecodeError が出る行をスキップして処理することにしたので、どのようにしたのか書いておきます。

コードの中にencodeとかdecodeとか出てきますがpython3です。

UnicodeDecodeErrorが途中の行で発生するファイルを作成する

create_file.py
def main():
    with open("hoge.txt", 'wb') as f:
        f.write("あ\n".encode('utf-8'))
        f.write("い\n".encode('utf-8'))
        f.write("う\n".encode('utf-8'))
        f.write("シフトジス\n".encode('shift-jis'))  # この行だけ別の文字コードでencodeする
        f.write("え\n".encode('utf-8'))
        f.write("お\n".encode('utf-8'))
        f.write("か\n".encode('utf-8'))

if __name__ == "__main__":
    main()
$ python create_file.py

上で作成したファイルの中身を普通に吐き出そうとしてみる

output_line.py
def main():
    with open("hoge.txt", 'r') as f:
        line = f.readline()
        while line:
            print(line.rstrip())
            line = f.readline()

if __name__ == "__main__":
    main()
$ python output_line.py
Traceback (most recent call last):
  File "output_line.py", line 9, in <module>
    main()
  File "output_line.py", line 3, in main
    line = f.readline()
  File "/path/to/.pyenv/versions/3.5.1/lib/python3.5/codecs.py", line 321, in decode
    (result, consumed) = self._buffer_decode(data, self.errors, final)
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x83 in position 12: invalid start byte

途中にshit-jisのバイト列が入ってるので怒られます。このエラー文を見ると、 f.readline() の中でutf-8型でdecodeしようとしていることがわかります。その為、ファイルの途中で変なバイト列があると、f.readline()できなくなってしまうようです。try・exceptでエラーが出ないようにしても、結局f.readline()できないので次の行に進めなくなってしまいます。

f.readline()でUnicodeDecodeErrorを出さないようにする方法

簡単で、バイト列として行を受け取るようにします。

先ほどのコードでopenする時に r ではなく rb にすると出力でエラーが出なくなります。

output_line.py変更分
with open("hoge.txt", 'rb') as f:
$ python output_line.py
b'\xe3\x81\x82'
b'\xe3\x81\x84'
b'\xe3\x81\x86'
b'\x83V\x83t\x83g\x83W\x83X'
b'\xe3\x81\x88'
b'\xe3\x81\x8a'
b'\xe3\x81\x8b'

ただ、バイト列だと扱い辛いのでstr型に戻したいです。

最終形:UnicodeDecodeErrorが出る行を飛ばして処理する

出力をstrにしてUnicodeDecodeErrorが出る行をスキップする為には、decodeとtry・exceptを使います。最終形は下のコードです。

output_line.py
def main():
    with open("hoge.txt", 'rb') as f:  # 'r'でなく'rb'
        line = f.readline()
        while line:
            try:
                line = line.decode("utf-8")
            except UnicodeDecodeError:
                # utf-8でないバイト列が含まれる行はスキップする
                line = f.readline()
                continue
            # ...
            # 何か行いたい処理
            print(line.rstrip())
            # ...
            line = f.readline()

if __name__ == "__main__":
    main()
$ python output_line.py
あ
い
う
え
お
か

標準入力から受け取る場合

output_line.py
import sys

def main():
    for line in sys.stdin.buffer:  # bufferをつけるとバイト列で受け取れる
        try:
            line = line.decode("utf-8")
        except UnicodeDecodeError:
            # utf-8でないバイト列が含まれる行はスキップする
            continue
        ...
        # 何か行いたい処理
        print(line.rstrip())
        # ...

if __name__ == "__main__":
    main()
$ cat hoge.txt | python output_line.py
あ
い
う
え
お
か
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした