Использование csv.DictWriter для вывода сжатого файла CSV в памяти? - PullRequest
0 голосов
/ 06 февраля 2019

Я хочу использовать DictWriter из модуля csv Python для генерации CSV-файла, сжатого с помощью GZip.Мне нужно сделать все это в оперативной памяти, поэтому об использовании локальных файлов не может быть и речи.

Однако у меня возникают проблемы с требованиями к типу каждого модуля в Python 3. Предполагая, что я получил общую структуруправильно, я не могу заставить оба модуля работать вместе, потому что DictWriter нужно записать в буфер io.StringIO, а GZip нужен объект io.BytesIO.

Итак, когда я пытаюсь сделать:

buffer = io.BytesIO()
compressed = gzip.GzipFile(fileobj=buffer, mode='wb')
dict_writer = csv.DictWriter(buffer, ["a", "b"], extrasaction="ignore")

Я получаю:

TypeError: a bytes-like object is required, not 'str'

И попытка использовать io.StringIO с GZip также не работает.Как я могу пойти по этому поводу?

Ответы [ 2 ]

0 голосов
/ 06 февраля 2019

Вы можете использовать io.TextIOWrapper для плавного преобразования текстового потока в двоичный:

import io
import gzip
import csv
buffer = io.BytesIO()
with gzip.GzipFile(fileobj=buffer, mode='wb') as compressed:
    with io.TextIOWrapper(compressed, encoding='utf-8') as wrapper:
        dict_writer = csv.DictWriter(wrapper, ["a", "b"], extrasaction="ignore")
        dict_writer.writeheader()
        dict_writer.writerows([{'a': 1, 'b': 2}, {'a': 4, 'b': 3}])
print(buffer.getvalue()) # dump the compressed binary data
buffer.seek(0)
dict_reader = csv.DictReader(io.TextIOWrapper(gzip.GzipFile(fileobj=buffer, mode='rb'), encoding='utf-8'))
print(list(dict_reader)) # see if uncompressing the compressed data gets us back what we wrote

Это выводит:

b'\x1f\x8b\x08\x00\x9c6[\\\x02\xffJ\xd4I\xe2\xe5\xe52\xd41\x02\x92&:\xc6@\x12\x00\x00\x00\xff\xff\x03\x00\x85k\xa2\x9e\x12\x00\x00\x00'
[OrderedDict([('a', '1'), ('b', '2')]), OrderedDict([('a', '4'), ('b', '3')])]
0 голосов
/ 06 февраля 2019

Обходным путем будет сначала записать его в объект io.StringIO, а затем преобразовать содержимое обратно в io.BytesIO:

s = io.StringIO()
b = io.BytesIO()

dict_writer = csv.DictWriter(s, ["a", "b"], extrasaction="ignore")

... # complete your write operations ...

s.seek(0)  # reset cursor to the beginning of the StringIO stream
b.write(s.read().encode('utf-8')) # or an encoding of your choice

compressed = gzip.GzipFile(fileobj=b, mode='wb')

... 

s.close()   # Remember to close your streams!
b.close()

Хотя, как предполагает комментарий @ wwii, в зависимости от размераваши данные, возможно, более целесообразно вместо этого написать свои csv в bytes.

...