Работа с файлами
Содержание
Для открытия файлов в python используется функция open
:
file = open("/path/for/your/file.txt", "r")
open
принимает первым аргументом полное имя файла (с путём,
абсолютным или относительным), вторым - режим, в котором мы откроем
файлРежим | Обозначение |
---|---|
'r' |
открытие на чтение (является значением по умолчанию). |
'w' |
открытие на запись, содержимое файла удаляется, если файла не существует, создается новый. |
'x' |
открытие на запись, если файла не существует, иначе исключение. |
'a' |
открытие на дозапись, информация добавляется в конец файла. |
'b' |
открытие в двоичном режиме. |
't' |
открытие в текстовом режиме (является значением по умолчанию). |
'+' |
открытие на чтение и запись |
Таким образом, если мы хотим открыть файл в двоичном формате для записи, надо использовать режим “wb”, если мы хотим дописать содержимое в конец файла в текстовом формате, то - “a” или “at”, “r+b” - открыть двоичный файл на чтение и запись.
Обычно, файлы, в которых содержится текст, например, файлы txt
, код
вашей программы, файлы формата csv
, открываются в текстовом формате, а
файлы, которые нельзя проинтерпретировать как текст - в бинарном
(например, картинки, музыку). Иногда файлы с текстом открывают в
бинарном режиме, для более явного управления всеми спецсимволами
(например табуляция ↹).
При открытии файла в текстовом режиме, также можно указать подходящую кодировку. Например, если в вашем файле содержится текст на русском в utf8, откройте его в этой кодировке:
russian_file = open("russian.txt", "r", encoding="utf8")
Как только файл был открыт и у вас появился файловый объект, вы можете получить следующую информацию о нем:
Атрибут | Значение |
---|---|
file.closed | Возвращает True если файл был закрыт. |
file.mode | Возвращает режим доступа, с которым был открыт файл. |
file.name | Возвращает имя файла. |
У получаемого объекта есть несколько полезных методов, рассмотрим их.
метод
read(n)
позволяет прочитать следующиеn
символов файла. Замечу, что можно представить, что в нашем объекте файла есть указатель на текущую читаемую позицию. При открытии файла, она ставится в самое начало. По мере чтения, этот указатель сдвигается. Таким образом, если выполнятьread(n)
несколько раз подряд, мы будем получать не первыеn
символов, а каждый раз новые,n
символов.Если
n
явно не указать, то считается весь файл целиком (указатель окажется в самом конце файла). Для использования метода read, файл должен быть открыт в режиме для чтения Примечание: чтобы узнать текущее положение указателя внутри файла, можно воспользоваться методомtell()
, а чтобы установить указатель в нужное положениеpos
, используется методseek(pos)
file = open("russian.txt", "r", encoding="utf8") #открыли файл, file.tell() == 0, #т.е указатель стоит в самом начале text = file.read() #считали весь файл
если файл был открыт в режиме для записи, можно воспользоваться методом python:
write(buffer)
- записывает содержимое буфера в файл. Работа указателя при записи аналогична - он указывает на текущее обрабатываемое место.Примечание: при записи содержимого в файл, не гарантируется, что все запишется в файл здесь и сейчас, сразу после выполнения команды. Записываемая информация буферизуется (накапливается) и записывается при накоплении некоторого критического количества. Двоичные файлы буферизуются кусками фиксированного размера. Этот размер определяется эвристикой, пытающейся определить размер блока устройства, на котором находится файл, в случае неудачи использует io.DEFAULT_BUFFER_SIZE. Для многих систем буфер равен 4096 или 8192 байт. Содержимое принудительно записывется в файл при его закрытии. Также для принудительной записи в файл можно воспользоваться методом flush() - он просто записывает содержимое текущего буффера в файл здесь и сейчас.
Следует сказать, что открытый в любом режиме файл после его использования нужно обязательно закрывать. Делается это методом close(). Посе его выполнения работа с файлом будет корректно завершена, но с нашим объектом файла работать уже тоже будет нельзя - при необходимости повторной работы с файлом нужно снова его открывать при помощи open.
file = open("some_data.txt") text = file.read() file.close() #дальше работаем с text, если надо
Но вдруг в процессе выполнения нашей программы произройдет критическая ошибка и программа завершит свое выполнение, а мы, например, записывали в файл какую-то информацию? Верно, вполне возможно, что последняя добавленная информация в файл так и не запишется. Чтобы избежать такой ситуации, ну и чтобы просто не забывать вовремя вызывать close() используется конструкция with:
with open("text.txt", "w") as out: #в out теперь находится ссылка на наш объект файла, как если #бы было просто out = open("text.txt", "w") for i in range(100): out.write("А я запишу все эти строки в влюбом случае\n") #записываем 100 одинаковых строчек raise Exception #принудительно "вызываем" ошибку. #Об Exceptionах будет дальше в следующих семинарах #в файле все равно будут все 100 нужные строки
Через конструкцию with можно открывать сразу несколько файлов:
with open("input.txt", "r") as input, open("output.txt", "w") as output: output.write(input.read()) #скопировали содержимое input в output
- Чтобы считать из файла целую строку, используется метод readline(max_len). Если указать параметр max_len, то будут считаны максимум max_len символов
with open("text.txt", "r") as file: print(file.readline()) #считали и вывели первую строку файла
На самом деле у нашего объекта файла есть итератор, поэтому перебирать строки внутри файла можно с его помощью:
with open("text.txt", "r") as file: for line in file: print(line)
Такой способ чтения наиболее удобен для построчного чтения
def write_array(array, file_name): """записывает строки из array в файл file_name""" #ваш код здесь pass
Работа с файловой системой
Взаимодействие с файлами не ограничивается только самими файлами, нам часто приходится работать и с папками. Главными героями этого раздела будут библиотеки os и os.path. Они связаны с операционной системой компьютера и позволяют взаимодейстовать с файловой системой.
Все папки директории
os.listdir(dir) перечисялет файлы и папки в указанной директории dir. Если вызвать эту функцию без аргументов, она вернет файлы и папки текущей рабочей директории.
Текущая папка
Относительные пути строятся относительно текущей папки. Чтобы получить абсолютный путь файла из относительного, используется функция os.path.abspath(file_path). Чтобы узнать, какая папка является текущей, можно вызвать функцию os.getcwd(). Для смены текущей папки используется os.chdir(new_dir).
Проверка существования файла или папки и определение, является ли имя файлом или папкой
os.path.exists(file_name) проверяет, существует ли указанный файл (или директория) file_name.
Чтобы проверить, является ли данное имя name файлом или папкой, можно воспользоваться функциями os.isdir(name) или os.isfile(name), которые возвращают True или False.
Рекурсивный обход папок
Одной из самых интересных и мощных функций является функция os.walk(dir) - она позволяет рекурсивно пройтись по всем папкам, подпапкам, их подпапкам и так далее. На самом деле она возвращает генератор (последовательность элементов). Каждый элемент представляеьт собой кортеж из 3х элементов. Первый элемнт - строковое представление директории текущей директории, которую просматривает функция. Вторым элементом - список всех подпапок данной директории, а третьим - список всех файлов этой директории.
for current_dir, dirs, files in os.walk("."): #передаем в качестве аргумента текущую директорию #("." - означает именно ее) print(current_dir, dirs, files) #выведем, что получается
Копирование файлов
Копировать файлы можно при помощи функции copy из модуля shutil
shutil.copy("input.txt", "output.txt")
Копировать папки можно с помощью copytree
из того же модуля:
shutil.copytree("test", "test/test2") #Скопирует папку test внутрь неё самой же в подпапку test2
Многие другие функции для работы с файлами и папками вы сможете найти в модулях os и shutil. Теперь вы знаете, где искать нужный функционал ;)
Упражнение 3: Вам дана в архиве файловая структура, состоящая из директорий и файлов.
Вам необходимо распаковать этот архив (средствами языка python), и затем найти в данной в файловой структуре все директории, в которых есть хотя бы один файл с расширением “.py”.
Ответом на данную задачу будет являться файл со списком таких директорий, отсортированных в лексикографическом порядке.
Распространенные форматы текстовых данных
csv
csv является табличным форматом. В нем содержатся значения разделенные запятой (Comma-Separated Values). Например,
first name,last name,module1,module2,module3 Nikolay,Neznaev,0,20,10 Stepan,Sharyashiy,100,99.5,100
Для работы с csv файлами можно воспользоваться библиотекой csv:
import csv with open("example.csv", "r") as file: reader = csv.reader(file) #На основе открытого файла получаем объект из библиотеки csv for row in reader: print(row) #Каждая строка - список значений
import csv students = [ ["Greg", "Lebovskiy", 70, 80, 90, "Good job, Greg!"], ["Nick", "Shalopaev", 10, 50, 45, "Shalopaev, you should study better!"] ] with open("example.csv", "a") as file: writer = csv.writer(file) #На основе открытого файла получаем объект из библиотеки csv for student in students: writer.writerow(student) #Записываем строку #Вместо цикла выше мы могли сразу записать все через writer.writerows(students)
JSON
Это универсальные структуры данных. Почти все современные языки программирования поддерживают их в какой-либо форме. Логично предположить, что формат данных, независимый от языка программирования, должен быть основан на этих структурах.
Объекты в формате SJON хранятся как словари в Python, но с некоторыми деталями: во первых, ключом в json-объекте может быть только строка, значения True и False пишутся с маленькой буквы, значению None соответствует значение null, строки хранятся только внутри двойных кавычек.
Для удобной работы с json файлами в языке python можно использовать библиотеку json
Например:
import json student1 = { "full_name" : "Greg Martin", "scores" : [100, 85, 94], "certificate" : True, "comment": "Great job, Greg!" } student2 = { "full_name" : "John Price", "scores" : [0, 10, 0], "certificate" : False, "comment": "Guns aren't gonna help you here, captain!" } data = [student1, student2] print(json.dumps(data, indent=4, sort_keys=True)) #Делаем отступы в 4 пробела, сортируем ключи в алфавитном порядке
with open("output.json", "w") as out: json.dump(data, out, indent=4, sort_keys=True)
Для получения объекта python на основе его срокового представления можно воспользоваться функцией json.loads или json.load для считывания из файла:
json_str = json.dumps(data, indent=4, sort_keys=True) #получение строкового представления json data_again = json.loads(json_str) #получаем объект python print(sum(data_again[0]["scores"])) #убедимся в кореектном считывании: #посчитаем сумму баллов у первого студента with open("output.json") as file: data_from_file = json.load(file) #считаем объект из файла print(sum(data_from_file[0]["scores"])) #аналогично посчитаем сумму баллов
При записи-считывнии объектов из формата json кортежи превращаются в списки # Исключения (материал ниже взят с сайта https://pythonworld.ru/tipy-dannyx-v-python/isklyucheniya-v-python-konstrukciya-try-except-dlya-obrabotki-isklyuchenij.html )
Исключения (exceptions) - ещё один тип данных в python. Исключения необходимы для того, чтобы сообщать программисту об ошибках.
Самый простейший пример исключения - деление на ноль:
100 / 0 Traceback (most recent call last): File "", line 1, in 100 / 0 ZeroDivisionError: division by zero
Разберём это сообщение подробнее: интерпретатор нам сообщает о том, что он поймал исключение и напечатал информацию (Traceback (most recent call last)).
Далее имя файла (File ""). Имя пустое, потому что мы находимся в интерактивном режиме, строка в файле (line 1);
Выражение, в котором произошла ошибка (100 / 0).
Название исключения (ZeroDivisionError) и краткое описание исключения (division by zero).
Разумеется, возможны и другие исключения:
2 + '1' Traceback (most recent call last): File "", line 1, in 2 + '1' TypeError: unsupported operand type(s) for +: 'int' and 'str' int('qwerty') Traceback (most recent call last): File "", line 1, in int('qwerty') ValueError: invalid literal for int() with base 10: 'qwerty'
В этих двух примерах генерируются исключения TypeError и ValueError соответственно. Подсказки дают нам полную информацию о том, где порождено исключение, и с чем оно связано.
Рассмотрим иерархию встроенных в python исключений, хотя иногда вам могут встретиться и другие, так как программисты могут создавать собственные исключения. Данный список актуален для python 3.3, в более ранних версиях есть незначительные изменения.
- BaseException - базовое исключение, от которого берут начало все
остальные.
- SystemExit - исключение, порождаемое функцией sys.exit при выходе из программы.
- KeyboardInterrupt - порождается при прерывании программы пользователем (обычно сочетанием клавиш Ctrl+C).
- GeneratorExit - порождается при вызове метода close объекта generator.
- Exception - а вот тут уже заканчиваются полностью системные
исключения (которые лучше не трогать) и начинаются обыкновенные, с
которыми можно работать.
- StopIteration - порождается встроенной функцией next, если в итераторе больше нет элементов.
- ArithmeticError - арифметическая ошибка.
- FloatingPointError - порождается при неудачном выполнении операции с плавающей запятой. На практике встречается нечасто.
- OverflowError - возникает, когда результат арифметической операции слишком велик для представления. Не появляется при обычной работе с целыми числами (так как python поддерживает длинные числа), но может возникать в некоторых других случаях.
- ZeroDivisionError - деление на ноль.
- AssertionError - выражение в функции assert ложно.
- AttributeError - объект не имеет данного атрибута (значения или метода).
- BufferError - операция, связанная с буфером, не может быть выполнена.
- EOFError - функция наткнулась на конец файла и не смогла прочитать то, что хотела.
- ImportError - не удалось импортирование модуля или его атрибута.
- LookupError - некорректный индекс или ключ.
- IndexError - индекс не входит в диапазон элементов.
- KeyError - несуществующий ключ (в словаре, множестве или другом объекте).
- MemoryError - недостаточно памяти.
- NameError - не найдено переменной с таким именем.
- UnboundLocalError - сделана ссылка на локальную переменную в функции, но переменная не определена ранее.
- OSError - ошибка, связанная с системой.
- BlockingIOError
- ChildProcessError - неудача при операции с дочерним процессом.
- ConnectionError - базовый класс для исключений, связанных с
подключениями.
- BrokenPipeError
- ConnectionAbortedError
- ConnectionRefusedError
- ConnectionResetError
- FileExistsError - попытка создания файла или директории, которая уже существует.
- FileNotFoundError - файл или директория не существует.
- InterruptedError - системный вызов прерван входящим сигналом.
- IsADirectoryError - ожидался файл, но это директория.
- NotADirectoryError - ожидалась директория, но это файл.
- PermissionError - не хватает прав доступа.
- ProcessLookupError - указанного процесса не существует.
- TimeoutError - закончилось время ожидания.
- ReferenceError - попытка доступа к атрибуту со слабой ссылкой.
- RuntimeError - возникает, когда исключение не попадает ни под одну из других категорий.
- NotImplementedError - возникает, когда абстрактные методы класса требуют переопределения в дочерних классах.
- SyntaxError - синтаксическая ошибка.
- IndentationError - неправильные отступы.
- TabError - смешивание в отступах табуляции и пробелов.
- IndentationError - неправильные отступы.
- SystemError - внутренняя ошибка.
- TypeError - операция применена к объекту несоответствующего типа.
- ValueError - функция получает аргумент правильного типа, но некорректного значения.
- UnicodeError - ошибка, связанная с кодированием /
раскодированием unicode в строках.
- UnicodeEncodeError - исключение, связанное с кодированием unicode.
- UnicodeDecodeError - исключение, связанное с декодированием unicode.
- UnicodeTranslateError - исключение, связанное с переводом unicode.
- Warning - предупреждение.
Теперь, зная, когда и при каких обстоятельствах могут возникнуть исключения, мы можем их обрабатывать. Для обработки исключений используется конструкция try - except.
Первый пример применения этой конструкции:
try: k = 1 / 0 except ZeroDivisionError: k = 0 print(k)
В блоке try мы выполняем инструкцию, которая может породить исключение, а в блоке except мы перехватываем их. При этом перехватываются как само исключение, так и его потомки. Например, перехватывая ArithmeticError, мы также перехватываем FloatingPointError, OverflowError и ZeroDivisionError.
try: k = 1 / 0 except ArithmeticError: k = 0 print(k)
Также возможна инструкция except без аргументов, которая перехватывает вообще всё (и прерывание с клавиатуры, и системный выход и т. д.). Поэтому в такой форме инструкция except практически не используется, а используется except Exception. Однако чаще всего перехватывают исключения по одному, для упрощения отладки (вдруг вы ещё другую ошибку сделаете, а except её перехватит).
Ещё две инструкции, относящиеся к нашей проблеме, это finally и else. Finally выполняет блок инструкций в любом случае, было ли исключение, или нет (применима, когда нужно непременно что-то сделать, к примеру, закрыть файл). Инструкция else выполняется в том случае, если исключения не было.
f = open('1.txt') ints = [] try: for line in f: ints.append(int(line)) except ValueError: print('Это не число. Выходим.') except Exception: print('Это что ещё такое?') else: print('Всё хорошо.') finally: f.close() print('Я закрыл файл.') # Именно в таком порядке: try, группа except, затем else, и только потом finally.
class MyException(Exception): #создали свой класс. Ничего переопределять не обязательно pass raise MyException("My hovercraft is full of eels") #поднятие исключения