конвертировать io.StringIO в io.BytesIO - PullRequest
12 голосов
/ 28 апреля 2019

оригинальный вопрос: я получил объект StringIO , как я могу преобразовать его в BytesIO ?

обновление: Более общий вопрос: как преобразовать двоичный (закодированный) файлоподобный объект в декодированный файлоподобный объект в python3?

Наивный подход, который я получил:

import io
sio = io.StringIO('wello horld')
bio = io.BytesIO(sio.read().encode('utf8'))
print(bio.read())  # prints b'wello horld'

Есть ли более элегантный способ сделать это?

например, для обратного вопроса (BytesIO -> StringIO) существует класс - io.TextIOWrapper , который делает именно это (см. Этот ответ )

Ответы [ 5 ]

2 голосов
/ 06 мая 2019

Интересно, что, хотя вопрос может показаться разумным, не так просто найти практическую причину, по которой мне нужно было бы преобразовать StringIO в BytesIO. Оба они в основном являются буферами, и вам обычно требуется только один из них для дополнительных манипуляций с байтами или текстом.

Возможно, я ошибаюсь, но я думаю, что ваш вопрос на самом деле заключается в том, как использовать экземпляр BytesIO, когда некоторый код, которому вы хотите передать его, ожидает текстовый файл.

В этом случае это общий вопрос, и решение - codecs module.

Два обычных случая его использования следующие:

Создание объекта файла для чтения

In [16]: import codecs, io

In [17]: bio = io.BytesIO(b'qwe\nasd\n')

In [18]: StreamReader = codecs.getreader('utf-8')  # here you pass the encoding

In [19]: wrapper_file = StreamReader(bio)

In [20]: print(repr(wrapper_file.readline()))
'qwe\n'

In [21]: print(repr(wrapper_file.read()))
'asd\n'

In [26]: bio.seek(0)
Out[26]: 0

In [27]: for line in wrapper_file:
    ...:     print(repr(line))
    ...:
'qwe\n'
'asd\n'

Создание объекта файла для записи в

In [28]: bio = io.BytesIO()

In [29]: StreamWriter = codecs.getwriter('utf-8')  # here you pass the encoding

In [30]: wrapper_file = StreamWriter(bio)

In [31]: print('жаба', 'цап', file=wrapper_file)

In [32]: bio.getvalue()
Out[32]: b'\xd0\xb6\xd0\xb0\xd0\xb1\xd0\xb0 \xd1\x86\xd0\xb0\xd0\xbf\n'

In [33]: repr(bio.getvalue().decode('utf-8'))
Out[33]: "'жаба цап\\n'"
2 голосов
/ 04 мая 2019

@ foobarna ответ может быть улучшен путем наследования некоторого io базового класса

import io
sio = io.StringIO('wello horld')


class BytesIOWrapper(io.BufferedReader):
    """Wrap a buffered bytes stream over TextIOBase string stream."""

    def __init__(self, text_io_buffer, encoding=None, errors=None, **kwargs):
        super(BytesIOWrapper, self).__init__(text_io_buffer, **kwargs)
        self.encoding = encoding or text_io_buffer.encoding or 'utf-8'
        self.errors = errors or text_io_buffer.errors or 'strict'

    def _encoding_call(self, method_name, *args, **kwargs):
        raw_method = getattr(self.raw, method_name)
        val = raw_method(*args, **kwargs)
        return val.encode(self.encoding, errors=self.errors)

    def read(self, size=-1):
        return self._encoding_call('read', size)

    def read1(self, size=-1):
        return self._encoding_call('read1', size)

    def peek(self, size=-1):
        return self._encoding_call('peek', size)


bio = BytesIOWrapper(sio)
print(bio.read())  # b'wello horld'
2 голосов
/ 01 мая 2019

Это может быть в целом полезный инструмент для преобразования потока символов в поток байтов, поэтому здесь идет речь:

import io

class EncodeIO(io.BufferedIOBase):
  def __init__(self,s,e='utf-8'):
    self.stream=s               # not raw, since it isn't
    self.encoding=e
    self.buf=b""                # encoded but not yet returned
  def _read(self,s): return self.stream.read(s).encode(self.encoding)
  def read(self,size=-1):
    b=self.buf
    self.buf=b""
    if size is None or size<0: return b+self._read(None)
    ret=[]
    while True:
      n=len(b)
      if size<n:
        b,self.buf=b[:size],b[size:]
        n=size
      ret.append(b)
      size-=n
      if not size: break
      b=self._read(min((size+1024)//2,size))
      if not b: break
    return b"".join(ret)
  read1=read

Очевидно, write может быть определено симметрично для декодирования ввода и отправки его в базовыйпоток, хотя тогда вам придется иметь дело с достаточным количеством байтов только для части символа.

1 голос
/ 03 мая 2019

Как отмечали некоторые, вам нужно самостоятельно выполнить кодирование / декодирование.

Однако вы можете добиться этого элегантным способом - реализовать свой собственный TextIOWrapper для string => bytes.

Вот такой пример:

class BytesIOWrapper:
    def __init__(self, string_buffer, encoding='utf-8'):
        self.string_buffer = string_buffer
        self.encoding = encoding

    def __getattr__(self, attr):
        return getattr(self.string_buffer, attr)

    def read(self, size=-1):
        content = self.string_buffer.read(size)
        return content.encode(self.encoding)

    def write(self, b):
        content = b.decode(self.encoding)
        return self.string_buffer.write(content)

, который выдает такой результат:

In [36]: bw = BytesIOWrapper(StringIO("some lengt˙˚hyÔstring in here"))

In [37]: bw.read(15)
Out[37]: b'some lengt\xcb\x99\xcb\x9ahy\xc3\x94'

In [38]: bw.tell()
Out[38]: 15

In [39]: bw.write(b'ME')
Out[39]: 2

In [40]: bw.seek(15)
Out[40]: 15

In [41]: bw.read()
Out[41]: b'MEring in here'

Надеюсь, он прояснит ваши мысли!

0 голосов
/ 01 мая 2019

bio из вашего примера это _io.BytesIO объект класса. Вы использовали 2 раза функцию read().

Я придумал bytes преобразование и один read() метод:

sio = io.StringIO('wello horld')
b = bytes(sio.read(), encoding='utf-8')
print(b)

Но второй вариант должен быть еще быстрее:

sio = io.StringIO('wello horld')
b = sio.read().encode()
print(b)
...