Один из самых популярных форматов данных является CSV. Для работы с CSV в Python есть несколько библиотек и модулей. С помощью их вы сможете обрабатывать эти данные конвертируя их в словари и списки, создавать файлы и читать их. Основной модуль называется CSV и большинство задач разберем на его примере.
Как устроен формат CSV
Основное отличие формата CSV от обычного текста в его структуре, которая проявляется в разделителе. Именно из-за этого этот формат расшифровывается как 'comma separated values' (значения разделенные запятыми). Разделитель не всегда обязан быть в виде запятой, он может принимать и другие виды, например ';'. Разделители так же могут называться делимитер (delimiter). Пример того, как может выглядеть файл:
Имя; Пол; Возраст
Алексей; муж.; 20
Алина; жен.; 21
Первая строка может содержать название заголовки колонок, но это не обязательно. Так же видно, что разделители разделяют значения и из-за этого они не используются в конце строки.
Из-за простого формата CSV вам не обязательно импортировать модули. Вы можете использовать существующий функционал, который создаст данные в формате CSV, например так:
data = [
['Имя', 'Пол', 'Возраст'],
['Алексей', 'муж.', '20'],
['Алина', 'жен.', '21'],
]
csv = ''
for row in data:
csv += ','.join(row) + '\n'
Тем не менее в модулях реализованы дополнительные возможности по анализу таких данных. Например преобразование в словарь или определение форматов (диалекта).
Чтение CSV файлов
Как уже говорилось, основной модуль уже установлен вместе с Python. Для чтения файлов используется функция 'reader()', которая возвращает объект для итерации. Так мы можем открыть файл в большинстве случаев:
import csv
with open('file.csv', 'r') as f:
data = csv.reader(f)
for row in data:
print(row)
Для каких-то ОС может понадобится открывать файл с указанием разделителя новой строки, т.е. так:
with open('file.csv', 'r', newline='') as f:
Указание разделителя
По умолчанию считается, что вы используете запятую в качестве делимитра. В моем файле, в качестве разделителя, стоит ';', а файл содержит кириллицу (кириллица имеет значение на Windows). В этом случае файл читается следующим образом:
import csv
with open('file.csv', 'r', encoding='UTF-8') as f:
data = csv.reader(f, delimiter=';')
for row in data:
print(row)
В случае Windows по умолчанию используется кодировка 'cp1251' и, если вы не укажете 'UTF-8', то может появится ошибка:
- UnicodeDecodeError: 'charmap' codec can't decode byte 0x98 in position 1: character maps to <undefined>
Начальные пробелы
Со скриншота выше видно, что пробелы, в начале строки, не обрабатываются должным образом. Что бы убрать пробелы нужно указать параметр 'skipinitialspace':
import csv
with open('file.csv', 'r', encoding='UTF-8') as f:
data = csv.reader(f, delimiter=';', skipinitialspace=True)
for row in data:
print(row)
Кавычки
CSV файл может содержать кавычки в произвольных местах:
"Name","Age","Sex"
"Alexander Melnikov",45,"m"
"Piter Tolstoy",23,"m"
Убрать их можно используя параметр 'quoting':
import csv
with open('file.csv', 'r') as f:
data = csv.reader(f, quoting=csv.QUOTE_ALL)
for row in data:
print(row)
По умолчанию ищется двойная кавычка. Если вы хотите ее переопределить, то нужно использовать параметр 'quotechar'.
Поиска этой кавычки зависит от того, что указано в параметр 'quoting'. Так значение 'csv.QUOTE_ALL' говорит, что все значения находятся внутри кавычек. Параметр может принимать другие значения:
- csv.QUOTE_MINIMAL - используется по умолчанию. Кавычки используются в местах содержащие специальные символы (например двойная кавычка из quotechar или сам дилиметр);
- csv.QUOTE_NONNUMERIC - кавычки используются в нечисловых значениях. Если кавычек в значении не присутствует оно будет преобразовано во float;
- csv.QUOTE_NONE - кавычки, вокруг значений, не используются.
Если вы установили 'csv.QUOTE_NONE', а в файле все равно экранируются специальные символы, то можно использовать параметр 'escapechar' указывающий на символ экранирования.
Чтение с конвертацией в словарь
Если первая строка у вас содержит заголовки колонок, то вы ее можно преобразовать в ключи словаря. Что бы это сделать, вместо метода 'reader()' используется класс 'DictReader()':
import csv
with open('file.csv', 'r') as f:
data = csv.DictReader(f, delimiter=';')
for row in data:
print(row)
Тип словаря зависит от версии Python. Если вы используете версию ниже 3.8, то вернется 'OrderedDict', который можно преобразовать в обычный с 'dict()'. В версиях 3.8 и старше возвращается обычный тип словаря.
Параметры у класса аналогичны методу 'reader()'. Мы так же можем определять кавычки и символы экранирования.
Создание шаблона диалекта
Если у вас используется множество разных параметров и идет нарушение принципов DRY (dont repeat yorself), то вы можете использовать диалект (или просто шаблон). В примере мы регистрируем параметры под общим названием 'myDialect' и передаем в функцию или класс:
import csv
csv.register_dialect('myDialect',
delimiter=';',
skipinitialspace=True,
quoting=csv.QUOTE_ALL)
with open('file1.csv', 'r') as f:
data = csv.reader(f, dialect='myDialect')
for row in data:
print(row)
with open('file2.csv', 'r') as f:
data = csv.reader(f, dialect='myDialect')
for row in data:
print(row)
Автоматическое определение параметров CSV файла
Если используются разные файлы CSV и в каждом из них разные параметры для чтения, мы можем определить их автоматически с классом 'Sniffer'. Результат работы этого класса мы можем передать как диалект:
import csv
with open('file.csv', 'r') as f:
# читаем первые 100 символов (или весь файл)
sample = f.read(100)
# пример проверки, что файл имеет заголовки
header = csv.Sniffer().has_header(sample)
print('В файле есть заголовки: ', header)
# создаем диалект
dialect = csv.Sniffer().sniff(sample)
with open('file.csv', 'r') as f:
# передаем диалект и читаем файл
reader = csv.reader(f, dialect)
for row in reader:
print(row)
Метод 'has_header()' проверяет есть ли в файле заголовки. Аналогично этому методу могут быть использованы и другие методы проверяющие кавычки, делимитры и т.д.
Чтение в pandas
В библиотеке для анализа данных pandas так же есть возможность прочитать CSV файл. Эта библиотека устанавливается отдельно:
import pandas
csv = pandas.read_csv('file.csv', delimiter=';')
print(csv)
Запись данных в CSV
Для записи данных есть функция 'writer()'. В эту функцию мы можем передать все те же параметры, что в случае чтения. Базовая запись данных будет выглядеть следующим образом:
import csv
lines = [
['Alexander Melnikov', '31', 'm'],
['Mihail Tolstoy', '28', 'm'],
]
with open('file_w.csv', 'w', newline='') as f:
writer = csv.writer(f)
for line in lines:
writer.writerow(line)
# или просто
# writer.writerows(lines)
Как и понятно с примера выше:
- writerow - записывает каждый список построчно;
- writerows - записывает список списков в файл целиком.
Надобность в 'newline' так же может отличаться в разных ОС. Вы можете указать в функции 'writer' параметр 'lineterminator' со значением '\n', который обозначает символ переноса новой строки. По умолчанию он равен '\r\n':
...
with open('file_w.csv', 'w') as f:
writer = csv.writer(f, lineterminator='\n')
...
Аналогично 'lineterminator' могут передаваться следующие параметры:
- quotechar - символ для экранирования значений попадающие под условия указанные в 'quoting'. По умолчанию этот символ равен двойным кавычкам;
- quoting - какие значения должны быть экранированы: csv.QUOTE_MINIMAL (если в значении есть делимитер или сам символ экранирования), csv.QUOTE_ALL (все символы), csv.QUOTE_NONNUMERIC (оборачивает в кавычки все нечисловые значения), csv.QUOTE_NONE (не использует кавычки и, если в значениях используется символ делимитера, экранирует его в 'escapechar');
- escapechar - если поведение с кавычками не установлено, то символы разделителя в значениях экранируются в этот символ.
Пример использования:
import csv
lines = [
['Alexander, Melnikov', '31', 'm'],
['Mihail Tolstoy', '28', 'm'],
]
with open('file_w.csv', 'w', newline='') as f:
writer = csv.writer(f, escapechar='*', quoting=csv.QUOTE_NONE)
writer.writerows(lines)
Запись словаря
Аналогично чтению мы можем выполнить конвертацию словаря в CSV. Для этого есть класс 'DictWriter'. Запись словаря, от его чтения, отличается параметром 'fieldnames' в котором указываются заголовки колонок. Пример такой записи:
import csv
lines = [
{'name': 'Alexander Melnikov', 'age': 38, 'sex': 'm'},
{'name': 'Mihail Tolstoy', 'age': 28, 'sex': 'm'},
]
with open('file_w.csv', 'w', newline='') as f:
writer = csv.DictWriter(f, fieldnames=['age','sex','name'])
for line in lines:
writer.writerow(line)
# или просто
# writer.writerows(lines)
Параметры, переданные в 'DictWriter', могут быть такими же что и в 'writer'.
Запись с pandas
Что бы сохранить данные с pandas мы сначала должны выполнить конвертацию в 'DataFrame':
import pandas as pd
# словарь
lines1 = [
{'name': 'Alexander Melnikov', 'age': 38, 'sex': 'm'},
{'name': 'Mihail Tolstoy', 'age': 28, 'sex': 'm'},
]
# список
lines2 = [
['Alexander Melnikov', 38, 'm'],
['Mihail Tolstoy', 28, 'm'],
]
lines1 = pd.DataFrame(lines1)
lines2 = pd.DataFrame(lines2)
lines1.to_csv('example1.csv')
lines2.to_csv('example2.csv')
...