Марк Лутц - Изучаем Python, Четвертое издание (1184811), страница 75
Текст из файла (страница 75)
Выможете отключить механизм буферизации с помощью дополнительных параметров функции open, но это может привести к снижению производительности операций ввода-вывода. Файлы в языке Python поддерживают такжевозможность позиционирования – метод seek позволяет сценариям управлять позицией чтения и записи.Файлы в действииДавайте рассмотрим небольшие примеры, демонстрирующие основы работыс файлами.
В первом примере выполняется открытие нового текстового файлав режиме для записи, в него записываются две строки (завершающиеся символом новой строки \n), после чего файл закрывается. Далее этот же файл открывается в режиме для чтения и выполняется чтение строк из него. Обратитевнимание, что третий вызов метода readline возвращает пустую строку – такимспособом методы файлов в языке Python сообщают о том, что был достигнут конец файла (пустая строка в файле возвращается как строка, содержащая единственный символ новой строки, а не как действительно пустая строка). Нижеприводится полный листинг сеанса:>>>>>>16>>>18>>>myfile = open(‘myfile.txt’, ‘w’) # Открывает файл (создает/очищает)myfile.write(‘hello text file\n’) # Записывает строку текстаmyfile.write(‘goodbye text file\n’)myfile.close()>>> myfile = open(‘myfile.txt’)>>> myfile.readline()‘hello text file\n’>>> myfile.readline()‘goodbye text file\n’>>> myfile.readline()‘’# Выталкивает выходные буферы на диск# Открывает файл: ‘r’ – по умолчанию# Читает строку# Пустая строка: конец файла293ФайлыОбратите внимание, что в Python 3.0 метод write возвращает количество записанных символов – в версии 2.6 этого не происходит, поэтому в интерактивном сеансе вы не увидите эти числа.
Этот пример записывает две строки текстав файл, добавляя в каждую из них символ конца строки \n; методы записи недобавляют символ конца строки, поэтому нам необходимо самостоятельно добавлять его в выводимые строки (в противном случае следующая операция записи просто продолжит текущую строку в файле).Если необходимо вывести содержимое файла, обеспечив правильную интерпретацию символов конца строки, его следует прочитать в строку целиком,с помощью метода read, и вывести:>>> open(‘myfile.txt’).read() # Прочитать файл целиком в строку‘hello text file\ngoodbye text file\n’>>> print(open(‘myfile.txt’).read()) # Более дружественная форма отображенияhello text filegoodbye text fileА если необходимо просмотреть содержимое файла строку за строкой, лучшимвыбором будет итератор файла:>>> for line in open(‘myfile’): # Используйте итераторы, а не методы чтения...print(line, end=’’)...hello text filegoodbye text fileВ этом случае функцией open создается временный объект файла, содержимоекоторого автоматически будет читаться итератором и возвращаться по однойстроке в каждой итерации цикла.
Обычно такой способ компактнее, эффективнее использует память и может оказаться быстрее некоторых других вариантов (конечно, это зависит от множества параметров). Так как мы еще не касались темы инструкций и итераторов, вам придется подождать до главы 14, гдедается более полное описание этого примера.Текстовые и двоичные файлы в Python 3.0Строго говоря, в предыдущем примере выполняются операции с текстовымифайлами.
В версиях �������������������������������������������������������Python������������������������������������������������� 3.0 и 2.6 тип файла определяется вторым аргументом функции open – символ «b» в строке режима означает binary (двоичный).В языке Python����������������������������������������������������������������������������������������������������������������������всегда существовала поддержка текстовых и двоичных файлов, но в Python 3.0 между этими двумя типами файлов была проведена болеечеткая грань:•• Содержимое текстовых файлов представляется в виде обычных строктипа str, выполняется автоматическое кодирование/декодирование символов Юникода и по умолчанию производится интерпретация символов конца строки.•• Содержимое двоичных файлов представляется в виде строк типа bytes, и онопередается программе без каких-либо изменений.В Python 2.6, напротив, текстовые файлы могли содержать текст из 8-битныхсимволов или двоичные данные, а для работы с текстом из символов Юникодаиспользовался специальный строковый тип и интерфейс доступа к файлам294Глава 9.
Кортежи, файлы и все остальное(строки unicode и функция codecs.open). Изменения в Python 3.0 определялисьтем фактом, что обычный текст и текст в Юникоде были объединены в единый строковый тип, что имеет определенный смысл, если учесть, что любойтекст может быть представлен в Юникоде, включая ASCII и другие 8-битныекодировки.Большинству программистов приходится иметь дело только с текстом ASCII,поэтому они могут пользоваться базовым интерфейсом доступа к текстовымфайлам, как показано в предыдущем примере, и обычными строками. С технической точки зрения, все строки в версии 3.0 являются строками Юникода,но для тех, кто использует только символы ASCII, это обстоятельство обычноостается незамеченным.
В действительности операции над строками в версиях 3.0 и 2.6 выполняются одинаково, если область применения сценария ограничивается такими простыми формами текста.Если у вас имеется необходимость интернационализировать приложение илиобрабатывать двоичные данные, отличия в версии 3.0 окажут большое влияние на программный код (обычно в лучшую сторону).
Вообще говоря, для работы с двоичными файлами следует использовать строки bytes, а обычные строкиstr – для работы с текстовыми файлами. Кроме того, так как текстовые файлыреализуют автоматическое преобразование символов Юникода, вы не сможетеоткрыть файл с двоичными данными в текстовом режиме – преобразование егосодержимого в символы Юникода, скорее всего, завершится с ошибкой.Рассмотрим пример. Когда выполняется операция чтения двоичных данныхиз файла, она возвращает объект типа bytes – последовательность короткихцелых чисел, представляющих абсолютные значения байтов (которые могутсоответствовать символам, а могут и не соответствовать), который во многихотношениях очень близко напоминает обычную строку:>>> data = open(‘data.bin’, ‘rb’).read() # Открыть двоичный файл для чтения>>> data# Строка bytes хранит двоичные данныеb’\x00\x00\x00\x07spam\x00\x08’>>> data[4:8]# Ведет себя как строкаb’spam’>>> data[4:8][0]# Но в действительности хранит 8-битные целые числа115>>> bin(data[4:8][0])# Функция bin() в Python 3.0‘0b1110011’Кроме того, двоичные файлы не выполняют преобразование символов концастроки – текстовые файлы по умолчанию отображают все разновидности символов конца строки в и из символ \n в процессе записи и чтения, и производятпреобразование символов Юникода в соответствии с указанной кодировкой.Так как операции с символами Юникода и с двоичными данными представляют особый интерес для многих программистов, мы отложим полное их обсуждение до главы 36.
А пока перейдем к некоторым более насущным примерамиспользования файлов.Сохранение и интерпретация объектов Python в файлахСледующий пример записывает различные объекты в текстовый файл. Обратите внимание на использование средств преобразования объектов в строки.Напомню, что данные всегда записываются в файл в виде строк, а методы за-295Файлыписи не выполняют автоматического форматирования строк (для экономиипространства я опустил вывод значений, возвращаемых методом write):>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>X, Y, Z = 43, 44, 45S = ‘Spam’D = {‘a’: 1, ‘b’: 2}L = [1, 2, 3]# Объекты языка Python должны# записываться в файл только в виде строкF = open(‘datafile.txt’, ‘w’)F.write(S + ‘\n’)F.write(‘%s,%s,%s\n’ % (X, Y,F.write(str(L) + ‘$’ + str(D)F.close()# Создает файл для записи# Строки завершаются символом \nZ))# Преобразует числа в строки+ ‘\n’) # Преобразует и разделяет символом $Создав файл, мы можем исследовать его содержимое, открыв файл и прочитавданные в строку (одной операцией).
Обратите внимание, что функция автоматического вывода в интерактивной оболочке дает точное побайтовое представление содержимого, а инструкция print интерпретирует встроенные символыконца строки, чтобы обеспечить более удобочитаемое отображение:>>> chars = open(‘datafile.txt’).read() # Отображение строки>>> chars# в неформатированном виде“Spam\n43,44,45\n[1, 2, 3]${‘a’: 1, ‘b’: 2}\n”>>> print(chars)# Удобочитаемое представлениеSpam43,44,45[1, 2, 3]${‘a’: 1, ‘b’: 2}Теперь нам необходимо выполнить обратные преобразования, чтобы получитьиз строк в текстовом файле действительные объекты языка Python.
Интерпретатор Python никогда автоматически не выполняет преобразование строкв числа или в объекты других типов, поэтому нам необходимо выполнить соответствующие преобразования, чтобы можно было использовать операции надэтими объектами, такие как индексирование, сложение и так далее:>>> F = open(‘datafile.txt’) # Открыть файл снова>>> line = F.readline()# Прочитать одну строку>>> line‘Spam\n’>>> line.rstrip()# Удалить символ конца строки‘Spam’К этой строке мы применили метод rstrip, чтобы удалить завершающий символ конца строки.
Тот же эффект можно было бы получить с помощью извлечения среза line[:-1], но такой подход можно использовать, только если мы уверены, что все строки завершаются символом \n (последняя строка в файле иногдаможет не содержать этого символа).Пока что мы прочитали ту часть файла, которая содержит строку. Теперь прочитаем следующий блок, в котором содержатся числа, и выполним разбор этого блока (то есть извлечем объекты):>>> line = F.readline()>>> line‘43,44,45\n’>>> parts = line.split(‘,’)# Следующая строка из файла# Это - строка# Разбить на подстроки по запятым296Глава 9.
Кортежи, файлы и все остальное>>> parts[‘43’, ‘44’, ‘45\n’]Здесь был использован метод split, чтобы разбить строку на части по запятым,которые играют роль символов-разделителей, – в результате мы получили список строк, каждая из которых содержит отдельное число. Теперь нам необходимо преобразовать эти строки в целые числа, чтобы можно было выполнятьматематические операции над ними:>>> int(parts[1])# Преобразовать строку в целое число44>>> numbers = [int(P) for P in parts] # Преобразовать весь список>>> numbers[43, 44, 45]Как мы уже знаем, функция int преобразует строку цифр в объект целого числа, а генератор списков, представленный в главе 4, позволяет применить функцию ко всем элементам списка в одной инструкции (подробнее о генераторахсписков читайте далее в этой книге).
Обратите внимание: для удаления завершающего символа \n в конце последней подстроки не был использован методrstrip, потому что int и некоторые другие функции преобразования просто игнорируют символы-разделители, окружающие цифры.Наконец, чтобы преобразовать список и словарь в третьей строке файла, можно воспользоваться встроенной функцией eval, которая интерпретирует строкукак программный код на языке Python (формально – строку, содержащую выражение на языке Python):>>> line = F.readline()>>> line“[1, 2, 3]${‘a’: 1, ‘b’: 2}\n”>>> parts = line.split(‘$’)# Разбить на строки по символу $>>> parts[‘[1, 2, 3]’, “{‘a’: 1, ‘b’: 2}\n”]>>> eval(parts[0])# Преобразовать строку в объект[1, 2, 3]>>> objects = [eval(P) for P in parts] # То же самое для всех строк в списке>>> objects[[1, 2, 3], {‘a’: 1, ‘b’: 2}]Поскольку теперь все данные представляют собой список обычных объектов,а не строк, мы сможем применять к ним операции списков и словарей.Сохранение объектов Python с помощью модуля pickleФункция eval, использованная в предыдущем примере для преобразованиястрок в объекты, представляет собой мощный инструмент.