Независимый от файловой системы способ использования glob.glob и регулярных выражений с именами файлов Unicode в Python

Я работаю над библиотекой, которую я хочу сохранить независимой от платформы, файловой системы и Python2.x / 3.x. Тем не менее, я не знаю, как 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'ü-*.é') . Эта же проблема возникает, когда я пытаюсь сопоставить имя файла с регулярным выражением: только нормализованное выражение unicode, нормализованное как NFD на Mac, соответствует имени файла, тогда как только регулярное выражение NFC соответствует именам файлов, читаемым в Linux / Windows (я использую флаг re.UNICODE в обоих случаях).

Существует ли стандартный способ решения этой проблемы?

Я надеюсь, что так же, как sys.getfilesystemencoding() возвращает кодировку для файловой системы, существовала бы функция, которая возвращает нормализацию Unicode, используемую базовой файловой системой.

Однако я не мог найти ни такой функции, ни безопасного / стандартного способа тестирования функции.


Mac + HFS+ использует нормализацию НФД: https://apple.stackexchange.com/a/10484

Linux + Windows использует нормализацию NFC: http://qerub.se/filenames-and-unicode-normalization-forms

Ссылка на код: https://github.com/musical-ut/seqfile/blob/feat-unicode/seqfile/seqfile.py

Я предполагаю, что вы хотите совместить эквивалентные имена файлов в Unicode, например, вы ожидаете, что шаблон ввода 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 

Вот как я решаю проблему:

 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+ ).

Однако я не уверен , что это безопасно или есть лучший способ написать его . Например, я не знаю, будет ли он работать на Mac с установленным на нем NFS.