Файлы. Кодировки. Работа с файлами.
Содержание
Напоминание
Для понимания материала ОБЯЗАТЕЛЬНО каждый пример пытаться исполнить, понять, почему он работает или не работает, и попробовать в нём что-то поменять, и посмотреть, что получится.
Программирование – это практическая дисциплина, и без успешных попыток что-то сделать руками понимания теории не появляется.
Что такое текстовый файл
С точки зрения операционной системы, и, следовательно, питона, файл – это последовательность байт. (Можно считать, что один байт – это одно число от 0 до 255, то есть если бы у нас был алфавит из 256 букв, то файл из N байт – это N букв этого алфавита).
Общепринято нестрогое и неформальное деление файлов на текстовые и бинарные.
В текстовом файле последовательность байт несёт единственный смысл: последовательность букв текста в человеческом понимании. (В простейшем виде действует соотношение: один байт – одна буква). Т.е. в текстовом файле кроме текста нет ничего. Простейший пример текстового файла – текст, набранный в notepad (он же блокнот) в windows.
В бинарном файле, в зависимости от типа файла (например, картинка JPEG, ролик MKV, документ DOC, архив ZIP) разным байтам придаётся разный смысл: какие-то трактуются как числа, какие-то обозначают яркость канала цвета, какие-то громкость звука. Если бы мы попытались открыть бинарный файл текстовым редактором (например, notepad), то мы увидим непонятную ерунду: потому, что мы те же байты пытаемся интерпретировать с точки зрения другого языка. (Как пытаться расслышать русские слова в китайской речи).
Начиная с этого занятия мы обнаружим, что в питоне гораздо проще общаться с текстовыми файлами.
Понятие кодировки
Кодировка текста – это правила, которые устанавливают, какие байты соответствуют какой букве.
Очень важная кодировка – ASCII. Это одна из довольно старых кодировок, она стандартизована в Америке в 1963 году, и она приписывает 128 различным значениям байта буквы, цифры, и основные знаки пунктуации. В этой кодировке один байт обозначает одну букву, но для некоторых значений байта начертание не определено.
За ней последовало множество кодировок для разных стран: западной Европы, Греции, России, которые заполняли оставшиеся 128 значений каким-нибудь образом так, чтобы запихать туда необходимые символы (буквы с различными диакритическими знаками, греческие буквы, русские буквы, арабская вязь, японская кана и т.п). Из них полезно уметь узнавать хотя бы названия трёх старых кодировок для русского языка:
- cp866, она же DOS
- cp1251, она же windows-1251
- koi8-r
Во всех этих кодировках байты с числовыми значениями от 0 до 127 совпадают с ASCII, а остальным значениям приписаны русские буквы (притом везде разными, никак друг на друга не похожими способами).
Для языков, которые используют китайские иероглифы, сделать кодировку с отображением один байт = один символ невозможно, поэтому китайцы, японцы и корейцы посочиняли для себя кодировок, где один иероглиф задаётся последовательностью из двух-четырёх байт рядом.
Всего таких кодировок образовалось порядка тысячи. А для того, чтобы иметь возможность между ними конвертировать тексты, нужно (задача по математике – сколько?) перекодировщиков.
Поэтому в конце 80-х годов возник The Unicode Consortium, который стал собирать реестр всех символов, которые возможно представлять в компьютере. Этот реестр стал называться Unicode.
Идея Unicode в том, что для того чтобы сделать универсальный перекодировщик текстов достаточно для каждой буквы сначала её расшифровать согласно таблицы для соответствующей кодировки и определить место этой буквы в реестре Unicode, а затем выдав представление этой буквы во второй кодировке.
Кроме того, The Unicode Consortium определило несколько кодировок, предназначенных для того, чтобы хранить все символы из реестра Unicode:
utf-8 – это наиболее популярная кодировка сейчас. Она замечательна тем, что для английского текста она совпадает с ASCII (и, соответственно, действует соответствие 1 байт = 1 буква), для русского, греческого, абабского и т.п. в ней действует соответствие 1 буква = 2 байта, а для иероглифов и совсем экзотических символов соотношение становится 1 буква = 3 байта.
utf-16 и utf-32 – это способы представлять наиболее употребимую часть реестра уникода в 2 или 4 байта соответственно.
Два типа строк в питоне
Строки, с которыми мы до сих пор работали в питоне, на самом деле были просто списками байтов. Для кодировки ASCII или других однобайтовых кодировок получается только одна беда – мы будем на экран писать те же байты, что и в тексте программы, а если экран отображает другую кодировку, чем в тексте программы, то мы увидим крякозябликов. А для многобайтных кодировок у нас будут получаться ещё и странные результаты про длину строк (количество байт) – она будет оказываться больше, чем количество символов в ней – поэтому я и советовал избегать русского языка.
Уникодовская идея о том, что внутри программы любой текст должен иметь универсальное представление, не зависимое от кодировки – правильная. Питон позволяет этой идеей пользоваться. Для этого в питоне есть ещё один тип строк – это "уникодовские строки".
Уникодовские строки обозначаются в питоне префиксом u перед открывающей кавычкой строки:
1 print u"Привет"
Так как даже при прочтении программы питон должен раскодировать её текст и превратить его в универсальное представление, мы должны ему сообщить о том, в какой кодировке хранится текст программы. Для этого нужно в одной из первых двух строк программы добавить комментарий с текстом: coding: имя кодировки. Редактор IDLE добавляет такой комментарий самостоятельно, если только у вас в тексте программы есть не-английские буквы.
Для работы с кодировками есть две операции:
строка в какой-то кодировке -> decode -> уникодная строка
уникодная строка -> encode -> строка в какой-то кодировке
Кроме того, в питоне команда print самостоятельно переводит уникодные строки в кодировку экрана.
Как прочитать и записать файл в питоне
Для работы с файлом в питоне его сначала нужно "открыть".
В питоне есть два способа работы с файлами:
побайтовый – тогда файл открывается функцией open() – в этом случае все функции чтения возвращают обычные строки, а если в файле лежит текст, то за конвертирование кодировок отвечает сам автор программы
уникодный – тогда файл открывается функцией codecs.open() – в этом случае питон берёт на себя заботу о кодировках и возвращает в программу уникодные строки
Нас, очевидно, будет интересовать всегда второй способ.
Обе функции первым аргументом получают имя файла. Вторым аргументом – режим работы с файлом: букву 'r', если мы хотим файл только читать, 'w', если мы хотим в файл только писать. codecs.open имеет ещё и третий аргумент, которым мы указываем, кодировку текста в файле.
Открыв файл, мы получаем файловый объект, вызывая методы которого мы можем что-то делать с файлом.
Самое простое, что можно сделать с файлом – это его открыть и получить весь его текст в виде одной гигантской строки. Для этого у файловых объектов есть метод read:
Пока что для простоты мы будем работать только с файлами, лежащими в той же директории, где и наша питонская программа. В этом случае нам достаточно писать только имя файла. Как быть в других случаях, мы будем разбираться позже.
При общении с файлом у питона всегда есть понятие курсора, как в текстовом редакторе. read читает весь файл от текущего места (которое изначально в начале файла) и переводит курсор в конец файла, так что если мы вызовем read ещё раз, на следующий раз он нам вернёт пустую строку.
Самая частая практическая потребность от файлов – это чтение по строкам. Поэтому в питоне сам объект файла является итератором, идущим по строкам файла. (Если убрать слово итератор, то это утверждение полностью эквивалентно такому: если файл запихать в цикл for, то цикл for будет идти по строкам файла, если файл дать аргументом функции list(), то list() вернёт нам список строк в файле):
Если мы хотим записать текст в файл:
- мы должны указать, что открываем файл для чтения
- при этом в момент открытия питон создаст такой файл, если его ещё не было, и сотрёт содержимое этого файла, если он уже существовал! Обратите на это внимание: питон не будет спрашивать вас, уверены ли вы в том, что вы хотите стереть этот файл, он его просто сотрёт!
для записи в файл мы вызываем метод write, который дописывает наш текст в конец файла.
метод write не добавляет перенос строки, поэтому мы должны об этом позабоититься сами и дописать в конец строки '\n'
- символ '\n' в питоне обозначает обычный конец строки
чтобы данные оказались у нас на диске, мы должны закрыть файл методом close (здесь история точно такая же как с вытаскиванием флэшки из компьютера: если вы не закроете файл, зачастую у вас всё запишется как нужно, и только в самый ответственный момент окажется, что вы создали пустой файл. За этим тщательно следит Мэрфи)
Бдительный читатель справедливо заподозрит, что sys.stdout, которым мы пользовались на первом занятии, на самом деле есть ни что иное как виртуальный файл, содержимое которого сразу оказывается на экране. Однако я повторяю призыв, что печатать на экран проще с помощью команды print, и давайте мы только ей для этого и будем пользоваться.