在 Python 中使用 glob.glob 和带有 unicode 文件名的正则表达式的文件系统独立方式
Filesystem independent way of using glob.glob and regular expressions with unicode filenames in Python
我正在开发一个我想保持平台、文件系统和 Python2.x/3.x 独立的库。但是,我不知道如何以 platform/file-system 独立的方式对文件进行 glob 匹配,并将文件名与正则表达式进行匹配。
例如(在 Mac,使用 IPython,Python 2.7):
In[7]: from glob import glob
In[8]: !touch 'ü-0.é' # Create the file in the current folder
In[9]: glob(u'ü-*.é')
Out[9]: []
In[10]: import unicodedata as U
In[11]: glob(U.normalize('NFD', u'ü-*.é'))
Out[11]: [u'u\u0308-0.e\u0301']
但是,这不适用于 Linux 或 Windows,我需要 unicode.normalize('NFC', u'ü-*.é')
。当我尝试将文件名与正则表达式进行匹配时,会出现同样的问题:只有在 Mac 上标准化为 NFD
的 unicode 正则表达式匹配文件名,而只有 NFC
正则表达式匹配读取的文件名在 Linux/Windows 上(我在这两种情况下都使用 re.UNICODE
标志)。
是否有处理此问题的标准方法?
我希望就像 sys.getfilesystemencoding()
returns 文件系统的编码一样,存在一个 returns 底层文件系统使用的 Unicode 规范化的函数。
但是,我既找不到这样的函数,也找不到 safe/standard 对其进行功能测试的方法。
Mac + HFS+
使用 NFD 规范化:https://apple.stackexchange.com/a/10484
Linux + Windows 使用 NFC 标准化:http://qerub.se/filenames-and-unicode-normalization-forms
Link 编码:https://github.com/musically-ut/seqfile/blob/feat-unicode/seqfile/seqfile.py
我是这样解决问题的:
import unicodedata as U
# ...
globPattern = os.path.join(folder, prefix + u'*' + suffix)
rawRegEx = prefix + u'([0-9]+)' + suffix + u'$'
# Mac uses NFD normalization for Unicode filenames while windows
# linux/windows use NFC normalization
if sys.platform.startswith('darwin'):
normalizedGlobPattern = U.normalize('NFD', globPattern)
normalizedRegEx = U.normalize('NFD', rawRegEx)
else:
normalizedGlobPattern = U.normalize('NFC', globPattern)
normalizedRegEx = U.normalize('NFC', rawRegEx)
allFiles = glob.glob(normalizedGlobPattern)
# ...
numFilesRegEx = re.compile(normalizedRegEx, _re.UNICODE)
numberedFiles = (re.search(numFilesRegEx, f) for f in allFiles
if re.search(numFilesRegEx, f))
这似乎通过了我可以在 AppVeyor (Windows)、Travis (Linux) 和我的笔记本电脑 (Mac + HFS+
) 上进行的所有测试.
但是,我不确定这是否安全或是否有更好的写法。例如,我不知道它是否可以在安装了 NFS 的 Mac 上运行。
我假设您想匹配 unicode equivalent 文件名,例如您希望 u'\xE9*'
的输入模式匹配任何操作系统上的文件名 u'\xE9qui'
和 u'e\u0301qui'
,即字符级模式匹配。
你必须明白这不是 Linux 上的默认设置,其中字节被视为字节,并且在当前系统编码中并非每个文件名都是有效的 unicode 字符串(尽管 Python 3 使用 'surrogateescape' 错误处理程序将它们表示为 str
无论如何)。
考虑到这一点,这是我的解决方案:
def myglob(pattern, directory=u'.'):
pattern = unicodedata.normalize('NFC', pattern)
results = []
enc = sys.getfilesystemencoding()
for name in os.listdir(directory):
if isinstance(name, bytes):
try:
name = name.decode(enc)
except UnicodeDecodeError:
# Filenames that are not proper unicode won't match any pattern
continue
if fnmatch.filter([unicodedata.normalize('NFC', name)], pattern):
results.append(name)
return results
我正在开发一个我想保持平台、文件系统和 Python2.x/3.x 独立的库。但是,我不知道如何以 platform/file-system 独立的方式对文件进行 glob 匹配,并将文件名与正则表达式进行匹配。
例如(在 Mac,使用 IPython,Python 2.7):
In[7]: from glob import glob
In[8]: !touch 'ü-0.é' # Create the file in the current folder
In[9]: glob(u'ü-*.é')
Out[9]: []
In[10]: import unicodedata as U
In[11]: glob(U.normalize('NFD', u'ü-*.é'))
Out[11]: [u'u\u0308-0.e\u0301']
但是,这不适用于 Linux 或 Windows,我需要 unicode.normalize('NFC', u'ü-*.é')
。当我尝试将文件名与正则表达式进行匹配时,会出现同样的问题:只有在 Mac 上标准化为 NFD
的 unicode 正则表达式匹配文件名,而只有 NFC
正则表达式匹配读取的文件名在 Linux/Windows 上(我在这两种情况下都使用 re.UNICODE
标志)。
是否有处理此问题的标准方法?
我希望就像 sys.getfilesystemencoding()
returns 文件系统的编码一样,存在一个 returns 底层文件系统使用的 Unicode 规范化的函数。
但是,我既找不到这样的函数,也找不到 safe/standard 对其进行功能测试的方法。
Mac + HFS+
使用 NFD 规范化:https://apple.stackexchange.com/a/10484
Linux + Windows 使用 NFC 标准化:http://qerub.se/filenames-and-unicode-normalization-forms
Link 编码:https://github.com/musically-ut/seqfile/blob/feat-unicode/seqfile/seqfile.py
我是这样解决问题的:
import unicodedata as U
# ...
globPattern = os.path.join(folder, prefix + u'*' + suffix)
rawRegEx = prefix + u'([0-9]+)' + suffix + u'$'
# Mac uses NFD normalization for Unicode filenames while windows
# linux/windows use NFC normalization
if sys.platform.startswith('darwin'):
normalizedGlobPattern = U.normalize('NFD', globPattern)
normalizedRegEx = U.normalize('NFD', rawRegEx)
else:
normalizedGlobPattern = U.normalize('NFC', globPattern)
normalizedRegEx = U.normalize('NFC', rawRegEx)
allFiles = glob.glob(normalizedGlobPattern)
# ...
numFilesRegEx = re.compile(normalizedRegEx, _re.UNICODE)
numberedFiles = (re.search(numFilesRegEx, f) for f in allFiles
if re.search(numFilesRegEx, f))
这似乎通过了我可以在 AppVeyor (Windows)、Travis (Linux) 和我的笔记本电脑 (Mac + HFS+
) 上进行的所有测试.
但是,我不确定这是否安全或是否有更好的写法。例如,我不知道它是否可以在安装了 NFS 的 Mac 上运行。
我假设您想匹配 unicode equivalent 文件名,例如您希望 u'\xE9*'
的输入模式匹配任何操作系统上的文件名 u'\xE9qui'
和 u'e\u0301qui'
,即字符级模式匹配。
你必须明白这不是 Linux 上的默认设置,其中字节被视为字节,并且在当前系统编码中并非每个文件名都是有效的 unicode 字符串(尽管 Python 3 使用 'surrogateescape' 错误处理程序将它们表示为 str
无论如何)。
考虑到这一点,这是我的解决方案:
def myglob(pattern, directory=u'.'):
pattern = unicodedata.normalize('NFC', pattern)
results = []
enc = sys.getfilesystemencoding()
for name in os.listdir(directory):
if isinstance(name, bytes):
try:
name = name.decode(enc)
except UnicodeDecodeError:
# Filenames that are not proper unicode won't match any pattern
continue
if fnmatch.filter([unicodedata.normalize('NFC', name)], pattern):
results.append(name)
return results