Part 6

Запись файлов

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

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

Создание нового файла

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

with open("new_file.txt", "w") as my_file:
    # code to write something to the file

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

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

with open("new_file.txt", "w") as my_file:
    my_file.write("Hello there!")

Когда вы выполните программу, в директории появится новый файл с именем new_file.txt. Его содержимое будет выглядеть так:

Пример данных

Hello there!

Если вы хотите, чтобы в файле были переносы строк, их придется добавлять вручную — функция write работает не совсем так же, как более привычная функция print, хотя они похожи. Поэтому следующая программа

with open("new_file.txt", "w") as my_file:
    my_file.write("Hello there!")
    my_file.write("This is the second line")
    my_file.write("This is the last line")

приведет к файлу с таким содержимым:

Пример данных

Hello there!This is the second lineThis is the last line

Переносы строк достигаются добавлением символов новой строки \n к строкам-аргументам:

with open("new_file.txt", "w") as my_file:
    my_file.write("Hello there!\n")
    my_file.write("This is the second line\n")
    my_file.write("This is the last line\n")

Теперь содержимое new_file.txt будет выглядеть так:

Пример данных

Hello there! This is the second line This is the last line

Loading

Добавление данных в существующий файл

Если вы хотите добавить данные в конец файла, а не перезаписывать весь файл, файл нужно открыть в режиме добавления с аргументом a.

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

Следующая программа открывает файл new_file.txt и добавляет пару строк текста в конец:

with open("new_file.txt", "a") as my_file:
    my_file.write("This is the 4th line\n")
    my_file.write("And yet another line.\n")

После выполнения этой программы содержимое файла будет выглядеть так:

Пример вывода

Hello there! This is the second line This is the last line This is the 4th line And yet another line.

На практике добавление данных в файлы — не очень распространенная задача.

Чаще файл читается, обрабатывается и целиком перезаписывается. Например, когда содержимое должно измениться в середине файла, обычно проще всего перезаписать весь файл.

Loading

Запись CSV-файлов

CSV-файлы можно записывать построчно с помощью метода write, как и любые другие файлы. Следующий пример создает файл coders.csv, где каждая строка содержит имя, рабочую среду, любимый язык и годы опыта одного программиста. Поля разделены точкой с запятой.

with open("coders.csv", "w") as my_file:
    my_file.write("Eric;Windows;Pascal;10\n")
    my_file.write("Matt;Linux;PHP;2\n")
    my_file.write("Alan;Linux;Java;17\n")
    my_file.write("Emily;Mac;Cobol;9\n")

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

Пример вывода

Eric;Windows;Pascal;10 Matt;Linux;PHP;2 Alan;Linux;Java;17 Emily;Mac;Cobol;9

Что если данные, которые нужно записать, хранятся в памяти компьютера в списке?

coders = []
coders.append(["Eric", "Windows", "Pascal", 10])
coders.append(["Matt", "Linux", "PHP", 2])
coders.append(["Alan", "Linux", "Java", 17])
coders.append(["Emily", "Mac", "Cobol", 9])

Мы можем собрать строку, которую хотим записать, как f-строку и записать готовую строку в файл так:

with open("coders.csv", "w") as my_file:
    for coder in coders:
        line = f"{coder[0]};{coder[1]};{coder[2]};{coder[3]}"
        my_file.write(line+"\n")

Если каждый список с данными программиста был бы очень длинным, с большим количеством элементов, собирать строку вручную было бы довольно неудобно. Вместо этого можно использовать цикл for:

with open("coders.csv", "w") as my_file:
    for coder in coders:
        line = ""
        for value in coder:
            line += f"{value};"
        line = line[:-1]
        my_file.write(line+"\n")

Очистка содержимого файлов и удаление файлов

Иногда необходимо очистить содержимое существующего файла. Для этого достаточно открыть файл в режиме записи и сразу закрыть его:

with open("file_to_be_cleared.txt", "w") as my_file:
    pass

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

Можно также обойтись без блока with, используя следующую однострочную команду:

open('file_to_be_cleared.txt', 'w').close()
Loading
Loading

Работа с данными в формате CSV

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

CSV-файл, переданный программе на вход, выглядит так:

Пример данных

Peter;4;2;3;5;4;0;0 Paula;7;2;8;3;5;4;5 Susan;3;4;3;5;3;4;4 Emily;6;6;5;5;0;4;8

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

Файл читается по принципам, рассмотренным в предыдущем разделе. Данные хранятся в словаре, где ключ — имя студента, а значение — список баллов, полученных студентом, в целочисленном формате:

def read_weekly_points(filename):
    weekly_points = {}
    with open(filename) as my_file:
        for line in my_file:
            parts = line.split(";")
            point_list = []
            for points in parts[1:]:
                point_list.append(int(points))
            weekly_points[parts[0]] = point_list

    return weekly_points

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

def grade(points):
    if points < 20:
        return 0
    elif points < 25:
        return 1
    elif points < 30:
        return 2
    elif points < 35:
        return 3
    elif points < 40:
        return 4
    else:
        return 5

def save_results(filename, weekly_points):
    with open(filename, "w") as my_file:
        for name, point_list in weekly_points.items():
            point_sum = sum(point_list)
            my_file.write(f"{name};{point_sum};{grade(point_sum)}\n")

Такая структура позволяет написать очень простую главную часть программы. Обратите внимание, что имена файлов для чтения и записи передаются как аргументы в главной части:

weekly_points = read_weekly_points("weekly_points.csv")
save_results("results.csv", weekly_points)

Когда главная часть программы выполняется, содержимое файла results.csv, созданного в результате, выглядит так:

Пример данных

Peter;18;0 Paula;34;3 Susan;26;2 Emily;41;5

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

Допустим, мы хотим добавить функцию для вывода оценки одного студента. У нас уже есть функция, которая определяет оценку студента, поэтому мы можем использовать ее в новой функции:

def get_grade(student_name, weekly_points):
    for name, point_list in weekly_points.items():
        if name == student_name:
            return grade(sum(point_list))


weekly_points = read_weekly_points("weekly_points.csv")
print(get_grade("Paula", weekly_points))
Пример данных

3

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

Loading
Loading
Loading
Вы дошли до конца этого раздела! Перейти к следующему разделу:

Текущие баллы можно посмотреть в синем индикаторе в правом нижнем углу страницы.