Продвинутая работа с файлами в Python

Содержание
Введение
seek(0): перемещение в начало файла
Записать json в файл
Удалить первые несколько строк файла
Запись вывода программы в файл
Определить кодировки файлов
Прочитать файл из другой директории
Найти строку
Удалить определённую строку
Удалить все между тегами
Дописать после тега
Похожие статьи

Введение

В этой статье вы можете познакомиться с дополнительными примерами работы с файлами в Python 3.

Подразумевается, что вы уже владеете материалом из предыдущей статьи Основы работы с файлами в Python

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

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

python3 files.py

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

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

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

topbicycle.ruheihei.rueth1.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'

Записать 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

Пример работы с 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)

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

Пример скрипта для определения кодировок файлов. О том как создать файлы в разных кодировках в 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 пока проблемы

Путь до файла

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

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

Найти строку

Рассмотрим файл sites.md и постараемся найти строку heihei.ru

topbicycle.ru heihei.ru eht1.ru

Воспользуемся тем, что менеджер контекста возвращает итератор и пройдёмся по файлу методом next()

with open("sites.md", "r") as f: while True: try: line = str(next(f)) print(line) except StopIteration: raise ValueError("End of file") if line == "heihei.ru\n": print("Found heihei.ru!")

topbicycle.ru heihei.ru Found heihei.ru! eth1.ru Traceback (most recent call last): File "/mnt/c/Users/Andrei/readline_ex.py", line 4, in <module> line = str(next(f)) ^^^^^^^ StopIteration During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/mnt/c/Users/Andrei/readline_ex.py", line 7, in <module> raise ValueError("End of file") ValueError: End of file

С помощью цикла for

with open("sites.md", "r") as f: for line in f: if line == "heihei.ru\n": print("Found heihei.ru") else: print("Some other line found")

Some other line found Found heihei.ru Some other line found

Удалить определённую строку

Рассмотрим файл sites.md и постараемся удалить все строки heihei.ru

topbicycle.ru heihei.ru eht1.ru

with open("sites.md", "r") as f: with open("new_file.txt", "w") as nf: for line in f: if line != "heihei.ru\n": nf.write(line) with open("new_file.txt", "r") as nf: print(nf.readlines())

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

Удалить все между двумя строками

Рассмотрим файл sites.md

topbicycle.ru heihei.ru <bikes> Forward Stark Stels </bikes> eth1.ru devhops.ru

Удалим всё между тегами <bikes> и </bikes> и запишем в новый файл.

Включая сами теги:

copy = True with open("sites.md", "r") as f: print(f.readlines()) with open("sites.md", "r") as f: with open("clean.md", "w") as nf: for line in f: if line == "<bikes>\n": copy = False if copy: nf.write(line) if line == "</bikes>\n": copy = True with open("clean.md", "r") as nf: print(nf.readlines())

['topbicycle.ru\n', 'heihei.ru\n', '<bikes>\n', 'Forward\n', 'Stark\n', 'Stels\n', '</bikes>\n', 'eth1.ru\n', 'devhops.ru\n'] ['topbicycle.ru\n', 'heihei.ru\n', 'eth1.ru\n', 'devhops.ru\n']

Оставляя теги:

copy = True with open("sites.md", "r") as f: print(f.readlines()) with open("sites.md", "r") as f: with open("clean.md", "w") as nf: for line in f: if line == "</bikes>\n": copy = True if copy: nf.write(line) if line == "<bikes>\n": copy = False with open("clean.md", "r") as nf: print(nf.readlines())

['topbicycle.ru\n', 'heihei.ru\n', '<bikes>\n', 'Forward\n', 'Stark\n', 'Stels\n', '</bikes>\n', 'eth1.ru\n', 'devhops.ru\n'] ['topbicycle.ru\n', 'heihei.ru\n', '<bikes>\n', '</bikes>\n', 'eth1.ru\n', 'devhops.ru\n']

Более универсальная версия с использованием функций

Здесь изменения будут записаны сразу в исходный файл.

def main(): path = "sites.md" tag = "<bikes>" lines = readlines(path) delete_between_tags(path, lines, tag) def readlines(path): with open(path, "r") as f: lines = f.readlines() return lines def delete_between_tags(path, lines, tag): # либо в цикле обрезать line.strip("\n") tag = str(tag) + "\n" taglist = list(tag) taglist.insert(1, "/") endtag = "".join(taglist) with open(path, "w") as f: copy = True for line in lines: if line == tag: copy = False if copy: f.write(line) if line == endtag: copy = True if __name__ == "__main__": main()

Тег не обязан быть в начале строки, поэтому скрипт можно доработать.

Например, в таком файле предыдущий скрипт ничего не удалит.

heihei.ru <topbicycle.ru> <bikes> Forward Stark Stels </bikes> </topbicycle.ru> eth1.ru devhops.ru

Если мы уверены в том, что каждый тег написан на отдельной строке, достаточно заменить line = tag на tag in line и тоже для endtag.

for line in lines: if tag in line: copy = False if copy: f.write(line) if endtag in line: copy = True

Строка

tag = str(tag) + "\n"

тоже становится не нужна.

heihei.ru <topbicycle.ru> </topbicycle.ru> eth1.ru devhops.ru

Дописать после тега

Рассмотрим файл sites.md

heihei.ru <topbicycle.ru> <bikes> Forward Stark Stels </bikes> </topbicycle.ru> eth1.ru devhops.ru

Допишем следущий текст после тега <bikes>

<german> Cube Drossiger Ghost </german>

def main(): path = "sites.md" tag = "<bikes>" text = """ <german> Cube Drossiger Ghost </german> """ lines = readlines(path) append_after_tag(path, lines, tag, text) def readlines(path): with open(path, "r") as f: lines = f.readlines() return lines def append_after_tag(path, lines, tag, text): tag = str(tag) + "\n" taglist = list(tag) taglist.insert(1, "/") endtag = "".join(taglist) with open(path, "w") as f: write_flag = True for line in lines: f.write(line) if tag in line: if write_flag == True: f.write(text) write_flag = False if __name__ == "__main__": main()

heihei.ru <topbicycle.ru> <bikes> <german> Cube Drossiger Ghost </german> Forward Stark Stels </bikes> </topbicycle.ru> eth1.ru devhops.ru

Похожие статьи
Работа с файлами в Python
Python
Основы работы с файлами
glob: Работа с несколькими файлами
shutil: Работа с несколькими файлами
os
pathlib
Скачать файл по сети
psutil: cистемные ресурсы
Обучение программированию на Python