1. サイトトップ
  2. ブログ
  3. Python
  4. try文を使用して異なる文字エンコードのファイルを一度に扱う方法

try文を使用して異なる文字エンコードのファイルを一度に扱う方法

こんにちは。
プログラム1課 日高です。

皆さんは大量のファイルに対して同じ作業(文字列の検索等)を行う時はどのようにしているでしょうか?
私は簡単な操作ならバッチファイルやPythonをよく使っています。

以前Pythonで文字列の置換を行うスクリプトを作成した時に、shift_jisとutf-8のファイルが混じっていて一気に処理できないという問題に遭遇しました。

今回は、その時に行ったtry文を使った解決方法について紹介したいと思います。

目次

1つの文字エンコードのみを対象としたスクリプト

まず例外が発生するプログラムの例として、「ロジカルビート」の文字列を「logicalbeat」に置換するスクリプトを書いてみました。

文字エンコードはshift_jisを指定しています。

import glob

# 同じ階層のtxtファイルを取得
fileNames = glob.glob("./*.txt")

for fileName in fileNames:
    # ファイルの読み込み
    file = open(fileName, "r", encoding = "shift_jis")
    data = file.read()
    file.close()

    # 文字列の置き換え
    data = data.replace("ロジカルビート", "logicalbeat")

    # ファイルの上書き
    file = open(fileName, "w", encoding = "shift_jis")
    file.write(data)
    file.close()

文字エンコードが異なる時の例外

全てのファイルがshift_jisで統一されているならこれで問題ありませんが、文字エンコードが異なるファイルが混ざっていると、以下のような例外が発生し動作が止まってしまいます。

例外が発生しました: UnicodeDecodeError
'shift_jis' codec can't decode byte 0xef in position 0: illegal multibyte sequence
  File "TextSwap.py", line 9, in <module>
    data = file.read()
           ^^^^^^^^^^^
UnicodeDecodeError: 'shift_jis' codec can't decode byte 0xef in position 0: illegal multibyte sequence

この例外は、 read()で読み込んだデータがopen()で指定した文字エンコード(↑の例ではshift_jis)でなかった場合に発生します。

このままでは置換作業が出来ないので、try文を使ってshift_jis以外のファイルを扱えるようにしてみます。

実際にスクリプトを書く前にtry文の動作について紹介しておきます。

try文について

Pythonのtry文はtry, except, else, finallyで構成されます。
C++の経験がある方はC++のtryとはそれぞれ以下のような対応をイメージすると分かりやすいと思います。

Python try except else finally
C++ try catch無し無し

try

この中で発生した例外によって、処理を分岐します。

・例外が発生した場合は例外と対応するexceptに移動します。
・例外が発生しなかった場合はelseに移動します。

except

try内で例外が発生した時のに実行されます。

・以下の様に書く事で受け取る例外を指定する事ができます。

except UnicodeDecodeError as e:
    # 例外発生時の処理

・UnicodeDecodeErrorという例外が発生した時にだけここを通り、変数eには例外の詳細が入っています。
・指定されていない例外が来た場合はexceptを通らず、通常通りスクリプトの動作が停止します。

else

try内で例外が発生しなかった場合に実行されます 。

finally

例外の有無に関わらず、最後に実行されます。

・例外が発生してもしなくても必ず最後にここを通ります。
・「try・except・else」の中で「break・continue・return」を使った場合も一旦ここを経由するようになっています。

複数の文字エンコードに対応したスクリプト

try文を使い、文字エンコードの異なるファイルが混じっていても動くようにしたものが以下になります。

import glob

# 同じ階層のtxtファイルを取得
fileNames = glob.glob("./*.txt")
# 確認する文字エンコード一覧
ENCODES = {"shift_jis", "utf-8"}

for fileName in fileNames:
    for encode in ENCODES:
        try:
            # ファイルの読み込み
            file = open(fileName, "r", encoding = encode)
            data = file.read()
        except UnicodeDecodeError as e:
            # read()で例外発生時は何もしない
            file.close()
            continue
        else:
            # read()に成功したので文字列を置き換える
            file.close()
            data = data.replace("ロジカルビート", "logicalbeat")
            file = open(fileName, "w", encoding = encode)
            file.write(data)
            file.close()
            break

tryからexcept内のコードに特に注目してみてください。
成功するまでENCODESで指定した文字エンコードで順にread()を試すようになっています。

今回はshift_jisとutf-8しか使っていません。
他の文字エンコードについては以下のリンク先を参考にしてみてください。
https://docs.python.org/ja/3/library/codecs.html#standard-encodings

実際に使用する際は、読み込め無かったり開けなかったファイルの一覧を出力する等の機能も追加するとより便利なものになると思います。

最後に

今回はpythonのtry文を使い複数の文字エンコードに対応する方法を紹介しました。
事前に例外にならないよう確認するのも良いですが、try文の方が簡単に書ける事もあります。
機会は少ないかもしれませんが、例外が起きた時の対応方法としてtry文も選択しに加えてみてください。


【免責事項】

本サイトでの情報を利用することによる損害等に対し、
株式会社ロジカルビートは一切の責任を負いません。