LoginSignup
8
5

More than 5 years have passed since last update.

Windows上のPython2系で日本語ファイル列挙(5C問題対策)

Last updated at Posted at 2015-11-30

WindowsにおけるPython2系だとマルチバイト文字の処理がまだ微妙なので、普通にファイル列挙処理を書くと、ファイルパス内に特定の文字(「表」「ソ」など)が登場したときに列挙処理がうまく動かないことがある。いわゆる5C問題

今回テストに用いるファイル一覧

C:/test
  filelist.py
  てすと/
    a1.txt
    a2.txt
  表/
    hyo1.txt
    hyo2.txt
    中の表/
      hyo10.txt
      hyo11.txt

普通にファイル列挙を書いたスクリプト

filelist.py
# -*- coding: utf-8 -*-
import os
SEP = os.sep
def filelist(dir_path):
    for item in os.listdir(dir_path):
        file_path = dir_path + SEP + item
        print(file_path)
        if os.path.isdir(file_path):
            filelist(file_path)
# test
script_dir = os.path.dirname(os.path.abspath(__file__))
filelist(script_dir)

実行結果

C:\test\filelist.py
C:\test\てすと
C:\test\てすと\a1.txt
C:\test\てすと\a2.txt
C:\test\表
C:\test\表\表

「表」が含まれるパス内の列挙がうまくいっていない。

対策 (decodeによる対策)

@wonderful_panda さんに教えていただきました。
ファイルパスを取得した後に .decode('cp932') しておくと問題なくファイル列挙が行えます。

# -*- coding: utf-8 -*-
import os
SEP = os.sep
def filelist(dir_path):
    for item in os.listdir(dir_path):
        file_path = dir_path + SEP + item
        print(file_path)
        if os.path.isdir(file_path):
            filelist(file_path)
# test
script_dir = os.path.dirname(os.path.abspath(__file__.decode('cp932')))
filelist(script_dir)

実行結果

C:\test\filelist.py
C:\test\てすと
C:\test\てすと\a1.txt
C:\test\てすと\a2.txt
C:\test\表
C:\test\表\hyo1.txt
C:\test\表\hyo2.txt
C:\test\表\中の表
C:\test\表\中の表\hyo10.txt
C:\test\表\中の表\hyo11.txt

「表」が含まれるパス内の列挙も正常に行えた。

対策 (旧。カレントディレクトリによる対策)

decode による対策のほうが圧倒的にスマートなのですが、それを教えていただく前に書いていた対策方法も残しておきます。

filelist.py
# -*- coding: utf-8 -*-
import os
SEP = os.sep
def filelist2(dir_path):
    old_dir = os.getcwd()
    os.chdir(dir_path) # カレントディレクトリを変更.
    for item in os.listdir("."):
        file_path = dir_path + SEP + item
        print(file_path)
        if os.path.isdir(item):
            filelist2(file_path)
    os.chdir(old_dir) # カレントディレクトリを元に戻す.
# test
script_dir = os.path.dirname(os.path.abspath(__file__))
filelist(script_dir)

os.listdir に対して、5C系の文字が含まれているパスを渡すと問題が生じるので、パスを直接渡す代わりにあらかじめカレントディレクトリを目的のパスに設定しておき、os.listdir にはカレントディレクトリを示す "." を渡す。これにより列挙は正常に行える。

実行結果

C:\test\filelist.py
C:\test\てすと
C:\test\てすと\a1.txt
C:\test\てすと\a2.txt
C:\test\表
C:\test\表\hyo1.txt
C:\test\表\hyo2.txt
C:\test\表\中の表
C:\test\表\中の表\hyo10.txt
C:\test\表\中の表\hyo11.txt

「表」が含まれるパス内の列挙も正常に行えた。

Python3系について

Windows上においても Python 3.5 等では、今回のような対策を施さなくてもファイル列挙は正常に行えた。
マルチバイト文字列処理を行うことが分かっている新規プログラムであれば Python2系ではなくPython3系で作り始めるのが無難に思う。

おまけ:PHPにおける5C問題解決

Windows 上の PHP でも同様の手段(カレントディレクトリ変更)でファイル列挙うまくできないかと試してみたけどうまくいかなった。情報いろいろ漁ってみたけど、結論としてはPHPにおける解決策は「無い」っぽい?

8
5
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
5