何らかの理由でPythonには、自然なソートを行うための組み込みの方法(1、10、2ではなく1、2、10)が付属していないため、自分で記述する必要があります。
import re
def sorted_alphanumeric(data):
convert = lambda text: int(text) if text.isdigit() else text.lower()
alphanum_key = lambda key: [ convert(c) for c in re.split('([0-9]+)', key) ]
return sorted(data, key=alphanum_key)
これで、この関数を使用してリストをソートできます。
dirlist = sorted_alphanumeric(os.listdir(...))
問題:
上記の関数を使用して文字列(フォルダー名など)を並べ替え、Windowsエクスプローラーのように並べ替えたい場合、一部のエッジケースで正しく機能しません。
この並べ替え関数は、特定の「特殊」文字が含まれるフォルダー名がある場合、Windowsで誤った結果を返します。たとえば、この関数は並べ替えます1, !1, !a, a
が、Windowsエクスプローラは並べ替えます!1, 1, !a, a
。
したがって、WindowsエクスプローラーがPythonで行うように正確に並べ替えるには、Windows組み込み関数を使用する必要があります。 ctypesを介し StrCmpLogicalW(もちろん、これはUnixでは機能しません)。
from ctypes import wintypes, windll
from functools import cmp_to_key
def winsort(data):
_StrCmpLogicalW = windll.Shlwapi.StrCmpLogicalW
_StrCmpLogicalW.argtypes = [wintypes.LPWSTR, wintypes.LPWSTR]
_StrCmpLogicalW.restype = wintypes.INT
cmp_fnc = lambda psz1, psz2: _StrCmpLogicalW(psz1, psz2)
return sorted(data, key=cmp_to_key(cmp_fnc))
この関数は、 sorted_alphanumeric()
ます。
ボーナス: winsort
Windowsでもフルパスをソートできます。
または、特にUnixを使用している場合は、 natsort
ライブラリ(pip install natsort
)をして、正しい方法(正しい位置にあるサブフォルダー)でフルパスで並べ替える。
次のように使用して、フルパスをソートできます。
from natsort import natsorted, ns
dirlist = natsorted(dirlist, alg=ns.PATH | ns.IGNORECASE)
フォルダー名(または文字列一般)の通常の並べ替えには使用しないでくださいsorted_alphanumeric()
。これは、上記の機能よりもかなり遅いためです。Windowsエクスプローラでの並べ替えを想定している場合
natsorted
は、ライブラリで誤った結果が返されるので、それを使用winsort()
してください。