На этом шаге мы рассмотрим решение этой задачи.
Вы хотите добавить или изменить кодировку Unicode уже открытого файла, не закрывая его.
Если вы хотите добавить кодирование/декодирование в Unicode уже существующему файловому объекту, открытому в бинарном режиме, оберните его объектом io.TextIOWrapper(). Например:
import urllib.request import io u = urllib.request.urlopen('http://www.python.org') f = io.TextIOWrapper(u, encoding='utf-8') text = f.read()
Если вы хотите изменить кодировку файла, открытого в текстовом режиме, используйте метод detach() для удаления существующего слоя текстовой кодировки перед заменой его новым. Вот пример изменения кодировки в sys.stdout:
>>> import sys >>> sys.stdout.encoding 'utf-8' >>> sys.stdout = io.TextIOWrapper(sys.stdout.detach(), encoding='1atin-1') >>> sys.stdout.encoding 'latin-1' >>>
Если вы это сделаете, то можете "сломать" вывод вашего терминала. Приведенный выше пример - это просто иллюстрация подхода.
Система ввода-вывода построена на последовательности слоев. Вы можете увидеть эти слои, если попробуете сделать следующее:
>>> f = open('sample.txt','w') >>> f <_io.TextIOWrapper name='sample.txt' mode='w' encoding='utf-8'> >>> f.buffer <_io.BufferedWriter name='sample.txt'> >>> f.buffer.raw <_io.FileIO name='sample.txt' mode='wb' closefd=True> >>>
В этом примере io.TextIOWrapper - это слой для обработки текста, который кодирует и декодирует в Unicode, ioBufferedWriter - буферизированный слой ввода-вывода, который работает с бинарными данными, а ioFileIO - "сырой файл", представляющий низкоуровневый файловый дескриптор в операционной системе. Добавление или изменение текстовой кодировки вовлекает добавление и изменение только верхнего слоя - io.TextIOWrapper.
Общее правило: небезопасно напрямую манипулировать слоями, используя показанные выше атрибуты. Например, вот что произойдет, если вы попытаетесь изменить кодировку с помощью этого приема:
>>> f <_io.TextIOWrapper name='sample.txt' mode='w' encoding='utf-8'> >>> f = io.TextIOWrapper(f.buffer, encoding='latin-1') >>> f <_io.TextIOWrapper name='sample.txt' encoding='latin-1'> >>> f.write('Hello') Traceback (most recent call last): . . . . ValueError: I/O operation on closed file. >>>
Это не работает, поскольку изначальное значение f было уничтожено, а лежащий в основе файл закрыт.
Метод detach() отделяет верхний слой файла и возвращает следующий, более низкоуровневый слой. Далее высший слой уже нельзя использовать. Например:
>>> f = open('sample.txt', 'w') >>> f <_io.TextIOWrapper name='sample.txt' mode='w' encoding='utf-8'> >>> b = f.detach() >>> b <_io.BufferedWriter name='sample.txt'> >>> f.write('Hello') Traceback (most recent call last): . . . . ValueError: underlying buffer has been detached >>>
Однако после отделения вы можете добавить новый верхний слой возвращаемому результату. Например:
>>> f = io.TextIOWrapper(b, encoding='latin-1') >>> f <_io.TextIOWrapper name='sample.txt' encoding='latin-1'> >>>
Хотя здесь мы показали изменение кодировки, этот прием может быть использован для изменения обработки строк, политики обработки ошибок и других аспектов работы с файлами. Например:
>>> sys.stdout = io.TextIOWrapper(sys.stdout.detach(), encoding='ascii', \ errors='xmlcharrefreplace') >>> print('Jalape\u00f1o') Jalapeño >>>
Заметьте, как не входящий в ASCII символ ñ был заменен на ñ в выводе.
На следующем шаге мы рассмотрим запись байтов в текстовый файл.