Я действительно хотел бы иметь свое соглашение о приложении Python исключительно со строками Unicode внутренне. Это шло хорошо у меня в последнее время, но я столкнулся с проблемой с обработкой путей. API POSIX для файловых систем не является Unicode, таким образом, это возможно (и на самом деле несколько распространено) для файлов, чтобы иметь "undecodable" имена: имена файлов, которые не кодируются в установленном кодировании файловой системы.
В Python это проявляет как смесь unicode
и str
объекты, возвращаемые из os.listdir()
.
>>> os.listdir(u'/path/to/foo')
[u'bar', 'b\xe1z']
В том примере, символе '\xe1'
кодируется в латинском 1 или somesuch, даже когда (гипотетическая) файловая система сообщает sys.getfilesystemencoding() == 'UTF-8'
(в UTF-8 тот символ составил бы два байта '\xc3\xa1'
). Поэтому Вы доберетесь UnicodeError
s повсеместно, при попытке использовать, например, os.path.join()
с путями Unicode, потому что имя файла не может декодироваться.
ПРАКТИЧЕСКОЕ РУКОВОДСТВО Unicode Python дает этот совет о unicode путях:
Обратите внимание, что в большинстве случаев, API Unicode должны использоваться. API байтов должны только использоваться в системах, где undecodable имена файлов могут присутствовать, т.е. системы Unix.
Поскольку я главным образом забочусь о системах Unix, это означает, что я должен реструктурировать свою программу для контакта только со строками байтов для путей? (Если так, как я могу поддержать совместимость Windows?) Или там другой, лучшие способы иметь дело с undecodable именами файлов? Действительно ли они достаточно редки "в дикой природе", что я должен просто попросить, чтобы пользователи переименовали свои проклятые файлы?
(Если лучше просто иметь дело со строками байтов внутренне, у меня есть последующий вопрос: Как я храню строки байтов в SQLite для одного столбца при хранении остальной части данных как дружественные строки Unicode?)
В Python есть решение проблемы, если вы готовы перейти на Python 3.1 или более позднюю версию:
PEP 383 - Non-decodable Bytes in System Character Interfaces.
Если вам нужно хранить строки байтов в базе данных, предназначенной для UNICODE, то, вероятно, проще записать строки байтов в шестнадцатеричной кодировке. Таким образом, строку в шестнадцатеричном коде можно безопасно хранить как строку Unicode в базе данных.
Что касается проблемы имени пути UNIX, я понимаю, что для имен файлов не применяется конкретная кодировка, поэтому вполне возможно использовать Latin-1, KOI-8-R, CP1252 и другие в различных файлах. Это означает, что каждый компонент в имени пути может иметь отдельную кодировку.
У меня возникнет соблазн попытаться угадать кодировку имен файлов, используя что-то вроде модуля chardet . Конечно, нет никаких гарантий, поэтому вам все равно придется обрабатывать исключения, но у вас будет меньше некодируемых имен. Некоторые программы заменяют некодируемые символы на? что необратимо. Я бы предпочел, чтобы их заменили на \ xdd или \ xdddd, потому что при необходимости это можно поменять вручную. В некоторых приложениях можно представить строку пользователю, чтобы он мог ввести символы Юникода для замены некодируемых.
Если вы пойдете по этому пути, вы можете в конечном итоге расширить chardet для выполнения этой работы. Было бы неплохо дополнить его утилитой, которая сканирует файловую систему, находит недекодируемые имена и создает список, который можно редактировать, а затем отправлять обратно, чтобы исправить все имена с помощью эквивалентов Unicode.