После ответа от 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. Это только предположение относительно того, сколько байтов может понадобиться. Это неуклюжее двойное чтение - еще одна причина, чтобы избежать этого решения.