Как правильно обрабатывать строки Unicode Python с нулевыми байтами?

Вопрос

Кажется, что PyWin32 комфортно предоставляет строки Unicode с завершающим нулем в качестве возвращаемых значений. Я бы хотел «правильно» обращаться с этими струнами.

Скажем, я получаю строку вида: u'C:\\Users\\Guest\\MyFile.asy\x00\x00sy'. Похоже, что это строка в стиле C с завершающим нулем, висящая в объекте Unicode Python. Я хочу обрезать этого плохого парня до обычной строки символов, которую я мог бы, например, отобразить в строке заголовка окна.

Обрезка строки по первому нулевому байту - правильный способ справиться с этим?

Я не ожидал получить такое возвращаемое значение, поэтому мне интересно, упускаю ли я что-то важное о том, как Python, Win32 и unicode работают вместе ... или это просто ошибка PyWin32.

Фон

Я использую функцию выбора файлов Win32 GetOpenFileNameW из пакета PyWin32. Согласно документации, эта функция возвращает кортеж, содержащий полный путь к имени файла, как объект Unicode Python.

Когда я открываю диалог с существующим путем и установленным именем файла, я получаю странное возвращаемое значение.

Например, у меня было значение по умолчанию: C:\\Users\\Guest\\MyFileIsReallyReallyReallyAwesome.asy

В диалоговом окне я изменил имя на MyFile.asy и нажал «Сохранить».

Часть полного пути возвращаемого значения была: u'C: \ Users \ Guest \ MyFile.asy \ x00wesome.asy ''

Я ожидал, что это будет: u'C:\\Users\\Guest\\MyFile.asy'

Функция возвращает переработанный буфер без обрезки завершающих байтов. Излишне говорить, что остальная часть моего кода не была настроена для обработки строки в стиле C с завершающим нулем.

Демо-код

Следующий код демонстрирует строку с завершающим нулем в возвращаемом значении из GetSaveFileNameW.

Инструкции: в диалоговом окне измените имя файла на «MyFile.asy», затем нажмите «Сохранить». Наблюдайте за тем, что печатается на консоли. На выходе я получаю u'C:\\Users\\Guest\\MyFile.asy\x00wesome.asy'.

import win32gui, win32con

if __name__ == "__main__":
    initial_dir = 'C:\\Users\\Guest'
    initial_file = 'MyFileIsReallyReallyReallyAwesome.asy'
    filter_string = 'All Files\0*.*\0'
    (filename, customfilter, flags) = \
        win32gui.GetSaveFileNameW(InitialDir=initial_dir,
                    Flags=win32con.OFN_EXPLORER, File=initial_file,
                    DefExt='txt', Title="Save As", Filter=filter_string,
                    FilterIndex=0)
    print repr(filename)

Примечание. Если вы недостаточно сократите имя файла (например, если вы попробуете MyFileIsReally.asy), строка будет полной без нулевого байта.

Окружающая среда

64-разрядная версия Windows 7 Professional (без пакета обновления), Python 2.7.1, PyWin32 Build 216

ОБНОВЛЕНИЕ: артефакт трекера PyWin32

Судя по комментариям и ответам, которые я получил до сих пор, это, вероятно, ошибка pywin32, поэтому я отправил артефакт трекера.

ОБНОВЛЕНИЕ 2: исправлено!

Марк Хаммонд сообщил в артефакте трекера, что это действительно ошибка. Было зарегистрировано исправление для rev f3fdaae5e93d, так что, надеюсь, оно будет сделано в следующем выпуске.

Я думаю, что приведенный ниже ответ Алекси Торхамо - лучшее решение для версий PyWin32 до исправления.


person Steven T. Snyder    schedule 05.04.2011    source источник
comment
+1 Ха, похоже, вы нашли ошибку PyWin32!   -  person Katriel    schedule 06.04.2011
comment
Я написал Марку Хаммонду (основному разработчику PyWin32) небольшую заметку, отсылающую его к этой странице.   -  person Tom Zych    schedule 06.04.2011
comment
@Tom Спасибо! Я также подал сюда артефакт трекера: sourceforge.net/tracker/   -  person Steven T. Snyder    schedule 06.04.2011


Ответы (3)


Я бы сказал, что это ошибка. Правильный способ справиться с этим, вероятно, будет исправить pywin32, но если вы не чувствуете себя достаточно предприимчивым, просто обрезайте его.

Вы можете получить все до первого '\x00' с filename.split('\x00', 1)[0].

person Aleksi Torhamo    schedule 05.04.2011
comment
Вы правы, Марк Хаммонд (сопровождающий PyWin32) подтвердил, что это ошибка, и исправил ее в Rev f3fdaae5e93d, поэтому я думаю, что это должно исправить это в следующем выпуске. См. sourceforge.net/tracker/. - person Steven T. Snyder; 29.04.2011

Этого не происходит в версии PyWin32 / Windows / Python, которую я тестировал; Я не получаю нулей в возвращаемой строке, даже если она очень короткая. Вы можете проверить, исправляет ли ошибка более новая версия одного из вышеперечисленных.

person Nicholas Riley    schedule 05.04.2011
comment
Какая у вас конфигурация? Я использую Python 2.7.1 и PyWin32 Build 216 (последняя версия) в Windows 7 Pro (без пакета обновления). - person Steven T. Snyder; 06.04.2011
comment
Я использую Python 2.6.5 в Windows 7 Enterprise x64 с той же сборкой PyWin32. Может дело в 32 против 64-битных? - person Nicholas Riley; 06.04.2011
comment
На самом деле я тоже использую 64-битную Windows. Однако я не использую 64-битную сборку Python. - person Steven T. Snyder; 06.04.2011
comment
Ага, может быть. Я использую 64-битный Python (и Win 7 SP1, о котором я забыл упомянуть). В любом случае, определенно звучит как проблема PyWin32, о которой стоит сообщить. - person Nicholas Riley; 06.04.2011

ISTR, что у меня была эта проблема несколько лет назад, затем я обнаружил, что такие функции Win32, связанные с диалогом имени файла, возвращают последовательность 'filename1\0filename2\0...filenameN\0\0', включая возможные символы мусора в зависимости от буфера, выделенного Windows.

Теперь вы можете предпочесть список вместо необработанного возвращаемого значения, но это будет RFE, а не ошибка.

PS Когда у меня возникла эта проблема, я вполне понял, почему можно было ожидать, что GetOpenFileName, возможно, вернет список имен файлов, в то время как я не мог представить, почему GetSaveFileName. Возможно, это считается единообразием API. Да и кого я должен знать?

person tzot    schedule 28.04.2011
comment
Привет, ΩΤΖΙΟΥ, разделенная нулем последовательность для выбора нескольких файлов - это задокументированное поведение, так что это не проблема. Проблема, описанная в моем вопросе, заключалась в выборе одного имени файла. В таком случае я бы не ожидал, что там будут нулевые символы, поскольку в документации указано, что возвращаемое значение будет полным путем. Я зарегистрировал артефакт трекера для ошибки, и теперь он исправлен в коде разработки вместе с уточненной документацией. - person Steven T. Snyder; 04.05.2011