Файлы в python

open() и close()

Функцию open() нужно вызвать до того, как:

  • read file
  • write in new file
  • add smth to file
  • rewrite file

Usage

файл_об = open(имя_файла, режим)

файл_об - объект файла, возвращаемый функцией open()
режим - строка, указывающая на тип файла и действия, которые нужно произвести над файлом имя_файла

Первая буква строки режим указывает на операцию:
r - read
w - write. Если файла нет - будет создан. Если файл есть - будет перезаписан.
x - write, только если такого файла еще нет, иначе выкинет FileExistsError
a - add данных в конец файла, если он существует

Вторая буква строки режим указывает на тип файла:
t или ничего - текстовый файл
b - binary

После открытия файла нужно вызвать функции для чтения/записи. Затем нужно закрыть файл, для гарантии того, что все операции записи завершены, а память освобождена. (Впрочем, оператор with может это всё автоматизировать)

Пример создания пустого файла(открытия и закрытия файла):

>>> fout = open('1212.txt', 'w')
>>> fout.close()

Проверка файла

os.path.isfile()

>>> os.path.isfile('sasd')
True
>>> os.path.isfile('.')
False

exists()

>>> import os
>>> os.path.exists('oops.txt')
True
>>> os.path.exists('./oops.txt')
True
>>> os.path.exists('waffles')
False
>>> os.path.exists('.')
True
>>> os.path.exists('..')
True

Write

Запись в файл w/ print()

>>> fout = open('1212.txt', 'w')
>>> print('some dumb shiuh', file=fout)
>>> fout.close()

Этот код перезаписал предыдущий 1212.txt.

Запись в файл w/ write()

write() не добавляет никаких пробелов или символов новой строки, в отличие от print():

>>> poem = '''Boys and girls, come out to play.
			  The moon doth shine as bright as day.'''
>>> len(poem)
81
 
>>> fout = open('test', 'w')
>>> fout.write(poem)
81  # Число записанных байтов
>>> fout.close()
 
# или:
>>> print(poem, file=fout)

Запись в бинарник. write()

В этом режиме читаться/записываться будут байты, а не строки:

>>> bdata = bytes(range(0, 256))
>>> len(bdata)
256
 
>>> fout = open('bfile', 'wb')
>>> fout.write(bdata)
256
>>> fout.close()

Запись фрагментами:

>>> fout = open('bfile', 'wb')
>>> size = len(bdata)
>>> offset = 0
>>> chunk = 100
>>> while True:
		if offset > size:
			break
		fout.write(bdata[offset:offset+chunk])
		offset += chunk
 
100
100
56
>>> fout.close()

Запись длинных строк

>>> poem = '''Boys and girls, come out to play.
	The moon doth shine as bright as day.
	The moon doth shine as bright as day.
	The moon doth shine as bright as day.'''
>>> fout = open('test', 'w')
>>> size = len(poem)
>>> offset = 0
>>> chunk = 100
>>> while True:
		if offset > size:
			break
		fout.write(poem[offset:(offset+chunk)])
		offset += chunk
 
100
47

Код выше первым заходом записал 100 символов, затем - 47.

Но лучше всего юзать такой код:

>>> try:
		fout = open('test', 'x')
		fout.write('shmunky junky franky')
except FileExistsError:
		print('test already exists')
 
test already exists

Read

read(), readline(), readlines()

Вызвав read() без аргументов, можно достать весь файл сразу, но будь осторожнее: файл размером 1ГБ потребит 1ГБ памяти.

>>> fin = open('test', 'r')
>>> poem = fin.read()
>>> fin.close()
 
>>> poem
'some dumb shiuh\n'

Можно указать макс. кол-во символов, которое read() вернет за один вызов:

>>> poem = ''
>>> fin = open('test', 'r' )
>>> chunk = 100
>>> while True:
		fragment = fin.read(chunk)
		if not fragment:
			break
		poem += fragment
 
>>> fin.close()
>>> len(poem)
150

После того как прочитается весь файл, дальнейшие вызовы read() будут возвращать пустую строку '', которая рассматривается как False при проверке if not fragment.

Кодировка

В open() можно также передать нужную кодировку:

filiee = read('test', 'r', encoding='utf-8')

Можно считывать построчно w/ readline():

>>> poem = ''
>>> fin = open('test', 'r' )
>>> while True:
		line = fin.readline()
		if not line:
			break
		poem += line
 
>>> fin.close()
>>> len(poem)
150

Для текстового файла даже пустая строка имеет длину (1 - символ новой строки) и такая строка будет считаться True. Когда весь файл будет считан, функция readline() (read() тоже) вернет пустую строку, которая - False.

Но всё же самый простой способ считать текстовый файл - юзать итератор, который будет возвращать по одной строке за раз. Пример ниже похож на предыдущий, но кода меньше:

>>> poem = ''
>>> fin = open('test', 'r' )
>>> for line in fin:
		poem += line
 
>>> fin.close()
>>> len(poem)
150

Чтение бинарника. read()

>>> fin = open('bfile', 'rb')
>>> bdata = fin.read()
>>> len(bdata)
256
>>> fin.close()

Ключевое слово with()

Если забудешь закрыть файл, его закроет Python после того, как будет удалена последняя ссылка на этот файл. если откроешь файл и не закроешь его явно, он будет закрыт автоматически по завершении функции. Файл должен быть закрыт, чтобы все оставшиеся операции записи были завершены.
В Python есть менеджеры контекста для очистки таких объектов, как открытые файлы:

with выражение as переменная
 
>>> with open('relativity', 'w') as fout:
		fout.write(poem)

После того как блок кода, расположенный под менеджером контекста завершится (или нормально, или путем генерации исключения), файл закроется автоматически.

Смена позиции. seek() и tell()

В процессе чтения/записи Python отслеживает местоположение в файле. tell() возвращает текущее смещение от начала файла в байтах. seek() позволяет перейти к другому смещению в файле.
не обязательно прочитывать каждый байт файла, чтобы добраться до последнего.

>>> fin = open('bfile', 'rb')
>>> fin.tell()
0
>>> fin.seek(255)
255

Считывание всех данных от текущей позиции до конца файла:

>>> bdata = fin.read()
>>> len(bdata)
1
>>> bdata[0]
255

seek() также возвращает текущее смещение.

Можно вызвать seek(), передав ей второй аргумент seek(offset, origin):

  • origin == 0 (по дефолту), сместится на offset байтов от начала файла
  • origin == 1, сместится на offset байтов с текущей позиции
  • origin == 2, сместится на offset байтов от конца файла

Эти значения также определены в стандартном модуле os:

>>> import os
>>> os.SEEK_SET
0
>>> os.SEEK_CUR
1
>>> os.SEEK_END
2

последний байт можно считать разными способами:

>>> fin = open('bfile', 'rb')
 
# Один байт перед концом файла:
>>> fin.seek(-1, 2)
255
>>> fin.tell()
255
 
# Считать данные до конца файла:
>>> bdata = fin.read()
>>> len(bdata)
1
>>> bdata[0]
255

Смена позиции на 2 байта до конца файла:

>>> fin.seek(254, 0)
254
>>> fin.tell()
254
 
# или на 1 байт:
>>> fin.seek(1, 1)
255
>>> fin.tell()
255

Эти функции наиболее полезны при работе с бинарниками.
Их можно юзать и для работы с текстовыми файлами, но если файл состоит не только из символов формата ASCII (каждый из которых занимает по 1 байту в памяти), будет трудно определить смещение. Ибо оно будет зависеть от кодировки текста, а самая популярная кодировка (UTF-8) использует разное кол-во байтов для разных символов.

copy() и move()

>>> import shutil
>>> shutil.copy('oops.txt', 'ohno.txt')
>>> shutil.copy('ohno.txt', 'ohyeuh.txt')

rename()

>>> import os
>>> os.rename('ohno.txt', 'ohwell.txt')

link() - создает жесткую ссылку, symlink() - символьную (soft).
islink() проверяет, является ли файл симлинком.

>>> os.link('oops.txt', 'yikes.txt')
>>> os.path.isfile('yikes.txt')
True
>>> os.path.islink('yikes.txt')
False
>>> os.symlink('oops.txt', 'jeepers.txt')
>>> os.path.islink('jeepers.txt')
True

chmod() и chown()

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

Сделаю oops.txt доступным для чтения только владельцу:

>>> os.chmod('oops.txt', 0o400)

Есть вариант юзать (percy shmurky??😱😱) такие выражения вместо восьмеричной суеты:

>>> import stat
>>> os.chmod('oops.txt', stat.S_IRUSR)

remove()

>>> os.remove('oops.txt')
>>> os.path.exists('oops.txt')
False

Соус: Книга “Простой Python Глава 14. “Файлы и каталоги

python