Подтвердить что ты не робот

Имена файлов Unicode в Windows с Python & subprocess.Popen()

Почему происходит следующее:

>>> u'\u0308'.encode('mbcs')   #UMLAUT
'\xa8'
>>> u'\u041A'.encode('mbcs')   #CYRILLIC CAPITAL LETTER KA
'?'
>>>

У меня есть приложение Python, принимающее имена файлов из операционной системы. Он работает для некоторых международных пользователей, но не для других.

Например, это имя файла Unicode: и '\ u041a\u0433\u044b\u044b\u0448\u0444\u0442'

не будет кодироваться с кодировкой Windows "mbcs" (той, которая используется файловой системой, возвращается sys.getfilesystemencoding()). Я получаю "???????", показывая, что кодер не работает на этих символах. Но это не имеет никакого смысла, поскольку имя файла пришло от пользователя для начала.

Обновление: вот фон по моим причинам... У меня есть файл в моей системе с именем на кириллице. Я хочу вызвать subprocess.Popen() с этим файлом в качестве аргумента. Popen не будет обрабатывать unicode. Обычно я могу уйти с кодировкой аргумента с кодеком, данным sys.getfilesystemencoding(). В этом случае он не будет работать

4b9b3361

Ответ 1

В Py3K - по крайней мере, из Python 3.2 - subprocess.Popen и sys.argv работают последовательно (по умолчанию unicode) строки в Windows. CreateProcessW и GetCommandLineW.

В Python - по крайней мере до v2.7.2 - subprocess.Popen работает с аргументами Unicode. Он придерживается CreateProcessA (while os.* соответствует Unicode). И shlex.split создает дополнительную бессмыслицу.

Pywin32 win32process.CreateProcess также не автоматически переключается на версию W, и не существует win32process.CreateProcessW. То же самое с GetCommandLine. Таким образом, нужно использовать ctypes.windll.kernel32.CreateProcessW.... Возможно, модуль подпроцесса должен быть исправлен по этой проблеме.

UTF8 на argv[1:] с частными приложениями остается неуклюжим в Unicode OS. Такие трюки могут быть законными для 8-битных "Latin1" строковых ОС, таких как Linux.

UPDATE vaab создал исправленную версию Popen для Python 2.7, которая устраняет проблему.
См. https://gist.github.com/vaab/2ad7051fc193167f15f85ef573e54eb9
Сообщение в блоге с пояснениями: http://vaab.blog.kal.fr/2017/03/16/fixing-windows-python-2-7-unicode-issue-with-subprocesss-popen/

Ответ 2

Документы для sys.getfilesystemencoding() говорят, что для Windows NT и более поздних версий имена файлов являются в основном Unicode. Если у вас есть допустимое имя файла в юникоде, зачем вам его кодировать с помощью mbcs?

Документы для модуля кодеков говорят, что mbcs кодирует с использованием "кодовой страницы ANSI" (которая будет отличаться в зависимости от локали пользователя), поэтому, если локаль не использует кириллические символы, splat.

Изменить: Таким образом, ваш процесс вызывает subprocess.Popen(). Если ваш вызываемый процесс находится под вашим контролем, два процесса могут быть согласны использовать UTF-8 в качестве формата Unicode Transport Format. В противном случае вам может потребоваться задать список рассылки pywin32. В любом случае отредактируйте свой вопрос, чтобы указать степень контроля над вызываемым процессом.

Ответ 3

Если вам нужно передать имя существующего файла, у вас может быть больше шансов на успех, передав версию файла Unicode 8.3.

У вас должен быть установлен pywin32 пакет, который вы можете сделать:

>>> import win32api
>>> win32api.GetShortPathName(u"C:\\Program Files")
'C:\\PROGRA~1'

Я считаю, что эти короткие имена файлов используют только символы ASCII, и поэтому вы должны иметь возможность использовать их в качестве аргументов в командной строке.

Если вам нужно указать также имена файлов, которые нужно создать, вы можете создать их с нулевым размером заранее из Python с использованием имен файлов Unicode и передать краткое имя файла в качестве аргумента.

ОБНОВЛЕНИЕ: пользователь bogdan правильно говорит о том, что генерация имени файла 8.3 может быть отключена (я также отключил ее, когда у меня был Windows XP на моем ноутбуке), поэтому вы не можете полагаться на них. Таким образом, в качестве еще более надуманного подхода при работе с томами NTFS можно жесткую ссылку имена файлов Unicode на простые ASCII; передайте имена файлов ASCII во внешнюю команду и затем удалите их.

Ответ 4

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Я автор исправления, упомянутого ниже.

Чтобы поддерживать командную строку unicode в окнах с помощью python 2.7, вы можете использовать этот патч до subprocess.Popen(..)

Ситуация

Поддержка Python 2 командной строки unicode в окнах очень плохая.

Серьезно прослушиваются:

  • выдача командной строки юникода в систему со стороны вызывающего абонента (через subprocess.Popen(..)),

  • и чтение текущих аргументов unicode командной строки со стороны вызываемого абонента (через sys.argv),

Подтверждено и не будет исправлено на Python 2. Они исправлены в Python 3.

Технические причины

В Python 2 реализация windows subprocess.Popen(..) и sys.argv использует системы с незашифрованными готовыми окнами CreateProcess(..) (см. python code и MSDN doc CreateProcess) и не использует GetCommandLineW(..) для sys.argv.

В Python 3 реализация windows subprocess.Popen(..) использует правильные системные вызовы Windows CreateProcessW(..), начиная с 3.0 (см. code in 3.0) и sys.argv использует GetCommandLineW(..), начиная с 3.3 (см. code в 3.3).

Как это исправлено

Данный patch будет использовать модуль ctypes для вызова окон C системы CreateProcessW(..). Он предлагает новый фиксированный объект Popen путем переопределения частного метода Popen._execute_child(..) и частной функции _subprocess.CreateProcess(..) для установки и использования CreateProcessW(..) из системной библиотеки Windows таким образом, чтобы максимально имитировать, как это делается в Python 3.6.

Как использовать его

Как использовать данный патч, демонстрируется с этим объяснением блога. Он также показывает, как читать текущие процессы sys.argv с другое исправление.