В моем приложении django я предоставляю форму, которая позволяет пользователям загружать файл.Файл может быть в различных форматах (Excel, CSV), поступать с различных платформ (Mac, Linux, Windows) и кодироваться в различных кодировках (ASCII, UTF-8).
Для целей этого вопроса давайте предположим, что у меня есть представление, которое получает request.FILES['file']
, который является экземпляром InMemoryUploadedFile
, называемым file
.Моя проблема в том, что InMemoryUploadedFile
объекты (например, file
):
- Не поддерживают кодировку UTF-8 (я вижу
\xef\xbb\xbf
в начале файла, что, как я понимаю,флаг, означающий «этот файл - UTF-8»). - Не поддерживает универсальные символы новой строки (которые, вероятно, понадобятся большинству файлов, загружаемых в эту систему).
Сложнопроблема в том, что я хочу передать файл в модуль python csv
, который изначально не поддерживает Unicode.Я с радостью приму ответы, которые помогут избежать этой проблемы - как только я получу джанго, играющий хорошо с UTF-8, я уверен, что смогу сделать то же самое.(Точно так же, пожалуйста, игнорируйте требование поддержки Excel - я жду, пока CSV заработает, прежде чем приступить к анализу файлов Excel.)
Я попытался использовать StringIO
, mmap
, codec
и любой изширокий спектр способов доступа к данным в InMemoryUploadedFile
объекте.Каждый подход привел к различным ошибкам, но ни одна из них не была идеальной.Это показывает некоторый код, который мне кажется наиболее близким:
import csv
import codecs
class CSVParser:
def __init__(self,file):
# 'file' is assumed to be an InMemoryUploadedFile object.
dialect = csv.Sniffer().sniff(codecs.EncodedFile(file,"utf-8").read(1024))
file.open() # seek to 0
self.reader = csv.reader(codecs.EncodedFile(file,"utf-8"),
dialect=dialect)
try:
self.field_names = self.reader.next()
except StopIteration:
# The file was empty - this is not allowed.
raise ValueError('Unrecognized format (empty file)')
if len(self.field_names) <= 1:
# This probably isn't a CSV file at all.
# Note that the csv module will (incorrectly) parse ALL files, even
# binary data. This will catch most such files.
raise ValueError('Unrecognized format (too few columns)')
# Additional methods snipped, unrelated to issue
Обратите внимание, что я не потратил слишком много времени на фактический алгоритм синтаксического анализа, поэтому он может быть дико неэффективным, сейчас яболее важно, чтобы кодирование работало должным образом.
Проблема заключается в том, что результаты также не кодируются, несмотря на то, что они помещены в оболочку файла Unicode codecs.EncodedFile
.
РЕДАКТИРОВАТЬ: Оказывается, приведенный выше код действительно работает.codecs.EncodedFile(file,"utf-8")
это билет.Оказывается, причина, по которой я думал, что это не сработало, заключалась в том, что используемый мной терминал не поддерживает UTF-8.Живи и учись!