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

Открытие файла

Для открытия файла мы используем функцию open(). Она требует два аргумента: первый — путь к файлу или его имя, второй — в каком режиме его следует открыть. Режимы могут быть следующими:

  • “r” -> открыть только для чтения, вы можете читать файл, но не можете редактировать / удалять что-либо внутри
  • “w” -> открыть с возможностью записи, это означает, что если файл существует, то удаляется весь его содержимое и открывается для записи
  • “a” -> открыть в режиме добавления

Режим по умолчанию — только для чтения, то есть если вы не укажете режим, файл будет открыт только для чтения. Давайте откроем файл:

>>> fobj = open("love.txt")
>>> fobj
<open file 'love.txt', mode 'r' at 0xb7f2d968>

Закрытие файла

После открытия файла его всегда следует закрывать. Для этого мы используем метод close().

>>> fobj = open("love.txt")
>>> fobj
<open file 'love.txt', mode 'r' at 0xb7f2d968>
>>> fobj.close()

Важно

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

  • Существует верхний предел количества файлов, которые программа может открыть. Если вы превысите этот лимит, нет надежного способа восстановления, поэтому программа может аварийно завершить работу.
  • Каждый открытый файл потребляет часть основной памяти для структур данных, связанных с ним, таких как дескриптор файла или блокировки файлов и т.д. Таким образом, вы можете в конечном итоге тратить много памяти, если у вас открыто больше файлов, которые не полезны или непригодны.
  • Открытые файлы всегда подвержены риску повреждения и потери данных.

Чтение файла

Для чтения всего файла сразу используйте метод read().

>>> fobj = open("sample.txt")
>>> fobj.read()
'I love Python\nPradeepto loves KDE\nSankarshan loves Openoffice\n'

Если вы вызовете read() снова, он вернет пустую строку, так как весь файл уже прочитан. Для чтения одной строки за раз можно использовать readline().

>>> fobj = open("sample.txt")
>>> fobj.readline()
'I love Python\n'
>>> fobj.readline()
'Pradeepto loves KDE\n'

Для чтения всех строк в список используйте метод readlines().

>>> fobj = open("sample.txt")
>>> fobj.readlines()
['I love Python\n', 'Pradeepto loves KDE\n', 'Sankarshan loves Openoffice\n']

Вы можете даже перебирать строки в объекте файла.

>>> fobj = open("sample.txt")
>>> for x in f:
...     print(x, end=' ')
...
I love Python
Pradeepto loves KDE
Sankarshan loves Openoffice

Давайте напишем программу, которая будет принимать имя файла в качестве ввода от пользователя и отображать содержимое файла в консоли.

#!/usr/bin/env python3
name = input("Enter the file name: ")
fobj = open(name)
print(fobj.read())
fobj.close()

В последней строке вы можете увидеть, что мы закрыли объект файла с помощью метода close().

Вывод:

$ ./showfile.py
Enter the filename: sample.txt
I love Python
Pradeepto loves KDE
Sankarshan loves Openoffice

Запись в файл

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

>>> fobj = open("ircnicks.txt", 'w')
>>> fobj.write('powerpork\n')
>>> fobj.write('indrag\n')
>>> fobj.write('mishti\n')
>>> fobj.write('sankarshan')
>>> fobj.close()

Теперь прочитаем файл, который мы только что создали

>>> fobj = open('ircnicks.txt')
>>> s = fobj.read()
>>> print s
powerpork
indrag
mishti
sankarshan

copyfile.py

В этом примере мы скопируем заданный текстовый файл в другой файл.

#!/usr/bin/env python3
import sys
if len(sys.argv) < 3:
    print("Wrong parameter")
    print("./copyfile.py file1 file2")
    sys.exit(1)
f1 = open(sys.argv[1])
s = f1.read()
f1.close()
f2 = open(sys.argv[2], 'w')
f2.write(s)
f2.close()

Примечание

Такой способ чтения файла не всегда является хорошей идеей, так как файл может быть очень большим для чтения и размещения в памяти. Всегда лучше читать файл известного размера и записывать его в новый файл.

Здесь мы использовали новый модуль sys. sys.argv содержит все параметры командной строки. Помните команду cp в shell, после cp мы вводим сначала файл для копирования, а затем новое имя файла.

Первое значение в sys.argv — это имя самой команды.

#!/usr/bin/env python3
import sys
print("First value", sys.argv[0])
print("All values")
for i, x  in enumerate(sys.argv):
    print(i, x)

Результат:

$ ./argvtest.py Hi there
First value ./argvtest.py
All values
0 ./argvtest.py
1 Hi
2 there

Здесь мы использовали новую функцию enumerate(iterableobject), которая возвращает номер индекса и значение из итерируемого объекта.

Случайное перемещение в файле

Вы также можете случайным образом перемещаться внутри файла с помощью метода seek(). Он принимает два аргумента: смещение и whence. Чтобы узнать больше об этом, давайте прочитаем, что говорит нам помощь Python

seek(...)
seek(offset[, whence]) -> None. Переместиться на новую позицию в файле.

Аргумент offset - это количество байтов. Необязательный аргумент whence по умолчанию равен 0 (смещение от начала файла, смещение должно быть >= 0); другие значения - 1 (перемещение относительно текущей позиции, положительное или отрицательное), и 2 (перемещение относительно конца файла, обычно отрицательное, хотя многие платформы позволяют искать за пределами конца файла). Если файл открыт в текстовом режиме, только смещения, возвращаемые tell(), являются законными. Использование других смещений вызывает неопределенное поведение. Обратите внимание, что не все файловые объекты могут быть перемещены.

Давайте рассмотрим один пример

>>> fobj = open('/tmp/tempfile', 'w')
>>> fobj.write('0123456789abcdef')
>>> fobj.close()
>>> fobj = open('/tmp/tempfile')
>>> fobj.tell()    # сообщает нам текущую позицию смещения
0L
>>> fobj.seek(5) # Переходим к 5-му байту
>>> fobj.tell()
5L
>>> fobj.read(1) # Читаем 1 байт
'5'
>>> fobj.seek(-3, 2) # Переходим к 3-му байту от конца
>>> fobj.read() # Читаем до конца файла
'def'

Подсчет пробелов, табуляций и новых строк в файле

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

#!/usr/bin/env python3

import os
import sys


def parse_file(path):
    """
    Разбирает текстовый файл по указанному пути и возвращает информацию о пробелах, табуляциях и новых строках.

    :arg path: Путь к текстовому файлу для разбора

    :return: Кортеж с количеством пробелов, табуляций и строк.
    """
    fd = open(path)
    i = 0
    spaces = 0
    tabs = 0
    for i,line in enumerate(fd):
        spaces += line.count(' ')
        tabs += line.count('\t')
    #Теперь закроем открытый файл
    fd.close()

    #Возвращаем результат в виде кортежа
    return spaces, tabs, i + 1

def main(path):
    """
    Функция, которая выводит количество пробелов, табуляций и строк в файле.

    :arg path: Путь к текстовому файлу для разбора
    :return: True, если файл существует, или False.
    """
    if os.path.exists(path):
        spaces, tabs, lines = parse_file(path)
        print("Пробелы %d. Табуляции %d. Строки %d" % (spaces, tabs, lines))
        return True
    else:
        return False


if __name__ == '__main__':
    if len(sys.argv) > 1:
        main(sys.argv[1])
    else:
        sys.exit(-1)
    sys.exit(0)

Как видите, в программе есть две функции: main и parse_file, где вторая функция фактически разбирает файл и возвращает результат, а мы выводим результат в функции main. Разделение кода на более мелкие единицы (функции) помогает организовать код и облегчает написание тестов для функций.

Использование оператора with

В реальных сценариях мы должны стараться использовать оператор with. Он позаботится о закрытии файла за вас.

>>> with open('setup.py') as fobj:
...     for line in fobj:
...         print line,
...
#!/usr/bin/env python
"""Проект Факториал"""
from setuptools import find_packages, setup

setup(name = 'factorial',
    version = '0.1',
    description = "Модуль для вычисления факториала.",
    long_description = "Тестовый модуль для нашей книги.",
    platforms = ["Linux"],
    author="Кушал Данс",
    author_email="kushaldas@gmail.com",
    url="http://pymbook.readthedocs.org/en/latest/",
    license = "http://www.gnu.org/copyleft/gpl.html",
    packages=find_packages()
    )

Давайте напишем немного реального кода

Знаете ли вы, сколько ядер (CPU) в вашем процессоре? Или какое у него название модели? Давайте напишем код, который поможет нам узнать эти вещи.

Если вы используете Linux, то сначала можете посмотреть вывод команды lscpu. Информацию можно найти в файле, расположенном по пути /proc/cpuinfo.

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

Примечание

Всегда помните, что лучше читать файлы построчно, чем целиком. Иногда вам придется читать файлы, которые намного больше доступной оперативной памяти.

После этого попробуйте написать свою собственную команду lscpu на Python :)

Перейти к следующему уроку →