Работа с файлами в Python

Содержание
Введение
Создать
open(): Открыть
close(): Закрыть
with: Менеджер контекста
read(): чтение файла
seek(0): перемещение в начало файла
readline(): построчное чтение
Очистить файл
Копировать файл
Запись в файл
Дописать к файлу
Записать json в файл
Удалить первые несколько строк файла
Запись вывода программы в файл
Пример
Определить кодировки файлов
Прочитать файл из другой директории
glob
Похожие статьи

Введение

В этой статье вы узнаете как организовать работать с файлами в Python 3.

Создайте файл files.py и копируйте туда код из примеров.

Запустить файл можно командой

python3 files.py

Создать

Создать файл можно командой open

Опции:

r  чтение

rb чтение в бинарном режиме

rt чтение в текстовом режиме

только запись.

wb запись в бинарном режиме

wt запись в текстовом режиме

w+ запись и чтение

запись в конец файла - сохранит данные, которые были в файле

b - это селектор бинарного режима

t - это селектор текстового режима

Любая опция с w перезапишет существующий файл - будьте внимательны!

Пример:

f = open("log.txt","w+")

Кодировка

docs.python.org/3/library/codecs.html#standard-encodings

import sys print(sys.getdefaultencoding())

utf-8

Открыть файл

Синтаксис:

open(path_to_file, mode, encoding)

По умолчанию используется кодировка utf-8

Чтобы открыть файл для чтения выполните

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'

Закрыть файл

Чтобы закрыть файл выполните

f.close()

Менеджер контекста

Предпочтительнее использовать менеджер контекста для работы с файлами

Как только вы выйдете из блока в котором открыт файл - он автоматически закроется

with open('text.txt', 'r') as f: pass print(f.closed)

True

Будем рассматривать примеры работы с файлами как с использованием менеджера контекста так и без него.

read(): чтение файла

Получать данные из файла можно с помощью read()

В неё можно передавать аргумент типа int и тогда будет возвращено соответсвующее количество байт содераждания файла.

Прочитать содержимое файла

with open('sites.txt', 'r') as f: f_contents = f.read() print(f_contents)

www.urn.su www.heihei.ru www.topbicycle.ru

Рассмотрим файл sites.md который состоит из одной строки

topbicycle.ruheihei.rueht1.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()

seek(0): перемещение в начало файла

С помощью seek(0) можно поставить указатель в начало файла.

Перейдём в конец файла sites.md

topbicycle.ruheihei.rueht1.ru

>>> f = open('sites.md', mode='rt', encoding='utf-8') >>> f.read(3)

top

>>> f.read()

'bicyleheihei.rueth1.ru'

>>> f.read()

''

Теперь с помощью seek() поставим указатель в начало

>>> f.seek(0)

0

>>> f.read(3)

'top'

Очистить файл

>>> f = open('sites.md', mode='w', encoding='utf-8') >>> f.write('')

0

>>> f.close() >>> exit()

readline(): построчное чтение

Метод readline() выводит содержимое построчно.

А метод readlines() выводит все строки в виде списка

>>> 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'

>>> f.readline()

''

>>> f.seek(0)

0

>>> f.readlines()

['www.topbicycle.ru\n', 'www.heihei.ru\n', 'eth1.ru']

>>> f.close()

Все строки с символом перехода на новую строку - readlines

with open('sites.txt', 'r') as f: f_contents = f.readlines() print(f_contents)

['www.urn.su\n', 'www.heihei.ru\n', 'www.topbicycle.ru\n']

Строки по одной readline

with open('sites.txt', 'r') as f: f_contents = f.readline() print(f_contents) f_contents = f.readline() print(f_contents)

www.urn.su www.heihei.ru

Строки по одной без лишних переходов end=''

with open('sites.txt', 'r') as f: f_contents = f.readline() print(f_contents, end = '') f_contents = f.readline() print(f_contents, end = '')

www.urn.su www.heihei.ru

Цикл для построчного вывода

with open('sites.txt', 'r') as f: for line in f: print(line, end = '')

www.urn.su www.heihei.ru www.topbicycle.ru

В качестве альтернативы можно использовать sys.stdout.write()

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.txt

www.urn.su www.heihei.ru www.topbicycle.ru

Прочитать определённое количество символов

with open('sites.txt', 'r') as f: f_contents = f.read(20) print(f_contents)

www.urn.su www.heihe

Если выполнять эту команду последовательно - будут прочитаны следующие символы

with open('sites.txt', 'r') as f: f_contents = f.read(20) print(f_contents, end = '') f_contents = f.read(20) print(f_contents, end = '')

www.urn.su www.heihei.ru www.topbicycle.

Цикл для произвольного количества символов .read

with open('sites.txt', '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.urn.su www.heihei.ru www.topbicycle.ru

Выражение f_contents = f.read(size_to_read) нужно для того, чтобы когда файл закончится и f.read(size_to_read) станет нулем len(f_contents) тоже станет нулем и цикл завершится

Имя файла

Пример программы, которая выводит на экран имя файла и режим, в котором он открыт

f = open('log.txt', 'r') print(f.name) print(f.mode) f.close()

Если файл log.txt существует, то в терминале вы увидите

log.txt
r

Копирование файлов

Текстовые файлы

Их можно копировать построчно

with open('sites.txt', 'r') as rf: with open('sites_copy.txt', 'w') as wf: for line in rf: wf.write(line)

cat sites_copy.txt

www.urn.su 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

cat sites.md www.topbicycle.ru www.heihei.ru eth1.ruwww.aviasales.ru www.booking.comwww.tutu.ru www.velodrive.ruwww.velosklad.ru%

topbicycle.ruheihei.ru

ls -l

-rw-r--r-- 1 andrei users 24 Apr 20 15:17 sites.md

Конечный размер файла будет зависеть от опецарионной системы.

В Windows и Linux разные переносы строк, поэтому когда Python применяет свой универсальный перенос строки количество байт может увеличится на 1 а может остаться прежним.

write() возвращает количество байт, переданных в файл, а не фактический размер записанных данных.

Дописать в файл

Если нужно добавить новые данные к предыдущему содержимому без удаления - используется опция a (append)

with open('log.txt', 'a') as f: f.write("some text")

Рассмотрим пример добавления данных в файл 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

Видно, что переносы строк появились только там, где из указали вручную.

Записать json в файл

import json # нужно где-то взять json r = urllib.request.urlopen('http://urn.su/api/v1/getjson') rr = r.read() rj = json.loads(rr) with open('file.txt', 'w') as f: json.dump(rj, f)

Удалить первые несколько строк файла

with open('log.txt', 'a') as fin: data = fin.readlines()[1:] with open('new.txt', 'w') as fout: fout.writelines(data)

Запись вывода программы в файл

Если вы запускаете скрипт из терминала, воспользуйтесь перенаправлением

python script.py > script.log

В самом скрипте можно временно подменить стандартный вывод.

Допустим я делаю запрос к API

(Подробнее про REST API)

import sys … # Сохраним ссылку на оригинальный stdout original_stdout = sys.stdout with open("log.txt", "a") as f: sys.stdout = f print(resp.data) sys.stdout = original_stdout

Тоже самое, если приходит json и хочется записать его красиво

import sys import json … with open("log.txt", "a") as f: sys.stdout = f print(json.dumps(resp.data, indent=4)) sys.stdout = original_stdout

Последовательность Рекамана

import sys from itertools import count, islice def sequence(): """Generate Recaman's sequence.""" seen = set() a = 0 for n in count(1): yield a seen.add(a) c = a - n if c < 0 or c in seen: c = a + n a = c def write_sequence(filename, num): """Write Recaman's sequence to a text file.""" f = open(filename, mode='wt', encoding='utf-8') f.writelines( f"{r}\n" for r in islice(sequence(), num + 1)) f.close() if __name__ == '__main__': write_sequence(filename=sys.argv[1], num=int(sys.argv[2]))

python recaman.py recaman.dat 1000
cat recaman.dat

1 3 6 2 7 … 2687 3685 2686 3686

"""Read and print an integer series.""" import sys def read_series(filename): f = open(filename, mode='rt', encoding='utf-8') series = [] for line in f: a = int(line.strip()) series.append(a) f.close() return series def main(filename): series = read_series(filename) print(series) if __name__ == "__main__": filename = sys.argv[1] main(filename)

python series.py recaman.dat

[0, 1, 3, 6, 2, 7 … 3684, 2687, 3685, 2686, 3686]

Если в файле будут неподходящие данные ошибка будет показана в Traceback, а до закрытия файла дело не дойдёт.

echo badidea >> recaman.dat
python series.py recaman.dat

Traceback (most recent call last): File "/home/andrei/series.py", line 21, in <module> main(filename) File "/home/andrei/series.py", line 15, in main series = read_series(filename) File "/home/andrei/series.py", line 8, in read_series a = int(line.strip()) ValueError: invalid literal for int() with base 10: 'badidea'

Это серьёзная проблема, и обычно решается использованием менеджера контекста , но можно решить и добавлением try , finally

Модифицируем функцию read_series() заодно добавив генератор списка

def read_series(filename): try: f = open(filename, mode='rt', encoding='utf-8') return [int(line.strip()) for line in f] finally: f.close()

С with получается короче.

def read_series(filename): with open(filename, mode='rt', encoding='utf-8') as f: return [int(line.strip()) for line in f]

В write_sequence() из recaman.py тоже лучше применить with

def write_sequence(filename, num): """Write Recaman's sequence to a text file.""" with open(filename, mode='wt', encoding='utf-8') as f: f.writelines( f"{r}\n" for r in islice(sequence(), num + 1))

Пример работы с bytes

bmp.py

"""A module for dealing with BMP bitmap image files.""" def write_grayscale(filename, pixels): """Creates and writes a grayscale BMP file. Args: filename: The name of the BMP file to be created. pixels: A rectangular image stored as a sequence of rows. Each row must be an iterable series of integers in the range 0-255. Raises: ValueError: If any of the integer values are out of range. OSError: If the file couldn't be written. """ height = len(pixels) width = len(pixels[0]) with open(filename, 'wb') as bmp: # BMP Header bmp.write(b'BM') size_bookmark = bmp.tell() # The next four bytes hold the filesize as a 32-bit bmp.write(b'\x00\x00\x00\x00') # little-endian integer. Zero placeholder for now. bmp.write(b'\x00\x00') # Unused 16-bit integet - should be zero bmp.write(b'\x00\x00') # Unused 16-bit integet - should be zero pixel_offset_bookmark = bmp.tell() # The next four bytes hold the integer offset to the bmp.write(b'\x00\x00\x00\x00') # pixel data. Zero placeholder for now. # Image Header bmp.write(b'\x28\x00\x00\x00') # Image header size in bytes - 40 decimal bmp.write(_int32_to_bytes(width)) # Image width in pixels bmp.write(_int32_to_bytes(height)) # Image height in pixels bmp.write(b'\x01\x00') # Number of image planes bmp.write(b'\x08\x00') # Bits per pixel 8 for grayscale bmp.write(b'\x00\x00\x00\x00') # No compression bmp.write(b'\x00\x00\x00\x00') # Zero for uncompressed images bmp.write(b'\x00\x00\x00\x00') # Unused pixels per meter bmp.write(b'\x00\x00\x00\x00') # Unused pixels per meter bmp.write(b'\x00\x00\x00\x00') # Use whole color table bmp.write(b'\x00\x00\x00\x00') # All colors are important # Color palette - a linear grayscale for c in range(256): bmp.write(bytes((c, c, c, 0))) # Blue, Green, Red, Zero # Pixel data pixel_data_bookmark = bmp.tell() for row in reversed(pixels): # BMP files are bottom to top row_data = bytes(row) bmp.write(row_data) padding = b'\x00' * ((4 - (len(row) % 4)) % 4) # Pad row to multiple of four bytes bmp.write(padding) # End of file eof_bookmark = bmp.tell() # Fill in file size placeholder bmp.seek(size_bookmark) bmp.write(_int32_to_bytes(eof_bookmark)) # Fill in pixel offset placeholder bmp.seek(pixel_offset_bookmark) bmp.write(_int32_to_bytes(pixel_data_bookmark)) def _int32_to_bytes(i): """Convert an integer to four bytes in little-endian format.""" # &: Bitwise-and # >>: Right-shift return bytes( (i & 0xff, i >> 8 & 0xff, i >> 16 & 0xff, i >> 24 & 0xff) )

fractal.py

import math def mandel(real, imag): """The logarighm of number of iterations needed to determine whether a complex point is in the Mandelbrot set. Args: real: The real coordinate imag: The imaginary coordinate Returns: An integer in the range 1-255. """ x = 0 y = 0 for i in range(1, 257): if x*x + y*y > 4.0: break xt = real + x*x - y*y y = imag + 2.0 * x * y x = xt return int(math.log(i) * 256 / math.log(256)) -1 def mandelbrot(size_x, size_y): """Make an Mandelbrot set image. Args: size_x: Image width size_y: Image height Returns: A list of lists of integers in the range 0-255 """ return [[mandel((3.5 * x / size_x) - 2.5, (2.0 * y / size_y) - 1.0) for x in range(size_x)] for y in range(size_y)]

>>> import fractal >>> pixels = fractal.mandelbrot(448, 256) >>> import reprlib >>> reprlib.repr(pixels)

'[[31, 31, 31, 31, 31, 31, ...], [31, 31, 31, 31, 31, 31, ...], [31, 31, 31, 31, 31, 31, ...], [31, 31, 31, 31, 31, 31, ...], [31, 31, 31, 31, 31, 31, ...], [31, 31, 31, 31, 31, 31, ...], ...]'

>>> import bmp >>> bmp.write_grayscale("mandel.bmp", pixels)

Определение размеров bmp изображения

def dimensions(filename): """Determine the dimensions in pixels of a BMP image. Args: filename: The filename of a BMP file. Returns: A tuple containing two integers with the width and height in pixels. Raises: ValueError: If the file was not a BMP file. OSError: If there was a problem reading the file. """ with open(filename, 'rb') as f: magic = f.read(2) if magic != b'BM': raise ValueError(f"{filename} is not a BMP file") f.seek(18) width_bytes = f.read(4) height_bytes = f.read(4) return ( _bytes_to_int32(width_bytes), _bytes_to_int32(height_bytes)) def _bytes_to_int32(b): "Convert a bytes object containing four bytes into an integer." return b[0] | (b[1] << 8) | (b[2] << 16) | (b[3] << 24)

>>> import bmp >>> bmp.dimensions("mandel.bmp")

(448, 256)

encode()

>>> s = "abcd" >>> len(s) 4 >>> s.encode() b'abcd' >>> s = "сайт" >>> len(s) 4 >>> s.encode() b'\xd1\x81\xd0\xb0\xd0\xb9\xd1\x82' >>> s = "сайт" >>> s.encode('iso-8859-5') b'\xe1\xd0\xd9\xe2' >>> s.encode('utf-8') b'\xd1\x81\xd0\xb0\xd0\xb9\xd1\x82'

Японские символы

>>> j = "平仮" >>> len(j) 2 >>> j.encode() b'\xe5\xb9\xb3\xe4\xbb\xae' >>> j.encode("SHIFT-JIS") b'\x95\xbd\x89\xbc'

Преобразовать в "кириллический" iso-8859-5 не получится

>>> j.encode('iso-8859-5') Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/lib/python3.8/encodings/iso8859_5.py", line 12, in encode return codecs.charmap_encode(input,errors,encoding_table) UnicodeEncodeError: 'charmap' codec can't encode characters in position 0-1: character maps to <undefined>

j.encode('iso-8859-5') # это тоже что и j.encode('iso-8859-5', errors='strict')

>>> j.encode('iso-8859-5', errors='ignore') b'' >>> j = "Japanese symbol 平仮" >>> j.encode('iso-8859-5', errors='ignore') b'Japanese symbol '

Определить кодировки файлов

Пример скрипта для определения кодировок файлов. О том как создать файлы в разных кодировках в Linux читайте здесь

python -m pip install python-magic

import magic def get_encoding(sample): blob = open(sample, 'rb').read() m = magic.open(magic.MAGIC_MIME_ENCODING) m.load() encoding = m.buffer(blob) return encoding files = ['utf-8-file', 'windows-1251-file', 'shift-jis-file'] for f in files: print(get_encoding(f))

utf-8 iso-8859-1 unknown-8bit

С определением SHIFT-JIS пока проблемы

Путь до файла

python -m pip install pathlib

import pathlib from pathlib import Path dir_path = pathlib.Path.cwd() print(dir_path)

/home/andrei/sandbox/python/file_path

Прочитать файл из другой директории

Предположим, что мы находимся в директории one проекта file_path:

file_path/ ├── one │ └── path.py └── two └── sites.txt

cat ../two/sites.txt

www.heihei.ru

Прочитать файл sites.txt с помощью Python поможет библиотека pathlib

import pathlib from pathlib import Path dir_path = pathlib.Path.cwd() path = Path(dir_path, "..", "two", "sites.txt") with open(path, "r") as f: sites = f.read() print(sites)

python path.py

www.heihei.ru

glob

Оффициальная документация

Предположим, что мы находимся в директории glob_ex:

glob_ex/ └── sample_files ├── it.txt └── travel.txt

# glob_ex.py import glob files = glob.glob("./sample_files/*.txt") print(files)

python glob_ex.py

['./sample_files/it.txt', './sample_files/travel.txt']

# glob_ex.py import glob files = glob.glob("./sample_files/*.txt") for file in files: print(file) with open(file, "r") as f: lines = f.readlines() for line in lines: print(line)

python glob_ex.py

./sample_files/it.txt beget.com kaspersky.com ./sample_files/travel.txt aviasales.com booking.com

Похожие статьи
Python
Интерактивный режим
str: строки
\: перенос строки
Списки []
if, elif, else
Циклы
Функции
try except
Пакеты
*args **kwargs
ООП
enum
Опеределить тип переменной Python
Тестирование с помощью Python
Работа с REST API на Python
Скачать файл по сети
SQLite3: работа с БД
datetime: Дата и время в Python
json.dumps
Selenium + Python
Сложности при работе с Python
DJANGO
Flask
Скрипт для ZPL принтера
socket :Python Sockets
Виртуальное окружение
subprocess: выполнение bash команд из Python
multiprocessing: несколько процессов одновременно
psutil: cистемные ресурсы
sys.argv: аргументы командной строки
PyCharm: IDE
pydantic: валидация данных
paramiko: SSH из Python
enumerate
logging: запись в лог
Обучение программированию на Python
Контакты и сотрудничество:
Рекомендую наш хостинг beget.ru
Пишите на info@eth1.ru если Вы:
1. Хотите написать статью для нашего сайта или перевести статью на свой родной язык.
2. Хотите разместить на сайте рекламу, подходящуюю по тематике.
3. Хотите поддержать сайт материально
4. Нашли на сайте ошибку, неточности, баг и т.д. ... .......