名前順でソート(XPスタイル)
glob.glob()で得られるファイル名の順番は、
名前順ではないことに注意。
001.jpg, ... , 100.jpgの画像フォルダ内を
glob.glob()して得たリストの順番は名前順ではない。
list.sort()すればいいけれど。
peroonの日記
しかし、list.sort()すると、1.jpg, 10.jpg, 11.jpg, ... 2.jpg となる事に注意。
WinXP以降のように、数字を認識してソートするには、一手間必要です。
alist = ["{0}.jpg".format(i) for i in [10, 11, 0, 1, 2, 12]] def embedded_numbers(name): import re pieces = re.split(r"(\d+)", name) pieces[1::2] = map(int, pieces[1::2]) return tuple(pieces) print(sorted(alist, key=embedded_numbers)) # => ['0.jpg', '1.jpg', '2.jpg', '10.jpg', '11.jpg', '12.jpg']
次にctypesを使ってWinAPIを呼ぶ方法。
当然Windows限定ですが、Windowsのソート方法を確実に再現できます(多分バグも)。
ちなみに、StrCmpLogicalWにANSI版は無いようです。
alist = ["{0}.jpg".format(i) for i in [10, 11, 0, 1, 2, 12]] import ctypes SHLWAPI = ctypes.windll.LoadLibrary("SHLWAPI.dll") def cmp_filename_logical(f1, f2): return SHLWAPI.StrCmpLogicalW(unicode(f1), unicode(f2)) print(sorted(alist, cmp=cmp_filename_logical)) # => ['0.jpg', '1.jpg', '2.jpg', '10.jpg', '11.jpg', '12.jpg']
最後に、ctypesを使う方法の変形。
list.sortやsortedのcmp
引数は、Python3xでは廃止されています。
よって、上の方法は3xでは使えません。
ドキュメントにもあるとおり、
ASPN cookbookのレシピ
を使います。
alist = ["{0}.jpg".format(i) for i in [10, 11, 0, 1, 2, 12]] import ctypes SHLWAPI = ctypes.windll.LoadLibrary("SHLWAPI.dll") def cmp_filename_logical(f1, f2): return SHLWAPI.StrCmpLogicalW(unicode(f1), unicode(f2)) def CmpToKey(mycmp): 'Convert a cmp= function into a key= function' class K(object): def __init__(self, obj, *args): self.obj = obj def __lt__(self, other): return mycmp(self.obj, other.obj) == -1 def __gt__(self, other): return mycmp(self.obj, other.obj) == 1 def __eq__(self, other): return mycmp(self.obj, other.obj) == 0 def __le__(self, other): return mycmp(self.obj, other.obj) != 1 def __ge__(self, other): return mycmp(self.obj, other.obj) != -1 def __ne__(self, other): return mycmp(self.obj, other.obj) != 0 return K print(sorted(alist, key=CmpToKey(cmp_filename_logical))) # => ['0.jpg', '1.jpg', '2.jpg', '10.jpg', '11.jpg', '12.jpg']
上に挙げた3つの方法の速度を比較します。
def sorted1(alist): return sorted(alist, key=embedded_numbers) def sorted2(alist): return sorted(alist, cmp=cmp_filename_logical) def sorted3(alist): return sorted(alist, key=CmpToKey(cmp_filename_logical)) def main(): TestList = ["{0}.jpg".format(random.randrange(100)) for i in xrange(100)] from timeit import Timer for f in [sorted, sorted1, sorted2, sorted3]: t = Timer(lambda :f(TestList)) print(f.__name__, "->", t.timeit(1000)) #結果 #sorted -> 0.0443061897532 #sorted1 -> 1.18444467104 #sorted2 -> 1.52561428897 #sorted3 -> 2.14697398692
ctypesを使った方が若干遅いようですが、
ファイル名ソートなんてそんなにたくさんするものでもないでしょうから、
StrCmpLogicalWを使ったパターンの利用にためらいは不要でしょう。
もちろん、MacやUnixなら、embedded_numbers一択ですが。