Работа с csv и json файлами в Python

Управление текстовыми файлами формата csv, tsv, dtv, json в языке программирования Python

Формат CSV

CSV (от англ. Comma-Separated Values — значения, разделённые запятыми) — текстовый формат, предназначенный для представления табличных данных. Строка таблицы соответствует строке текста, которая содержит одно или несколько полей, разделенных запятыми.

К примеру, следующая таблица:

RankLanguageShare
1Python31.17%
2Java17.75%
3JavaScript8%
4C#7.05%
5PHP6.09%

в формате csv будет выглядеть следующим образом:

1
2
3
4
5
6
Rank,Language,Share
1,Python,31.17%
2,Java,17.75%
3,JavaScript,8%
4,C#,7.05%
5,PHP,6.09%

Подобные файлы можно обрабатывать вручную подобно txt — считывать, разделять на строки и отдельные элементы по разделителям с помощью split(), но в Python есть отдельный модуль для работы с файлами формата dsv.

Инфо
dsv (англ. delimiter separated values — «значения, разграниченные разделителем») общий формат для форм csv, tsv (англ. tab separated values — «значения, разделенные табуляцией») и других.

Модуль csv

Для использования: import csv

В данном модуле есть два основных объекта: reader и writer, созданные, чтобы читать и создавать csv файлы соответственно.

Чтение через reader

Рассмотрим файл products.csv, содержащий информацию о товарах интернет магазина:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
keywords,price,product_name
Садовый стул,1699,ВЭДДО
Садовый стул,2999,ЭПЛАРО
Садовый табурет,1699,ЭПЛАРО
Садовый стол,1999,ТЭРНО
Складной стол,7499,ЭПЛАРО
Настил,1299,РУННЕН
Стеллаж,1299,ХИЛЛИС
Кружка,39,СТЕЛЬНА
Молочник,299,ВАРДАГЕН
Термос для еды,699,ЭФТЕРФРОГАД
Ситечко,59,ИДЕАЛИСК
Чайник заварочный,499,РИКЛИГ
Кофе-пресс,699,УПХЕТТА
Чашка с блюдцем,249,ИКЕА
Кружка,249,ЭМНТ
Ситечко,199,САККУННИГ
Кружка,199,ФИНСТИЛТ
Тарелка,269,ЭВЕРЕНС

Код ниже читает содержимое файла и выводит его построчно:

1
2
3
4
5
6
import csv

with open('products.csv', encoding='utf-8') as file:
    rows = csv.reader(file)  # создаём reader объект
    for row in rows:
        print(row)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# Вывод:
['keywords', 'price', 'product_name']
['Садовый стул', '1699', 'ВЭДДО']
['Садовый стул', '2999', 'ЭПЛАРО']
['Садовый табурет', '1699', 'ЭПЛАРО']
['Садовый стол', '1999', 'ТЭРНО']
['Складной стол', '7499', 'ЭПЛАРО']
['Настил', '1299', 'РУННЕН']
['Стеллаж', '1299', 'ХИЛЛИС']
['Кружка', '39', 'СТЕЛЬНА']
['Молочник', '299', 'ВАРДАГЕН']
['Термос для еды', '699', 'ЭФТЕРФРОГАД']
['Ситечко', '59', 'ИДЕАЛИСК']
['Чайник заварочный', '499', 'РИКЛИГ']
['Кофе-пресс', '699', 'УПХЕТТА']
['Чашка с блюдцем', '249', 'ИКЕА']
['Кружка', '249', 'ЭМНТ']
['Ситечко', '199', 'САККУННИГ']
['Кружка', '199', 'ФИНСТИЛТ']
['Тарелка', '269', 'ЭВЕРЕНС']

Объект reader дает доступ к построчному итератору, полностью аналогичному работе с файлом или списком.

После выполнения строки rows = csv.reader(file) в переменную rows будет записан итератор, с помощью которого можно «пробежаться» циклом по файлу. В каждой итерации цикла при этом будет доступна соответствующая строка файла, уже разбитая по запятым и представляющая собой список. При этом автоматически будут учтены все нюансы с запятыми внутри кавычек и самими кавычками.

При создании reader объекта можно указать:

  • аргумент delimiter — односимвольная строка, используемая для разделения полей, по умолчанию имеет значение ','
  • аргумент quotechar — односимвольная строка, используемая для кавычек в полях, содержащих специальные символы, по умолчанию имеет значение '"'.

Чтение через DictReader

В модуле csv есть специальный объект DictReader, который поддерживает создание объекта-словаря на основе названий столбцов. С помощью DictReader объекта можно обращаться к полям не по индексу, а по названию, что делает код более понятным.

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
keywords;price;product_name
"Садовый стул, стул для дачи";1699;ВЭДДО
Садовый стул;2999;ЭПЛАРО
Садовый табурет;1699;ЭПЛАРО
Садовый стол;1999;ТЭРНО
"Складной стол, обеденный стол";7499;ЭПЛАРО
Настил;1299;РУННЕН
Стеллаж;1299;ХИЛЛИС
"Кружка, сосуд, стакан с ручкой";39;СТЕЛЬНА
Молочник;299;ВАРДАГЕН
Термос для еды;699;ЭФТЕРФРОГАД
Ситечко;59;ИДЕАЛИСК
Чайник заварочный;499;РИКЛИГ
Кофе-пресс;699;УПХЕТТА
Чашка с блюдцем;249;ИКЕА
"Кружка, стакан с ручкой";249;ЭМНТ
Ситечко;199;САККУННИГ
Кружка;199;ФИНСТИЛТ
"Тарелка, блюдце";269;ЭВЕРЕНС

Теперь прочитаем этот файл, используя DictReader:

1
2
3
4
5
6
import csv

with open('products.csv', encoding='utf-8') as file:
    rows = csv.DictReader(file, delimiter=';', quotechar='"')
    for row in rows:
        print(row)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# Вывод:
{'keywords': 'Садовый стул, стул для дачи', 'price': '1699', 'product_name': 'ВЭДДО'}
{'keywords': 'Садовый стул', 'price': '2999', 'product_name': 'ЭПЛАРО'}
{'keywords': 'Садовый табурет', 'price': '1699', 'product_name': 'ЭПЛАРО'}
{'keywords': 'Садовый стол', 'price': '1999', 'product_name': 'ТЭРНО'}
{'keywords': 'Складной стол, обеденный стол', 'price': '7499', 'product_name': 'ЭПЛАРО'}
{'keywords': 'Настил', 'price': '1299', 'product_name': 'РУННЕН'}
{'keywords': 'Стеллаж', 'price': '1299', 'product_name': 'ХИЛЛИС'}
{'keywords': 'Кружка, сосуд, стакан с ручкой', 'price': '39', 'product_name': 'СТЕЛЬНА'}
{'keywords': 'Молочник', 'price': '299', 'product_name': 'ВАРДАГЕН'}
{'keywords': 'Термос для еды', 'price': '699', 'product_name': 'ЭФТЕРФРОГАД'}
{'keywords': 'Ситечко', 'price': '59', 'product_name': 'ИДЕАЛИСК'}
{'keywords': 'Чайник заварочный', 'price': '499', 'product_name': 'РИКЛИГ'}
{'keywords': 'Кофе-пресс', 'price': '699', 'product_name': 'УПХЕТТА'}
{'keywords': 'Чашка с блюдцем', 'price': '249', 'product_name': 'ИКЕА'}
{'keywords': 'Кружка, стакан с ручкой', 'price': '249', 'product_name': 'ЭМНТ'}
{'keywords': 'Ситечко', 'price': '199', 'product_name': 'САККУННИГ'}
{'keywords': 'Кружка', 'price': '199', 'product_name': 'ФИНСТИЛТ'}
{'keywords': 'Тарелка, блюдце', 'price': '269', 'product_name': 'ЭВЕРЕНС'}

При создании DictReader объекта значениями по умолчанию для аргументов delimiter и quotechar являются ',' (символ запятой) и '"' (символ двойной кавычки) соответственно.

Инфо
Названия столбцов сохраняются в атрибуте fieldnames объекта DictReader

Запись через writer

Для записи данных в csv файл можно использовать специальный writer объект:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import csv

columns = ['first_name', 'second_name', 'class_number', 'class_letter']
data = [['Тимур', 'Гуев', 11, 'А'], ['Руслан', 'Чаниев', 9, 'Б'], ['Артур', 'Харисов', 10, 'В']]

with open('students.csv', 'w', encoding='utf-8', newline='') as file:
    writer = csv.writer(file)
    writer.writerow(columns)                 # запись заголовков
    for row in data:                         # запись строк
        writer.writerow(row)
1
2
3
4
5
# Создаётся файл students.csv с содержимым:
first_name,second_name,class_number,class_letter
Тимур,Гуев,11,А
Руслан,Чаниев,9,Б
Артур,Харисов,10,В

При открытии файла функцией open() был использован параметр newline со значением '' (пустая строка), который отвечает за переводы строк при чтении или записи в текстовый файл. По умолчанию имеет значение None, в этом случае все разделители строк преобразуются в '\n'. Если в файле оказывается лишний перевод строки, то следует использовать этот параметр в режиме newline='', тогда '\n' будет преобразован в пустую строку.

При создании writer объекта так же можно его настраивать, задавая delimiter и многие другие параметры.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import csv

columns = ['first_name', 'second_name', 'class_number', 'class_letter']
data = [['Тимур', 'Гуев', 11, 'А'], ['Руслан', 'Чаниев', 9, 'Б'], ['Роман', 'Белых', 10, 'В']]

with open('students.csv', 'w', encoding='utf-8', newline='') as file:
    writer = csv.writer(file, delimiter=';', quoting=csv.QUOTE_NONNUMERIC)
    writer.writerow(columns)
    for row in data:
        writer.writerow(row)
1
2
3
4
5
# Создаётся файл students.csv с содержимым:
"first_name";"second_name";"class_number";"class_letter"
"Тимур";"Гуев";11;"А"
"Руслан";"Чаниев";9;"Б"
"Роман";"Белых";10;"В"

Значение аргумента quoting=csv.QUOTE_NONNUMERIC означает, что в кавычки будут браться все нечисловые значения. По умолчанию символом кавычки является ", если нужно поменять символ, используется уже знакомый нам именованный аргумент quotechar.

Для quoting существуют и другие константы из модуля csv:

  • QUOTE_ALL: указывает объектам записи указывать все поля
  • QUOTE_MINIMAL: указывает объектам записи заключать в кавычки только те поля, которые содержат специальные символы, такие как разделитель delimiter, кавычка quotechar или любой из символов в lineterminator
  • QUOTE_NONNUMERIC: указывает объектам записи указывать все нечисловые поля
  • QUOTE_NONE: указывает объектам записи никогда не заключать в кавычки поля

Помимо метода writerow() можно использовать и метод writerows(), чтобы записать сразу несколько строк. Единственным аргументом этого метода может быть коллекция коллекций. То есть, каждый элемент списка rows в нашем случае должен быть коллекцией. Если rows будет, например, списком чисел, программа завершится с ошибкой.


Запись через DictWriter

Для записи данных в csv файл также можно использовать DictWriter объект, который позволяет записывать содержимое словаря в файл.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import csv

data = [{'first_name': 'Тимур', 'second_name': 'Гуев', 'class_number': 11, 'class_letter': 'А'},
        {'first_name': 'Руслан', 'second_name': 'Чаниев', 'class_number': 9, 'class_letter': 'Б'},
        {'first_name': 'Роман', 'second_name': 'Белых', 'class_number': 10, 'class_letter': 'В'}]

columns = ['first_name', 'second_name', 'class_number', 'class_letter']

with open('students.csv', 'w', encoding='utf-8', newline='') as file:
    writer = csv.DictWriter(file, fieldnames=columns, delimiter=';', quoting=csv.QUOTE_NONNUMERIC)
    writer.writeheader()                 # запись заголовков
    for row in data:                     # запись строк
        writer.writerow(row)
1
2
3
4
5
# Создаётся файл students.csv с содержимым:
"first_name";"second_name";"class_number";"class_letter"
"Тимур";"Гуев";11;"А"
"Руслан";"Чаниев";9;"Б"
"Роман";"Белых";10;"В"
Опасность
Ключи словарей, которые записываются в файл, должны совпадать с названиями полей, которые переданы в качестве аргумента fieldnames, иначе будет возникать ошибка ValueError.

Формат json

JSON (англ. JavaScript Object Notation, читается как “джЕйсон”) — текстовый формат обмена данными, основанный на синтаксисе объекта в языке программирования JavaScript. Как и многие другие текстовые форматы, JSON легко читается людьми. Несмотря на происхождение от JavaScript, формат считается независимым от языка и может использоваться практически с любым языком программирования.

В отличие от формата csv, данные в формате json не просто разделены запятыми, а чаще всего имеют структуру ключ-значение. Это напоминает словарь Python, но в отличие от словаря, ключи в json могут быть только строками, заключенными в двойные кавычки:

1
2
3
4
5
{
   "firstName": "Тимур",
   "lastName": "Гуев",
   "gender": "мужской"
}

Преимущества, которые сделали этот формат популярным:

  1. не занимает много места, является компактным в написании и быстро компилируется
  2. создание текстового содержимого понятно человеку, просто в реализации, а чтение со стороны среды разработки не вызывает никаких проблем. Чтение может осуществляться и человеком, поскольку ничего сложного в представлении данных нет
  3. структура преобразуется для чтения на любых языках программирования
  4. практически все языки имеют соответствующие библиотеки или другие инструменты для чтения данных JSON

В качестве значений в JSON могут быть использованы:

  • число (целое или вещественное);
  • литералы true (истина), false (ложь), null (отсутствие значения);
  • строка (последовательность символов, заключенная в двойные кавычки);
  • список (заключается в квадратные скобки [ ], значения разделяются запятыми). Список может быть пустым, значения в пределах одного списка могут иметь разный тип;
  • вложенный объект (неупорядоченное множество пар ключ: значение, заключённое в фигурные скобки { }). Ключ описывается строкой, между ним и значением стоит символ :. Пары ключ-значение отделяются друг от друга запятыми.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
{
   "firstName": "Тимур",
   "lastName": "Гуев",
   "age": 29,
   "gender": "мужской",
   "smoke": false,
   "address": {
       "streetAddress": "Часовая 25, кв. 127",
       "city": "Москва",
       "postalCode": 125315
   },
   "phoneNumbers": ["+7 (919) 424-84-34", "+7 (916) 928-92-34"]
}
Инфо
Вложенность данных может быть бесконечной

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

1
{"firstName": "Тимур", "lastName": "Гуев", "gender": "мужской"}
Инфо
JSON — это текстовый формат, который может быть представлен не только в виде пар ключ-значение (объектов). Он так же может содержать список, строку, число и т.д. Тем не менее, чаще всего используется структура ключ-значение. При этом, объекты являются неупорядоченными, списки же, наоборот, упорядочены

Модуль json

Для использования: import json

Преобразование переменных программы (Python-объектов) в формат для хранения называется «сериализацией», а обратное преобразование — «десериализацией». В Python для сериализации и десериализации в формат json есть одноимённый модуль.

Функция dumps()

Для сериализации данных в json строку используется функция dumps() из модуля json. Для того, чтобы сериализовать данные с ее помощью, достаточно передать в нее аргументом любой сериализуемый Python-объект.

1
2
3
4
5
6
7
8
import json

data = {'name': 'Russia', 'phone_code': 7, 'capital': 'Moscow', 'currency': 'RUB'}

json_data = json.dumps(data)            # сериализуем словарь data в json строку

print(type(json_data))
print(json_data)
1
2
3
# Вывод:
<class 'str'>
{"name": "Russia", "phone_code": 7, "capital": "Moscow", "currency": "RUB"}
Инфо
Независимо от того, какие кавычки использовались в Python-объекте, в результате данные будут в двойных кавычках (стандарт json)

Функция dump()

В отличие от функции dumps(), которая сериализует Python-объект в json строку, функция dump() записывает переданный Python-объект в файл.

1
2
3
4
5
6
import json

data = {'name': 'Russia', 'phone_code': 7, 'capital': 'Moscow', 'currency': 'RUB'}

with open('countries.json', 'w') as file:
    json.dump(data, file)
1
2
// Создаётся файл countries.json с содержимым:
{"name": "Russia", "phone_code": 7, "capital": "Moscow", "currency": "RUB"}
Инфо
Возможно сериализовать любой объект, поддерживаемый форматом json, например число, список, строку и т.д

indent, sort_keys и separators

Функции записи dumps() и dump() имеют необязательные аргументы indent, sort_keys и separators, которые можно использовать для более удобного чтения человеком.

  • Аргумент indent задает отступ от левого края. По умолчанию имеет значение None для более компактного представления без отступов. Если значением indent является строка, то она используется в качестве отступа.

  • Аргумент sort_keys задает сортировку ключей в результирующем json. По умолчанию имеет значение False для более быстрого выполнения. Если установить значение аргумента в True, то ключи будут отсортированы в алфавитном порядке, что особенно удобно, когда ключей много.

  • Аргумент separators задает кортеж, состоящий из двух элементов (item_separator, key_separator), которые представляют разделители для элементов и ключей. По умолчанию аргумент имеет значение (', ', ': ').

1
2
3
4
5
6
7
8
9
import json

data = {'name': 'Russia', 'phone_code': 7, 'capital': 'Moscow', 'currency': 'RUB'}

json_data1 = json.dumps(data)
json_data2 = json.dumps(data, indent=3, separators=(';', ' = '), sort_keys=True)

print(json_data1)
print(json_data2)
1
2
3
4
5
6
7
8
# Вывод:
{"name": "Russia", "phone_code": 7, "capital": "Moscow", "currency": "RUB"}
{
   "capital" = "Moscow";
   "currency" = "RUB";
   "name" = "Russia";
   "phone_code" = 7
}

Функция loads()

Для десериализации данных нужно использовать функцию loads(). Ее аргумент — это строка с данными в формате json.

1
2
3
4
5
6
7
import json

json_data = '{"name": "Russia", "phone_code": 7, "capital": "Moscow", "currency": "RUB"}'

data = json.loads(json_data)
print(type(data))
print(data)
1
2
3
# Вывод:
<class 'dict'>
{'name': 'Russia', 'phone_code': 7, 'capital': 'Moscow', 'currency': 'RUB'}
Опасность
В случае если строка для десериализации содержит данные с ошибкой, то модуль json не сможет правильно прочитать такую строку, и программа завершится с ошибкой json.decoder.JSONDecodeError

Функция load()

В отличие от функции loads(), которая в качестве аргумента принимает строку с данными в формате json, функция load() принимает файловый объект и возвращает его десериализованное содержимое.

Пусть файл data.json имеет следующее содержимое:

1
2
3
4
5
6
7
{
  "name": "Russia",
  "phone_code": 7,
  "capital": "Moscow",
  "cities": ["Abakan", "Almetyevsk", "Anadyr", "Anapa", "Arkhangelsk", "Astrakhan"],
  "currency": "RUB"
}

Тогда следующий код преобразует data.json в словарь data и выведет его элементы:

1
2
3
4
5
6
7
8
9
import json

with open('data.json') as file:
    data = json.load(file)                # передаем файловый объект
    for key, value in data.items():
        if type(value) == list:
            print(f'{key}: {", ".join(value)}')
        else:
            print(f'{key}: {value}')
1
2
3
4
5
6
# Вывод:
name: Russia
phone_code: 7
capital: Moscow
cities: Abakan, Almetyevsk, Anadyr, Anapa, Arkhangelsk, Astrakhan
currency: RUB

Типы данных в json

Модуль json автоматически определяет тип значения при десериализации. Такая автоматическая работа с типами данных выгодно отличает json от csv, при работе с которым таких автоматических преобразований нет.

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

Таблица конвертации типов данных Python в JSON:

PythonJSON
dictobject
list, tuplearray
strstring
int, floatnumber
Truetrue
Falsefalse
Nonenull

Таблица конвертации JSON в типы данных Python:

JSONPython
objectdict
arraylist
stringstr
number (int)int
number (real)float
trueTrue
falseFalse
nullNone

Ограничение по типам данных

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
import json

data = {
        'beegeek': 2018,
        ('Timur', 'Guev'): 29,
        ('Arthur', 'Kharisov'): 20,
        'stepik': 2013
       }

json_data = json.dumps(data, skipkeys=True)        # преобразуем dict в json

print(json_data)
1
2
# Вывод:
{"beegeek": 2018, "stepik": 2013}
Инфо
В JSON ключами словаря могут быть только строки. Но, если в словаре Python использовались числа, булевы значения или None, то ошибки не будет, вместо этого они будут преобразованы в строки.

Кириллические символы в json

Вывод следующего кода может вызвать смущение:

1
2
3
4
5
import json

data = {'firstName': 'Иван', 'lastName': 'Иванов'}
s = json.dumps(data)
print(s)
1
2
# Вывод:
{"firstName": "\u0418\u0432\u0430\u043d", "lastName": "\u0418\u0432\u0430\u043d\u043e\u0432"}

Каждая буква из строк Иван и Иванов будет заменена на её код. Эти коды стандартны, и код для каждой из букв индивидуален. Например 0438 — код буквы и. Обратное преобразование из строки в словарь вернет закодированное значение в первоначальный вид.

С помощью необязательного аргумента ensure_ascii функций dumps() и dump() можно отказаться от такого кодирования:

1
2
3
4
5
6
7
import json

data = {'firstName': 'Тимур', 'lastName': 'Гуев'}
s = json.dumps(data, ensure_ascii=False)
print(s)
result = json.loads(s)
print(result)
1
2
3
# Вывод:
{"firstName": "Тимур", "lastName": "Гуев"}
{'firstName': 'Тимур', 'lastName': 'Гуев'}

Python преобразует такую строку обратно в словарь без проблем (поскольку использует Unicode по умолчанию), но нужно помнить, что это может привести к проблемам с преобразованием в программах, написанных на других языках программирования.

Поддержать автора
NoisyCake cloudtipscloudtips
0%