Основы работы с файлами в Python
Введение
В этой статье вы узнаете как организовать работать с файлами в Python 3.
Создайте файл
files.py
и копируйте туда код из примеров.
Запустить файл можно командой
python3 files.py
Создать
Создать файл можно командой
open
Опции:
r чтение
rb чтение в бинарном режиме
rt чтение в текстовом режиме
w только запись.
wb запись в бинарном режиме
wt запись в текстовом режиме
w+ запись и чтение
a запись в конец файла - сохранит данные, которые были в файле
b - это селектор бинарного режима
t - это селектор текстового режима
Любая опция с w перезапишет существующий файл - будьте внимательны!
Пример:
f = open("log.txt","w+")
Кодировка
По умолчанию в open() используется кодировка которую возвращает вызов locale.getencoding()
In text mode, if encoding is not specified the encoding used is platform dependent: locale.getencoding() is called to get the current locale encoding.
import locale print(locale.getencoding())
cp1252
Это может отличаться от того, что возвращает sys.getdefaultencoding() или sys.stdout.encoding
docs.python.org/3/library/codecs.html#standard-encodings
import sys print(sys.getdefaultencoding())
utf-8
Открыть файл
Синтаксис:
open(path_to_file, mode, encoding)
Про encoding можно прочитать здесь
Чтобы открыть файл для чтения выполните
f = open("log.txt","r")
Если файл log.txt не существует, он не будет создан
raceback (most recent call last): File "files.py", line 1, in <module> f = open('log.txt', 'r') FileNotFoundError: [Errno 2] No such file or directory: 'log.txt'
Класс к которому принадлежит объект открытого файла называется _io.TextIOWrapper
print(type(f))
<class '_io.TextIOWrapper'>
Закрыть файл
Чтобы закрыть файл выполните
f.close()
Закрытие файла не удаляет ссылку на него.
f = open("log.txt","r") print(type(f)) f.close() print(type(f))
<class '_io.TextIOWrapper'> <class '_io.TextIOWrapper'>
Ссылка остаётся, но так как файл закрыт с ним почти ничего нельзя сделать
f = open("log.txt","r") f.close() contents = f.read()
Traceback (most recent call last): File "C:\Users\Andrei\close.py", line 4, in <module> contents = f.read() ^^^^^^^^ ValueError: I/O operation on closed file.
Закрыт ли файл
Для того чтобы проверить закрыт ли файл используется атрибут closed. Обратимся к нему после открытия файла. Ожидаемый результат - False
f = open("log.txt","r") print(f.closed)
False
Атрибут closed есть у объектов типа _io.TextIOWrapper поэтому проверять закрыта ли строка или что-то подобное смысла нет
s = "" print(s.closed)
Traceback (most recent call last): File "C:\Users\Andrei\closed.py", line 2, in <module> print(s.closed) ^^^^^^^^ AttributeError: 'str' object has no attribute 'closed'
Так как метод close() не удаляет ссылку на файл, можно обратиться к атрибуту closed и после закрытия файла. Ожидаемый результат - True
f = open("log.txt","r") print(f.closed) f.close() print(f.closed)
False True
Менеджер контекста
Предпочтительнее использовать
менеджер контекста
для работы с файлами
Как только вы выйдете из блока в котором открыт файл - он автоматически закроется
with open('log.txt', 'r') as f:
pass
print(f.closed)
True
С помощью функции
type()
легко убедиться, что объект, который получается в результате использования with имеет тип _io.TextIOWrapper
- тот же тип который мы получали с помощью обычного
open()
Для полноты картины, изучим доступные методы и атрибуты, с помощью
dir()
with open('log.txt', 'r') as f: print(type(f)) print(dir(f))
<class '_io.TextIOWrapper'> ['_CHUNK_SIZE', '__class__', '__del__', '__delattr__', '__dict__', '__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '_checkClosed', '_checkReadable', '_checkSeekable', '_checkWritable', '_finalizing', 'buffer', 'close', 'closed', 'detach', 'encoding', 'errors', 'fileno', 'flush', 'isatty', 'line_buffering', 'mode', 'name', 'newlines', 'read', 'readable', 'readline', 'readlines', 'reconfigure', 'seek', 'seekable', 'tell', 'truncate', 'writable', 'write', 'write_through', 'writelines']
Так как у объекта класса _io.TextIOWrapper есть метод
next()
по нему удобно итерировать.
Рассмотри файл sites.txt
www.heihei.ru www.testsetup.ru www.topbicycle.ru
И пройдём по нему с помощью next()
with open('sites.txt', 'r') as f: print(next(f)) print(next(f)) print(next(f)) print(next(f))
www.heihei.ru www.testsetup.ru www.topbicycle.ru Traceback (most recent call last): File "/mnt/c/Users/Andrei/open_ex.py", line 5, in <module> print(next(f)) ^^^^^^^ StopIteration
next() работает до тех пор, пока элементы не закончатся и затем поднимает StopIteration исключение.
Будем рассматривать примеры работы с файлами как с использованием менеджера контекста так и без него.
read(): чтение файла
Получать данные из файла можно с помощью read()
В неё можно передавать аргумент типа int и тогда будет возвращено
соответсвующее количество байт содераждания файла.
Прочитать содержимое файла
with open('sites.txt', 'r') as f:
f_contents = f.read()
print(f_contents)
www.heihei.ru www.testsetup.ru www.topbicycle.ru
Рассмотрим файл sites.md который состоит из одной строки
topbicycle.ruheihei.rueth1.ru
>>> f = open('sites.md', mode='rt', encoding='utf-8') >>> f.read(3)
'top'
>>> f.read(7)
'bicycle'
>>> f.read(3)
'.ru'
Чтобы получить всё что осталось в файл нужно вызвать read() без аргументов.
>>> f.read()
'heihei.rueth1.ru'
Если вызвать read() ещё раз, вернётся пустая строка
>>> f.read()
''
В данный момент указатель стоит на конце файла, но его можно переместить с помощью seek()
Прочитать определённое количество символов
with open('sites.md', 'r') as f:
f_contents = f.read(20)
print(f_contents)
www.eth1.ru www.heihe
Если выполнять эту команду последовательно - будут прочитаны следующие символы
with open('sites.md', 'r') as f:
f_contents = f.read(20)
print(f_contents, end = '')
f_contents = f.read(20)
print(f_contents, end = '')
www.eth1.ru www.heihei.ru www.topbicycle.
Цикл для произвольного количества символов .read
with open('sites.md', 'r') as f:
size_to_read = 10
f_contents = f.read(size_to_read)
while len(f_contents) > 0:
print(f_contents, end = '')
f_contents = f.read(size_to_read)
www.eth1.ru www.heihei.ru www.topbicycle.ru
Выражение f_contents = f.read(size_to_read) нужно для того, чтобы когда файл закончится и f.read(size_to_read) станет нулем len(f_contents) тоже станет нулем и цикл завершится
Очистить файл
>>> f = open('sites.md', mode='w', encoding='utf-8') >>> f.write('')
0
>>> f.close() >>> exit()
readline(): построчное чтение
Метод readline() выводит содержимое построчно.
А метод readlines()
выводит все строки в виде
списка
Рассмотрим файл sites.md который состоит из трёх строк
topbicycle.ru heihei.ru eth1.ru
>>> f = open('sites.md', mode='rt', encoding='utf-8') >>> f.readline()
'www.topbicycle.ru\n'
>>> f.readline()
'www.heihei.ru\n'
>>> f.readline()
'eth1.ru'
Когда строки закончатся readline() будет возвращать не ошибку, а просто пустую строку
>>> f.readline()
''
>>> type(f.readline())
<class 'str'>
Если нужно снова читать сначала пригодится метод seek() про которой можно прочитать в статье «Дополнительные приёмы работы с файлами»
>>> f.seek(0)
0
>>> f.readline()
'www.topbicycle.ru\n'
>>> f.close()
Строки по одной с помощью readline() в скрипте
with open('sites.md', 'r') as f:
f_contents = f.readline()
print(f_contents)
f_contents = f.readline()
print(f_contents)
www.eth1.ru www.heihei.ru
Убрать переходы на новую строку можно с помощью end=''
with open('sites.md', 'r') as f:
f_contents = f.readline()
print(f_contents, end = '')
f_contents = f.readline()
print(f_contents, end = '')
www.eth1.ru www.heihei.ru
Цикл для построчного вывода без использования readline()
with open('sites.md', 'r') as f:
for line in f:
print(line, end = '')
www.eth1.ru www.heihei.ru www.topbicycle.ru
В качестве альтернативы можно использовать sys.stdout.write()
Про передачу аргументов из терминала вы можете прочитать в статье
sys.argv[]
# files.py import sys f = open(sys.argv[1], mode='rt', encoding='utf-8') for line in f: sys.stdout.write(line) f.close()
python files.py sites.md
www.eth1.ru www.heihei.ru www.topbicycle.ru
readlines()
Метод readlines() выводит все строки в виде списка
>>> f.readlines()
['www.topbicycle.ru\n', 'www.heihei.ru\n', 'eth1.ru']
>>> f.close()
Все строки с символом перехода на новую строку - readlines
with open('sites.md', 'r') as f:
f_contents = f.readlines()
print(f_contents)
['www.eth1.ru\n', 'www.heihei.ru\n', 'www.topbicycle.ru\n']
Имя файла
Пример программы, которая выводит на экран имя файла и режим, в котором он открыт
f = open('log.txt', 'r')
print(f.name)
print(f.mode)
f.close()
Если файл log.txt существует, то в терминале вы увидите
log.txt
r
РЕКЛАМА от Яндекса. Может быть недоступна в вашем регионе
Конец рекламы. Если там пусто считайте это рекламой моей телеги
Копирование файлов
Текстовые файлы
Их можно копировать построчно
with open('sites.md', 'r') as rf:
with open('sites_copy.txt', 'w') as wf:
for line in rf:
wf.write(line)
cat sites_copy.txt
www.eth1.ru www.heihei.ru www.topbicycle.ru
Изображения
Их тоже можно копировать построчно, но открывать
и записывать нужно в побитовом режиме. То есть
нужно добавлять опцию b
Скачайте изображение велосипеда с сайта
TopBicycle.ru
или возьмите любую другую картинку
wget https://topbicycle.ru/b/img/stels_pilot_950_MD_26.jpg
ls
stels_pilot_950_MD_26.jpg
with open('stels_pilot_950_MD_26.jpg', 'rb') as rf:
with open('stels_pilot_950_MD_26_copy.jpg', 'wb') as wf:
for line in rf:
wf.write(line)
python3 files.py
ls
stels_pilot_950_MD_26.jpg stels_pilot_950_MD_26_copy.jpg
Более правильным подходом считается копирование не в построчном режиме а частями с фиксированным размером
with open('stels_pilot_950_MD_26.jpg', 'rb') as rf:
with open('stels_pilot_950_MD_26_copy.jpg', 'wb') as wf:
chunk_size = 4096
rf_chunk = rf.read(chunk_size)
while len(rf_chunk) > 0:
wf.write(rf_chunk)
rf_chunk = rf.read(chunk_size)
Записать файл
Чтобы очистить файл от старого содержимого и записать в него новое используется опция w (write)
with open('log.txt', 'w') as f:
f.write("some text")
Рассмотрим запись в файл в интерактивном режиме и без менеджера контекста
>>> f = open('sites.md', 'w') >>> f.write("topbicycle.ru")
13
>>> f.write("heihei.ru")
9
13 и 9 это число байт переданное в файл
>>> f.close() >>> exit() cat sites.md
topbicycle.ruheihei.ru
ls -l
-rw-r--r-- 1 andrei users 24 Dec 12 22:26 sites.md
Конечный размер файла будет зависеть от опецарионной системы.
В
Windows
и
Linux
разные переносы строк, поэтому когда Python применяет свой
универсальный перенос строки
количество байт может увеличится на 1 а может остаться прежним.
write() возвращает количество байт, переданных в файл, а не фактический размер записанных данных.
Если снова открыть файл и записать в него
>>> f = open('sites.md', 'w') >>> f.write("testsetup.ru") 12 >>> f.close()
Содержимое перезапишется.
Дописать в файл
Если нужно добавить новые данные к предыдущему содержимому без удаления - используется опция a (append)
with open('log.txt', 'a') as f:
f.write("some text")
writelines(): Дописать в файл
Рассмотрим пример добавления данных в файл sites.md с помощью метода writelines()
www.topbicycle.ru www.heihei.ru www.eth1.ru
>>> f = open('sites.md', mode='at', encoding='utf-8') >>> f.writelines(['www.aviasales.ru\n', 'www.booking.com', 'www.tutu.ru\n', 'www.velodrive.ru', 'www.velosklad.ru']) >>> f.close() >>> exit()
cat sites.md
www.topbicycle.ru www.heihei.ru eth1.ruwww.aviasales.ru www.booking.comwww.tutu.ru www.velodrive.ruwww.velosklad.ru
Видно, что переносы строк появились только там, где их указали вручную.
Если нужно добавить данные не в конец файла, а в определённое место -
изучите пример в статье
«Продвинутые приёмы работы с файлами»