12 Работа с вводом и выводом
12.1 Input-Output
Файл - поименованная часть информации на носителе информации. Вся наша работа с компьютером связана с файлами, работа операционной системы основывается на файлах, поэтому язык программирования должен уметь работать с файлами. Работа с файлами является частью общей системы над обработкой пользовательского ввода-вывода (input-output, I/O). Пользователь вводит какую-то информацию и получает какой-то вывод, ответ на свою задачу. Введенная информация содержится в оперативной памяти в виде потоков (streams). Каждая программа (процесс) содержит три потока:
- Поток ввода
- Поток вывода
- Поток ошибок - похож на поток вывода. Сюда выводят информацию о нештатных ситуациях.
Потоки ограничены по размеру и не предназначены для хранения - только для перенаправления куда-либо. Если мы хотим хранить информацию дольше, чем текущий момент выполнения инструкции, нужно записывать её в файл и читать из файла. Таким образом, работа с фалйами является одной из фундаментальных операций.
12.1.1 Операции над файлами
Чтобы начать работу с файлом, его необходимо открыть.
my_file = open("path/to/my/file","r", encoding = "utf-8")Переменная my_file содержит специальный объект, содержащий в себе функции по работе с файлом. Функция open принимает множество параметров, но основных три:
- Путь к файлу в файловой системе. Файловая система представляет из себя дерево каталогов и файлов, поэтому путь к файлу записывается как перечисление каталогов, записанное с некоторым разделителем, по которым надо пройти от корня файловой системы до нужного файла.
- Режим открытия файлов: файлы бывают текстовые (человекочитаемые, из символов), бинарные (совокупность байтов). По умолчанию файл открывается как текстовый. Если вы хотите открыть его как бинарный, то необходимо к букве режима открытия приписать b.
r - read. Файл открыт только для чтения. Вы не сможете изменить содержимое
w - write. Файл откроется для записи. Если файл не существует, он будет создан
ВНИМАНИЕЕсли файл существует, то всё его содержимое будет утрачено. Используйте аккуратно!
a - append. Файл доступен для чтения и записи в конец файла. Указатель для изменения можно перемещаь по файлу. Используйте этот режим для редактирования существующих файлов.
- Кодировка файла - принципы хранения символов (используемая кодовая таблица)
- UTF-8 - используется по умолчанию в UNIX-системах (Linux, MacOS)
- cp1251 - используется по умолчанию в Windows
Существуют несколько особенностей связанных с путями к файлам в зависимости от используемой операционной системы.
- Разделитель
В Windows разделителем является обратный слэш (\). Из-за подобного разделителя возникают некоторые проблемы: в сочетании с некоторыми символами обратный слэш интерпретируется как служебный символ (escape sequence), из-за чего строка с путём неправильно прочитывается интерпретатором. Примеры таких символов представлены в Table 12.1.
Table 12.1: Примеры управляющих символов (escape sequences) Символ Значение \t табуляция \r возврат каретки \n перенос строки \u символ юникода В Unix-системах (Linux, macOS) такой проблемы нет. Разделителем является прямой слэш (/).
- Местоположение корня
- В Windows корнем файловой системы является том, который обозначается одной заглавной латинской буквой (C или D).
- В Unix-системах корень обозначается просто слэшом.
Существуют два типа путей (независимо от ОС):
Абсолютные - полный путь от корня до целевого файла (“С:\Users\User\Document\my_file.txt”)
Относительные - путь до целевого файла от текущего местоположения. (Допустим, это ““С:\Users\User”, тогда относительный путь будет равен “Document\my_file.txt”)
Модуль os и его подмодули содержат большое количество функций для работы с файлами и путями
import os
1print(os.getcwd())
2os.chdir(path)
3os.exists(path)
4os.isfile(path)
5os.isdir(path)
6path = os.path.join(dir1,dir2,dir3)- 1
- Получить текущую директорию в виде строки
- 2
- Изменить текущую директорию
- 3
- Проверка на существование файла/папки
- 4
- Проверка на то, является ли объект указанного пути существующим файлом
- 5
- Проверка на то, является ли объект указанного пути существующей папкой
- 6
- Рекомендуемый способ составления путей из нескольких компонентов независимо от операционной системы
Для операций чтения и записи при открытии файла резервируется ограниченное место в оперативной памяти называемое буфером. Перед тем, как записать содержимое файла в переменную и наоборот, информация хранится в этом буфере. Операции чтения показаны на Listing 12.1.
file = open("some/path/to.txt")
content = file.read()
first_5_bytes = file.read(5)
lines = file.readlines()- Открытие файла. По умолчанию файл открывается в кодировке utf-8 для чтения
- Прочитать всё содержимое файла
- Прочитать первые 5 символов
- Прочитать файл построчно - вернет список, в котором элементами будут являтся строки. Строкой в данном случае является совокупность символов до знака переноса строки (\n).
Прочитать весь файл целиком можно только если он небольшой по размеру. В биоинформатике это может оказаться опасной операцией, так как часто приходится работать с большими файлами, и попытка загрузить всё его содержимое в оперативную память может закончится отказом программы. Поэтому более безопасный способ чтения файла в цикле по частям.
file = open("some/path/to.txt")
1for line in file:
print(line)
2chunk = file.read(30)
while chunk:
print(chunk)
chunk = file.read(30)- 1
- Чтение файла в цикле построчно
- 2
- Чтение кусками (чанками) по 30 символов
Запись в файл и работа с курсором рассмотрена на Listing 12.2
file = open("some/path/to.txt","w")
file.write("Meow meow")
file.write(line + "\n")
file.tell()
file.seek(offset, whenence)- Открытие файла на запись
- Запись некоторой последовательности символов в файл
- Если вы хотите записать последовательность символов как строку файла, тогда надо прибавить символ переноса строки (если последовательность символов изначально на него не заканчивается).
- Получить текущее положение указателя
- Сместить указатель на offset байт относительно whenence. whenence может принимать три значения: 0 - начало файла, 1 - текущее местоположение, 2 - конец файла.
После окончания работы файл необходимо обязательно закрыть!
file.close()Какие могут быть последствия незакрытия файла.
- Ваши изменения не сохранятся, так как не перейдут из буфера в файл.
- Файл может остаться заблокированным для открытия другими программами и частями вашей программы.
- Возникнет неприятное являние, которое будет тормозить работу выполнения вашей и других программ - утечка памяти. Утечка памяти - состояние, когда не освобождается выделенная память после окончания её использования.
Для безопасной работы с файлами используют особую синтаксическую конструкцию - менеджер контекста.
with open("my_file.txt") as file:
for line in file:
print(line)Менеджер контекста сам закроет файл по окончанию работы с ним, даже в случае внештатной ситуации. Не нужно самим вызывать функцию close.
12.2 Упражнения
Ваши программы должны быть приспособлены к чтению файлов из следующих задач Rosalind. Использовать специальные модули запрещается.