Страницы

Поиск по вопросам

среда, 13 февраля 2019 г.

Как получить файл (кириллица в имени) с FTP?

Python 3.5, ftplib
В каком виде ftp.nlst() получает имена файлов? И как их привести к нормальному виду?
filelist = ftp.nlst() - получаем список имен файлов print (filelist[3]) - выводим имя 4-ого файла и получаем иероглифы
@jfs >>> print(ascii(filelist[6])) '\xc8\xed\xf1\xf2\xf0\xf3\xea\xf6\xe8\xff.pdf'
Спасибо за подробный ответ)


Ответ

Изначально (RFC 765, 959), FTP только 7-бит ASCII поддерживал, RFC 2640 расширяет поддержку до других кодировок, RFC 3659 уточняет, что команды такие как MLST могут вернуть пути либо в UTF-8 кодировке, либо это может быть произвольная каша байтов—за некоторыми исключениями такими как CRLF (новая строка, b'\x0d\x0a'), одинокий Telnet IAC (b'\xff').
На POSIX имена файлов могут быть произвольной последовательностью байтов за исключением b'\x2f' и b\x00' (слэш и ноль).
В Питоне 3, ftplib формально использует latin-1 кодировку по умолчанию, которая произвольную последовательность байт позволяет в Unicode декодировать.
В: В каком виде ftp.nlst() получает имена файлов?
ftp.nlst() возвращает список текстовых (Unicode) строк.
В: И как их привести к нормальному виду?
Если вы знаете, что сервер использует единственную кодировку для имён файлов в вашем случае (вероятно, utf-8, если вывод команды feat её показывает) и нет непредставимых имён (PEP 383), то правильные имена можно получить декодированием:
filename = filename.encode(ftp.encoding).decode(your_encoding)
Можно передать 'surrogateescape' обработчик ошибок в .decode(), чтобы поддерживать и непредставимые имена (чтобы была возможность без потерь восстановить изначальные байты, одновременно позволяя печатать представимые имена в «нормальном виде»).

Судя по результату ascii(), в вашем случае ftp-сервер возвращает имена в cp1251 кодировке (ANSI codepage на русской Винде):
>>> print(ascii(filelist[6])) '\xc8\xed\xf1\xf2\xf0\xf3\xea\xf6\xe8\xff'
В этом случае, your_encoding='cp1251', чтобы получить исходное имя:
filename = filelist[6].encode(ftp.encoding).decode('cp1251') # -> 'Инструкция'
Чтобы зря не перекодировать, можно перед вызовом ftp.nlst() выставить ftp.encoding = 'cp1251', если известно что все имена файлов в этой кодировке представимы. Тогда ftp.nlst() сразу вернёт правильно декодированные имена.

Комментариев нет:

Отправить комментарий