2.8. Работа с файлами

На серьезных олимпиадах, а также во многих других ситуациях, вам надо читать данные не с клавиатуры, а из файла, и выводить данные в файл, а не на «экран».

(В таком случае, конечно, вы должны знать имена этих файлов; в задачах они, как правило, указаны; на алгопроге имена файлов почти всегда — input.txt для входных данных и output.txt для выходных.)

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

2.8.1. Ввод данных

2.8.1.1. Чтение по аналогии с input

Чтобы считать данные из файла, вам надо сначала «открыть файл на чтение». Это делается командой

f = open("input.txt", "r")

Здесь input.txt — файл, откуда надо считать данные, параметр "r" указывает, что вы собираетесь именно читать (read) данные, а не записывать (write, см. ниже).

Далее с полученным объектом f можно работать. Самая простая операция — f.readline() — возвращает очередную строку файла. Это полный аналог input(), за исключением того, что на конце полученной строки будет специальный символ перевода строки "\n" (при выводе строки на экран он не будет заметен, но переведет лишний раз строку). Как правило, он вам будет мешать, но вы его можете легко убрать методом .rstrip("\n"), например, f.readline().rstrip("\n").

Пример. Пусть во входном файле два числа по одному на строке. С клавиатуры вы бы считывали так:

a = int(input())
b = int(input())

Тогда из файла надо считывать так:

f = open("input.txt", "r")
a = int(f.readline().rstrip("\n"))
b = int(f.readline().rstrip("\n"))

Аналогично, если два числа в одной строке. С клавиатуры это считывается так:

a, b = map(int, input().split())

Тогда из файла считываем так:

f = open("input.txt", "r")
a, b = map(int, f.readline().rstrip("\n").split())

Более сложный пример: сначала число N, а потом N строк по одному числу в каждой. С клавиатуры:

n = int(input())
for i in range(n):
    x = int(input())
    #... что-то делаем с x

Из файла:

f = open("input.txt", "r")
n = int(f.readline().rstrip("\n"))
for i in range(n):
    x = int(f.readline().rstrip("\n"))
    #... что-то делаем с x

2.8.1.2. Чтение до конца файла

Пока файл не кончился, функция readline будет вам всегда возвращать непустую строку (в ней будет как минимум символ "\n"). Как только файл кончится, readline вернет пустую строку. Поэтому читать до конца файла можно так:

f = open("input.txt", "r")
while True:
    s = f.readline()
    if s == "":
        break
    # обрабатываем s, в частности, теперь можно вызвать s = s.rstrip("\n")

Альтернативный вариант — можно сразу считать весь файл в массив строк:

data = open("input.txt", "r").readlines()

Теперь data — это массив строк, каждый элемент которого — это очередная строка из входного файла. Например, если в файле было написано

1 2 3
4 5 6
some text

то data будет содержать массив ["1 2 3\n", "4 5 6\n", "some text\n"], и дальше вы можете работать с этим массивом как вам надо.

Еще можно написать open("input.txt", "r").read(), это считает весь файл в одну большую строку (в том числе в середине этой строки могут быть символы перевода строки, но это все равно будет одна большая строка, а не массив строк).

2.8.2. Вывод

Для вывода данных вы можете открыть файл на вывод:

f = open("output.txt", "w")

(буква w обозначает write, запись). И дальше можно использовать f в качестве опционального аргумента уже знакомой вам функции print:

print(a, b, file=f)

После окончания всего вывода рекомендуется вызвать f.close(), чтобы данные реально оказались записаны на диск (хотя в большинстве случаев все работает и без этого).

2.8.3. Как это использовать в олимпиадах

Основное достоинство ввода из файлов при решении алгоритмических задач (на олимпиадах, тут на сайте и т.д.) — что вам не надо каждый раз заново вводить весь тест. Если вы отлаживаете программу на некотором тесте, разбираетесь, почему она не работает, пытаетесь исправить ошибки, вы будете много раз запускать программу на одном и том же тесте. Каждый раз его вводить — сложно и долго. Намного проще его один раз записать в файл, и дальше делать ввод из файла.

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

А именно, во многих задачах у вас чтение данных идет не до конца файла — например, вы считываете только два числа, или только одну строку, или вам задается число N и дальше N чисел — во всех этих случаях программе не важно, что идет после этих данных. Вы там можете хранить другие тесты, а потом, когда вам нужно, переносите просто нужный тест в самое начало файла.

(А вообще, можете даже написать программу так, чтобы она обрабатывала вообще все тесты, которые есть во входном файле — это так называемый мультитест. На тестирующем сервере будет только один тест, и программа отработает только его, а при вашем тестировании программа будет сразу запускаться на многих тестах. А еще, бывают задачи, где во входных данных сразу мультитест, т.е. задается сразу много тестов. Тогда тем более вы можете тестировать сразу на многих тестах.)

Ну и при стресс-тестировании ввод из файла вам тоже будет удобнее.