Определение кодировки файла, загруженного в Google App Engine - PullRequest
1 голос
/ 22 января 2012

У меня есть веб-сайт на основе GAE и Python, и я бы хотел, чтобы пользователь мог загружать текстовый файл для обработки.Моя реализация основана на стандартном коде из документов (см. http://code.google.com/appengine/docs/python/blobstore/overview.html), и мой обработчик загрузки текстового файла по сути выглядит так:

class Uploader(blobstore_handlers.BlobstoreUploadHandler):
    def post(self):
        upload_files = self.get_uploads('file')
        blob_info = upload_files[0]
        blob_reader = blobstore.BlobReader(blob_info.key())
        for line in blob_reader:
            line = line.rstrip().decode('cp1252')
            do_something(line)
        blob_reader.close()

Это прекрасно работает для текстового файла, закодированного с помощью кодовой страницы 1252Это то, что вы получаете, когда используете Windows Notepad и сохраняете с так называемой кодировкой «ANSI». Но если вы используете этот обработчик с файлом, который был сохранен с кодировкой UTF-8 Notepad и содержит, скажем, некоторые символы кириллицыили u-umlaut, вы в конечном итоге с бредом. Для такого файла, изменение декодирования ('cp1252') для декодирования ('utf_8') добьется цели. (Ну, есть также возможность метки порядка байтов(Спецификация) в начале, но это легко убрать.)

Но как вы узнаете, какое декодирование использовать? Спецификация не гарантирована, и я не вижу другого способазнаете, кроме того, чтобы спросить пользователя - который, вероятно, тоже не знает. Есть ли надежный метод для определения кодировки? Мне не обязательно использовать Blobstore, если некоторые otее средство решает ее.

И еще есть кодировка, которую Windows Notepad называет «Unicode», которая представляет собой кодировку UTF-16 с прямым порядком байтов.Я не смог найти никакого декодирования (включая «utf_16_le»), которое правильно декодирует файл, сохраненный с этой кодировкой.Можно ли прочитать один из этих файлов?

Ответы [ 2 ]

3 голосов
/ 22 января 2012
1 голос
/ 23 января 2012

После ответа от demalexx мой обработчик загрузки теперь определяет кодировку, используя chardet (http://pypi.python.org/pypi/chardet), которая, насколько я могу судить, работает очень хорошо. По пути я обнаружил, что использование «for line in blob_reader» для чтения загруженных текстовых файлов чрезвычайно проблематично. Вместо этого, если вы не возражаете, прочитав весь файл одним глотком, решение легко. (Обратите внимание на удаление одной последовательности спецификации и разбиение линий по CR / LF.)

class Uploader(blobstore_handlers.BlobstoreUploadHandler):
    def post(self):
        upload_files = self.get_uploads('file')
        blob_info = upload_files[0]
        text = blobstore.BlobReader(blob_info.key()).read()
        encoding = chardet.detect(text)['encoding']
        if encoding is not None:
            for line in text.decode(encoding).lstrip(u'\ufeff').split(u'\x0d\x0a'):
                do_something(line)

Если вы хотите прочитать по частям из загруженного файла, вас ждет мир боли. Проблема в том, что «for line in blob_reader», по-видимому, читает до того места, где найден байт перевода строки (\ x0a), что губительно при чтении файла в кодировке utf_16_le, так как он разбивает последовательность \ x0a \ x00 пополам!

Я не рекомендую это, но вот обработчик загрузки, который будет успешно обрабатывать файлы, хранящиеся во всех кодировках в Блокноте Windows 7 (а именно, ANSI, UTF-8, Unicode и Unicode с прямым порядком байтов), по очереди. Как видите, удаление последовательностей завершения строки является громоздким.

class Uploader(blobstore_handlers.BlobstoreUploadHandler):
    def post(self):
        upload_files = self.get_uploads('file')
        blob_info = upload_files[0]
        blob_reader = blobstore.BlobReader(blob_info.key())
        encoding = chardet.detect(blob_reader.read(10000))['encoding']
        if encoding is not None:
            blob_reader.seek(0)
            for line in blob_reader:
                if line[:2] in ['\xff\xfe','\xfe\xff']:
                    start = 2
                elif line[:3] == '\xef\xbb\xbf':
                    start = 3
                else:
                    start = 0
                if encoding == 'UTF-16BE':
                    if line[-4:] == '\x00\x0d\x00\x0a':
                        line = line[start:-4]
                    elif start > 0:
                        line = line[start:]
                elif encoding == 'UTF-16LE':
                    if line[start] == '\x00':
                        start += 1
                    if line[-3:] == '\x0d\x00\x0a':
                        line = line[start:-3]
                    elif start > 0:
                        line = line[start:]
                elif line[-2:] == '\x0d\x0a':
                    line = line[start:-2]
                elif start > 0:
                    line = line[start:]
                do_something(line.decode(encoding))

Это, несомненно, хрупко, и мои тесты были ограничены этими четырьмя кодировками, и только для того, как Windows 7 Notepad создает файлы. Обратите внимание, что перед чтением строки я собираю до 10000 символов для анализа chardet. Это только предположение относительно того, сколько байтов может понадобиться. Это неуклюжее двойное чтение - еще одна причина, чтобы избежать этого решения.

...