Операции с CSV 101 — с помощью собственного модуля csv в Python

Первоначально опубликовано 28 августа 2022 года на сайте https://rivea0.github.io/blog.

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

# 📁 students.csv

first,last,house
Harry,Potter,Gryffindor
Hermione,Granger,Gryffindor
Luna,Lovegood,Ravenclaw
Draco,Malfoy,Slytherin

Войти в полноэкранный режим Выйти из полноэкранного режима

Как и в примере выше, самая первая строка (first,last,house) может быть использована для заголовков в качестве имен столбцов.

В Python имеется встроенный модуль csv для эффективной работы с CSV-файлами. Две основные операции — чтение и запись; в Python мы можем выполнять эти операции, используя списки (или, в более общем случае, любую итерабельную переменную) или словари.

csv.reader()

Для чтения CSV-файла можно использовать метод csv.reader(). Рассмотрим пример для нашего простого файла students.csv:

import csv

with open('students.csv') as csvfile:
    reader = csv.reader(csvfile)
    for row in reader:
        if row == ['first', 'last', 'house']:
            continue
        print(f'{row[0]} {row[1]} is in {row[2]}.')


# Harry Potter is in Gryffindor.
# Hermione Granger is in Gryffindor.
# Luna Lovegood is in Ravenclaw.
# Draco Malfoy is in Slytherin.
Вход в полноэкранный режим Выход из полноэкранного режима

Мы используем контекстный менеджер для открытия нашего файла. (Обратите внимание, что нам не нужно указывать аргумент 'r' в качестве режима для операции чтения, поскольку он используется по умолчанию; см. официальную документацию).
Когда мы используем csv.reader(), он возвращает объект reader, который мы храним в переменной reader. Затем, с помощью цикла for, мы выполняем итерацию по каждой строке, которая представляет собой список — и, поскольку наша первая строка — это заголовки, мы проходим эту итерацию и продолжаем. На самом деле, csv.DictReader() лучше подходит для этого, что мы увидим позже. Важным моментом здесь является то, что каждая строка представляет собой список, содержащий три элемента, и мы обращаемся к ним с помощью индексации (row[0], row[1], row[2]). Это не самая элегантная реализация, но достаточно простой пример, чтобы увидеть, как работает csv.reader().

csv.writer()

Допустим, мы хотим добавить Рона Уизли в наш CSV-файл, потому что мы хотим, чтобы наше золотое трио было вместе. Давайте посмотрим, как мы можем это сделать:

import csv

with open('students.csv', 'a') as csvfile:
    writer = csv.writer(csvfile)
    writer.writerow(['Ron', 'Weasley', 'Gryffindor'])
Войти в полноэкранный режим Выйти из полноэкранного режима

Здесь мы снова используем контекстный менеджер, чтобы открыть наш CSV-файл, на этот раз с аргументом 'a' в качестве режима для добавления в него. После этого мы используем csv.writer(), передавая ему наш объект файла. Он возвращает объект writer, который мы сохраняем в переменной writer. Затем мы вызываем метод writerow(), чтобы добавить Рона Уизли. Важно, что в качестве аргумента мы передаем список, хотя это не обязательно должен быть список — мы могли бы использовать итерабельную переменную типа кортежа, но использование списков более распространено.

Теперь наш файл выглядит следующим образом:

# 📁 students.csv

first,last,house
Harry,Potter,Gryffindor
Hermione,Granger,Gryffindor
Luna,Lovegood,Ravenclaw
Draco,Malfoy,Slytherin
Ron,Weasley,Gryffindor

Вход в полноэкранный режим Выход из полноэкранного режима

csv.DictReader()

Мы можем улучшить наш последний пример для чтения нашего CSV-файла. На этот раз воспользуемся csv.DictReader():

import csv

with open('students.csv') as csvfile:
    reader = csv.DictReader(csvfile)
    for row in reader:
        print(f'{row["first"]} {row["last"]} is in {row["house"]}.')


# Harry Potter is in Gryffindor.
# Hermione Granger is in Gryffindor.
# Luna Lovegood is in Ravenclaw.
# Draco Malfoy is in Slytherin.
# Ron Weasley is in Gryffindor.
Войдите в полноэкранный режим Выйти из полноэкранного режима

Как и csv.reader(), csv.DictReader() возвращает объект reader, но на этот раз, как сказано в документации, он «отображает информацию в каждой строке в dict, ключи которого задаются необязательным параметром fieldnames».
Если вы уже поняли, мы не указали параметр fieldnames, поэтому первый ряд используется как полевые имена по умолчанию. Мы можем увидеть это, посмотрев на атрибут fieldnames нашего объекта reader:

import csv

with open('students.csv') as csvfile:
    reader = csv.DictReader(csvfile)
    print(reader.fieldnames) # ['first', 'last', 'house']
Войти в полноэкранный режим Выход из полноэкранного режима

csv.DictReader() определенно сделал код более читабельным, чем предыдущий метод. Давайте посмотрим, как мы можем снова использовать словарь, на этот раз для записи в наш файл.

csv.DictWriter().

Мы уже добавляли Рона Уизли в наш students.csv, давайте добавим еще одного Уизли, на этот раз Джинни. Давайте посмотрим, как это можно сделать с помощью csv.DictWriter():

import csv

with open('students.csv', 'a') as csvfile:
    writer = csv.DictWriter(csvfile, fieldnames=['first', 'last', 'house'])
    writer.writerow({'first': 'Ginny', 'last': 'Weasley', 'house': 'Gryffindor'})
Войти в полноэкранный режим Выход из полноэкранного режима

Мы снова добавляем данные в наш файл, как мы это делали с помощью csv.writer(). Мы создаем объект writer с помощью csv.DictWriter(), конечно, передавая в него сам объект файла, а затем присваивая возвращенный объект writer переменной writer. Обратите внимание, что мы также передаем аргумент fieldnames, который представляет собой список, содержащий имена полей. Это необязательный аргумент, поэтому мы должны передавать его каждый раз, когда используем csv.DictWriter().
Мы снова используем метод writerow(), но на этот раз мы передаем ему словарь, ключами которого являются имена полей, которые мы только что указали.
Теперь наш файл выглядит следующим образом:

# 📁 students.csv

first,last,house
Harry,Potter,Gryffindor
Hermione,Granger,Gryffindor
Luna,Lovegood,Ravenclaw
Draco,Malfoy,Slytherin
Ron,Weasley,Gryffindor
Ginny,Weasley,Gryffindor

Вход в полноэкранный режим Выход из полноэкранного режима

Теперь, что если внутри writerow() передать ключ, которого нет в именах полей? Очевидно, мы получим ValueError, но давайте посмотрим на примере. Говорят, что дом — это не дом, но представьте, что мы по ошибке записали Гриффиндор как home Джинни, а не ее house, как в именах полей. Давайте посмотрим:

import csv

with open('students.csv', 'a') as csvfile:
    writer = csv.DictWriter(csvfile, fieldnames=['first', 'last', 'house'])
    writer.writerow({'first': 'Ginny', 'last': 'Weasley', 'home': 'Gryffindor'})


# ValueError: dict contains fields not in fieldnames: 'home'
Войти в полноэкранный режим Выйти из полноэкранного режима

Это происходит из-за необязательного параметра extrasaction внутри csv.DictWriter(). Его значение по умолчанию 'raise', поэтому, когда ключ не найден в именах полей, он выдает ошибку ValueError. Однако мы можем заставить его игнорировать это, передав значение 'ignore'. Итак, если мы сделаем это:

import csv

with open('students.csv', 'a') as csvfile:
    writer = csv.DictWriter(csvfile, fieldnames=['first', 'last', 'house'], extrasaction='ignore')
    writer.writerow({'first': 'Ginny', 'last': 'Weasley', 'home': 'Gryffindor'})
Войти в полноэкранный режим Выйти из полноэкранного режима

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

# 📁 students.csv

first,last,house
Harry,Potter,Gryffindor
Hermione,Granger,Gryffindor
Luna,Lovegood,Ravenclaw
Draco,Malfoy,Slytherin
Ron,Weasley,Gryffindor
Ginny,Weasley,

Войти в полноэкранный режим Выход из полноэкранного режима

Это не самое мудрое решение, однако, в таких случаях лучше иметь ошибку, если только у нас нет причин поступать иначе.

Мы также можем использовать метод writerows(), чтобы передать запись более чем одной строки. В качестве аргумента он принимает итерабельность, поэтому, допустим, у нас есть два словаря в списке для близнецов Уизли, мы можем добавить их оба сразу:

import csv

with open('students.csv', 'a') as csvfile:
    writer = csv.DictWriter(csvfile, fieldnames=['first', 'last', 'house'])
    twins = [
        {'first': 'Fred', 'last': 'Weasley', 'house': 'Gryffindor'},
        {'first': 'George', 'last': 'Weasley', 'house': 'Gryffindor'}
    ]
    writer.writerows(twins)
Войти в полноэкранный режим Выйти из полноэкранного режима

Теперь наш файл выглядит следующим образом:

# 📁 students.csv

first,last,house
Harry,Potter,Gryffindor
Hermione,Granger,Gryffindor
Luna,Lovegood,Ravenclaw
Draco,Malfoy,Slytherin
Ron,Weasley,Gryffindor
Ginny,Weasley,Gryffindor
Fred,Weasley,Gryffindor
George,Weasley,Gryffindor

Войти в полноэкранный режим Выйти из полноэкранного режима

Константы кавычек

Особенно при работе с объектами writer нам может понадобиться указать, как заключать поля в кавычки — например, чтобы справиться с ситуацией, когда наши поля могут включать сам символ-разделитель. Для этого мы можем использовать необязательный параметр quoting для методов writer, а также параметр quotechar, чтобы указать, какой символ использовать для кавычек.

Модуль csv имеет четыре константы, которые можно использовать для quoting:

csv.QUOTE_ALL: Для цитирования всех полей.

Рассмотрим простой пример с csv.QUOTE_ALL, используя csv.DictWriter(). Как вы можете себе представить, это приведет к цитированию всех полей:

import csv

with open('students.csv', 'a') as csvfile:
    writer = csv.DictWriter(csvfile, fieldnames=['first', 'last', 'house'], quotechar='"', quoting=csv.QUOTE_ALL)
    writer.writerow({'first': 'Cho', 'last': 'Chang', 'house': 'Ravenclaw'})
Войти в полноэкранный режим Выйти из полноэкранного режима

Теперь все поля Чо Чанга заключены в кавычки:

# 📁 students.csv

first,last,house
Harry,Potter,Gryffindor
Hermione,Granger,Gryffindor
Luna,Lovegood,Ravenclaw
Draco,Malfoy,Slytherin
Ron,Weasley,Gryffindor
Ginny,Weasley,Gryffindor
Fred,Weasley,Gryffindor
George,Weasley,Gryffindor
"Cho","Chang","Ravenclaw"

Ввести полноэкранный режим Выйти из полноэкранного режима

Заключение

Удобство, которое предоставляет Python благодаря своим встроенным модулям, действительно ценно, и модуль csv не является исключением. В этой статье мы рассмотрели очень простые операции чтения и записи файлов CSV, но всегда есть еще много интересного, и первой остановкой всегда является официальная документация. Надеюсь, теперь вы понимаете некоторые основы работы с файлами CSV.
Удачного кодирования.

Оцените статью
devanswers.ru
Добавить комментарий