В моем приложении django я предоставляю форму, которая позволяет пользователям загружать файл. Файл может быть в различных форматах (Excel, CSV), из разных платформ (Mac, Linux, Windows) и кодироваться в различных кодировках (ASCII, UTF-8).
В целях этого вопроса предположим, что у меня есть представление, которое принимает request.FILES['file']
, который является экземпляром InMemoryUploadedFile
, называемым file
. Моя проблема в том, что объекты InMemoryUploadedFile
(например, file
):
- Не поддерживайте кодировку UTF-8 (я вижу a
\xef\xbb\xbf
в начале файла, что, как я понимаю, означает флаг "этот файл UTF-8" ). - Не поддерживайте универсальные новые строки (что, вероятно, потребуется большинству файлов, загруженных в эту систему).
Усложнение проблемы заключается в том, что я хочу передать файл в модуль python csv
, который не поддерживает Unicode. Я с радостью соглашусь с ответами, которые избегают этой проблемы - как только я получу django, играю хорошо с UTF-8, я уверен, что смогу сделать дубль csv
в то же самое. (Аналогично, пожалуйста, проигнорируйте требование поддержки 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
.
EDIT: Оказывается, приведенный выше код действительно работает. codecs.EncodedFile(file,"utf-8")
- билет. Оказывается, причина, по которой я думал, что это не сработало, заключается в том, что терминал, который я использовал, не поддерживает UTF-8. Живи и учись!