Библиотека io в Python для ввода и вывода

Раздел: Стандартная библиотека Python -> Работа с вводом/выводом

Введение в библиотеку io для ввода/вывода

Модуль io из стандартной библиотеки Python предоставляет инструменты для работы с потоками данных. Он используется как основа для встроенной функции open() и позволяет гибко управлять буферизацией, кодировками и типами данных (текстовые или бинарные).

Основное решение: StringIO и BytesIO

Как сохранить данные в памяти, имитируя файловый объект?

Классы io.StringIO и io.BytesIO позволяют работать с буферами в оперативной памяти как с файлами. Они поддерживают те же методы (read, write, seek, tell), что и обычные файловые объекты.


import io

# Текстовый буфер
buf = io.StringIO()
buf.write('Привет, мир!')
buf.seek(0)
content = buf.read()
print(content)  # Привет, мир!
buf.close()
  

библиотека io python (библиотека io в python (ввод/вывод))

Привет, мир!
  

Аналогично для бинарных данных используется io.BytesIO:


binary_buf = io.BytesIO()
binary_buf.write(b'\x00\x01\x02')
binary_buf.seek(0)
data = binary_buf.read()
binary_buf.close()
  

Типичные ошибки:

  • Запись текста в BytesIO (нужно передавать bytes). Решение: явное преобразование через .encode().
  • Чтение после записи без сброса позиции (seek). Решение: перед чтением вызвать seek(0).
  • Незакрытие потока - хотя для коротких скриптов это не критично, лучше использовать менеджер контекста with.

Вариант 1: Использование io.StringIO для обработки текста

Как обработать многострочный текст в памяти без создания временного файла?


import io

text = """Строка1
Строка2
Строка3"""
buf = io.StringIO(text)
for line in buf:
    print(line.strip())
  
Строка1
Строка2
Строка3
  

Проблема: забытая новая строка в конце - read() возвращает весь текст, включая лишние символы. Решение: использовать rstrip().

Вариант 2: io.BytesIO для бинарных данных

Как записать изображение или сериализованный объект в байтовый массив?


import io
import json

data = {'key': 'value'}
buf = io.BytesIO()
buf.write(json.dumps(data).encode('utf-8'))
buf.seek(0)
result = json.loads(buf.read())
print(result)
  
{'key': 'value'}
  

Вариант 3: Буферизированные потоки (BufferedReader, BufferedWriter)

Как увеличить производительность при чтении/записи большого объёма данных?

Классы io.BufferedReader и io.BufferedWriter оборачивают низкоуровневый поток (например, FileIO) и добавляют буферизацию. Обычно они используются автоматически функцией open(), но можно создать вручную.


import io

# Низкоуровневый файловый объект
raw = io.FileIO('example.bin', 'wb')
buffered = io.BufferedWriter(raw, buffer_size=8192)
buffered.write(b'Hello' * 1000)
buffered.flush()
buffered.close()
  

Частая ошибка: забытый flush() - данные могут остаться в буфере. Решение: использование менеджера контекста, который автоматически сбрасывает буфер.

Вариант 4: TextIOWrapper для работы с текстом поверх бинарного потока

Как читать текстовый файл с нестандартной кодировкой или указать обработку ошибок?

io.TextIOWrapper оборачивает бинарный поток в текстовый с заданной кодировкой. Удобен, когда нужно вручную управлять буферизацией при текстовом вводе/выводе.


import io

raw = io.BytesIO(b'\xd0\x9f\xd1\x80\xd0\xb8\xd0\xb2\xd0\xb5\xd1\x82')
text_wrapper = io.TextIOWrapper(raw, encoding='utf-8')
print(text_wrapper.read())
  
Привет
  

Ошибка: несоответствие кодировки. Решение: указать параметр encoding или использовать errors='replace'.

Вариант 5: Позиционирование в потоке (seek, tell)

Как перейти к определённому месту в данных и узнать текущую позицию?

Методы seek(offset, whence) и tell() работают одинаково для всех потоков. Параметр whence может быть io.SEEK_SET (0 – начало), io.SEEK_CUR (1 – текущая), io.SEEK_END (2 – конец).


import io

buf = io.BytesIO(b'0123456789')
buf.seek(5)
print(buf.read(3))  # b'567'
buf.seek(-2, io.SEEK_END)
print(buf.read())   # b'89'
  
b'567'
b'89'
  

Расширенные примеры использования библиотеки io

Пример 1: Создание собственного потока на основе RawIOBase

Наследуясь от io.RawIOBase, можно реализовать поток, который читает данные из нестандартного источника (например, базы данных или генератора).

Пример

import io

class SimpleStream(io.RawIOBase):
    def __init__(self, data):
        self.data = data
        self.pos = 0

    def readable(self):
        return True

    def read(self, size=-1):
        if size < 0:
            rest = self.data[self.pos:]
            self.pos = len(self.data)
            return rest
        else:
            chunk = self.data[self.pos:self.pos+size]
            self.pos += len(chunk)
            return chunk

    def readinto(self, b):
        chunk = self.read(len(b))
        b[:len(chunk)] = chunk
        return len(chunk)

raw = SimpleStream(b'Testing raw stream')
buffered = io.BufferedReader(raw)
print(buffered.read(7))
print(buffered.read())
b'Testing'
b' raw stream'

Пример 2: Работа с архивами в памяти (gzip + BytesIO)

Сжатие и распаковка данных напрямую в байтовом буфере без создания файлов.

Пример

import io
import gzip

# Запись сжатых данных
buf = io.BytesIO()
with gzip.GzipFile(fileobj=buf, mode='wb') as gz:
    gz.write(b'Compressed content')

# Чтение сжатых данных
buf.seek(0)
with gzip.GzipFile(fileobj=buf, mode='rb') as gz:
    decompressed = gz.read()
print(decompressed)
b'Compressed content'

Пример 3: Использование TextIOWrapper для обработки разных кодировок

Чтение данных, закодированных в CP1251, с заменой неподдерживаемых символов.

Пример

import io

raw = io.BytesIO(b'\xcf\xf0\xee\xe2\xe5\xf0\xea\xe0')
wrapper = io.TextIOWrapper(raw, encoding='cp1251', errors='replace')
print(wrapper.readline())
Проверка

Пример 4: Комбинация BufferedRandom для произвольного доступа

io.BufferedRandom позволяет одновременно читать и писать в бинарный поток.

Пример

import io

buf = io.BytesIO(b'Hello World')
buffered = io.BufferedRandom(buf)
buffered.seek(6)
buffered.write(b'Python')
buffered.seek(0)
print(buffered.read())
b'Hello Python'

Пример 5: Использование io.open для явного управления буферизацией

Функция io.open() позволяет задать размер буфера и политику кэширования.

Пример

import io

with io.open('test.txt', 'w', buffering=1024*1024) as f:
    f.write('Large buffer test')

with io.open('test.txt', 'r', buffering=1) as f:
    print(f.read())
Large buffer test

Пример 6: Поток с возможностью повторного чтения (io.BufferedReader с позицией)

Сброс позиции после чтения – использование seek(0) для повторного чтения.

Пример

import io

data = b'Repeatable'
buf = io.BytesIO(data)
buff = io.BufferedReader(buf)
print(buff.read())
buff.seek(0)
print(buff.read())
b'Repeatable'
b'Repeatable'

Пример 7: Обработка ошибок кодировки при записи через StringIO

При записи в текстовый поток можно указать стратегию обработки ошибок (строгий режим, замена, игнорирование).

Пример

import io

# Пример с ошибкой – символ вне Latin-1
buf = io.StringIO()
try:
    buf.write('\u2603')  # снеговик вне Latin-1
    # Если поток имеет кодировку ascii, произойдёт ошибка
    # Но StringIO не привязан к кодировке, поэтому ошибки нет
except Exception as e:
    print(f'Ошибка: {e}')

# Для BytesIO с TextIOWrapper можно задать errors
raw = io.BytesIO()
wrapper = io.TextIOWrapper(raw, encoding='ascii', errors='replace')
wrapper.write('\u2603')
wrapper.flush()
print(raw.getvalue())
b'?'

Пример 8: Поток с поддержкой seek на основе RandomAccessIO

Использование io.BytesIO для быстрого перемещения по большому объёму данных, например, для поиска подстроки.

Пример

import io

big_data = b'x' * 10000 + b'needle' + b'y' * 10000
buf = io.BytesIO(big_data)
# Поиск через чтение порциями
while True:
    chunk = buf.read(1024)
    if not chunk:
        break
    pos = chunk.find(b'needle')
    if pos != -1:
        print(f'Found at position {buf.tell() - len(chunk) + pos}')
        break
Found at position 10000

Библиотека io в Python (ввод/вывод) - comments

En
библиотека io python (python)