Язык программирования Python

Работа с файлами csv с помощью модуля csv в Python

В стандартной библиотеке у нас есть модуль для чтения файлов csv. Этот модуль не очень полезен, если мы используем numpy или pandas, но поскольку эти модули не всегда доступны, полезно на него взглянуть.

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

Давайте начнем с импорта модуля и посмотрим, что он нам выдаст:

import csv print(dir(csv))
Code language: JavaScript (javascript)

Вот как выглядит результат команды выше:

['Dialect', 'DictReader', 'DictWriter', 'Error', 'OrderedDict', 'QUOTE_ALL', 'QUOTE_MINIMAL', 'QUOTE_NONE', 'QUOTE_NONNUMERIC', 'Sniffer', 'StringIO', '_Dialect', '__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', '__version__', 'excel', 'excel_tab', 'field_size_limit', 'get_dialect', 'list_dialects', 're', 'reader', 'register_dialect', 'unix_dialect', 'unregister_dialect', 'writer']
Code language: JSON / JSON with Comments (json)

Есть два названия, которые кажутся очевидными: reader и writer. Есть и другие моменты, с которыми мы будем знакомиться постепенно.

В справке для reader и writerговорится следующее:

help(csv.reader)
Code language: CSS (css)
Help on built-in function reader in module _csv: reader(...) csv_reader = reader(iterable [, dialect='excel'] [optional keyword args]) for row in csv_reader: process(row) The "iterable" argument can be any object that returns a line of input for each iteration, such as a file object or a list. The optional "dialect" parameter is discussed below. The function also accepts optional keyword arguments which override settings provided by the dialect. The returned object is an iterator. Each iteration returns a row of the CSV file (which can span multiple input lines).
Code language: JavaScript (javascript)
help(csv.writer)
Code language: CSS (css)
Help on built-in function writer in module _csv: writer(...) csv_writer = csv.writer(fileobj [, dialect='excel'] [optional keyword args]) for row in sequence: csv_writer.writerow(row) [or] csv_writer = csv.writer(fileobj [, dialect='excel'] [optional keyword args]) csv_writer.writerows(rows) The "fileobj" argument can be any object that supports the file API.

csv.reader используется для чтения файлов в формате csv, а csv.writer – для записи. Мы видим, что для обоих вариантов есть ключевое слово dialect.

Внутри модуля определено несколько диалектов:

print(csv.list_dialects())
Code language: CSS (css)

Что мы получим:

['excel', 'excel-tab', 'unix']
Code language: JSON / JSON with Comments (json)

По умолчанию мы получаем три разных диалекта.

Что такое диалект?

Давайте посмотрим, на чем основывается dialect.

print(csv.excel) print(dir(csv.excel))
Code language: CSS (css)

Получаем:

<class 'csv.excel'> ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_name', '_valid', '_validate', 'delimiter', 'doublequote', 'escapechar', 'lineterminator', 'quotechar', 'quoting', 'skipinitialspace']
Code language: HTML, XML (xml)

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

print(f"Разделитель: {csv.excel.delimiter}") print(f"Двойные кавычки: {csv.excel.doublequote}") print(f"Символ Escape: {csv.excel.escapechar}") print(f"Знак конца строки: {repr(csv.excel.lineterminator)}") print(f"Символ кавычек: {csv.excel.quotechar}") print(f"Контроль над тем, когда создавать кавычки: {csv.excel.quoting}") print(f"Игнорирование пробела после разделителя: {csv.excel.skipinitialspace}")
Code language: PHP (php)

На экране появится что-то вроде этого:

Разделитель'\r\n' Символвол" Контроль над тем, когда создавать кавычки: 0 Игнорирование пробела после разделителя: False
Code language: PHP (php)

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

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

Давайте создадим файл в формате csv . Я делаю это с помощью IPython или Jupyter Notebook, в котором есть волшебная команда writefile, позволяющая записать текстовый файл на диск.

Вы можете сделать то же самое или использовать текстовый редактор, такой как Geany, Notepad, Notepad++, vim, …

Единственное, что вам нужно сделать, это сохранить его в том же месте, где вы выполняете код, который я покажу ниже, и с тем же именем, которое я использую, ‘data.csv’:

%%writefile data.csv "a","b","c" 1,2,3 11,22,33
Code language: JavaScript (javascript)

Для чтения вышеуказанного файла мы можем использовать такой вариант, где разделителем является запятая ‘,’:

with open("./data.csv", newline="") as csvfile: reader = csv.reader(csvfile, delimiter=",") for row in reader: print(row)
Code language: JavaScript (javascript)

На экране появится следующее:

['a', 'b', 'c'] ['1', '2', '3'] ['11', '22', '33']
Code language: JSON / JSON with Comments (json)

Вы можете видеть, что значения считываются как строки, даже несмотря на наличие чисел, потому что по умолчанию он не выполняет никакого преобразования данных. Эта функция принимает ряд параметров, аналогичных тем, что мы имеем в диалекте. В диалекте, как мы видели выше, мы можем определить delimiter, doublequote, … И мы можем использовать их в качестве аргументов для csv.reader. Значение по умолчанию для quoting – csv.QUOTE_MINIMAL.

Если мы хотим, чтобы значения, которые являются числами, были преобразованы в числовые значения после того, как мы их прочитаем, мы можем использовать аргумент quoting и передать ему параметр csv.QUOTE_NONNUMERIC, который преобразует в float все, что не заключено в кавычки:

with open("./data.csv", newline="") as csvfile: reader = csv.reader(csvfile, delimiter=",", quoting=csv.QUOTE_NONNUMERIC) for row in reader: print(row)
Code language: JavaScript (javascript)

На экране появится следующее:

['a', 'b', 'c'] [1.0, 2.0, 3.0] [11.0, 22.0, 33.0]
Code language: JSON / JSON with Comments (json)

Если я создам новый файл csv (обратите внимание, он называется data_.csv) с одной из букв без кавычек (обратите внимание на первую “a”) и использую приведенный выше код, он сообщит мне, что не может его прочитать:

%%writefile data_.csv a,"b","c" 1,2,3 11,22,33
Code language: JavaScript (javascript)

Читаю его с помощью следующего кода:

with open("./data_.csv", newline="") as csvfile: reader = csv.reader(csvfile, delimiter=",", quoting=csv.QUOTE_NONNUMERIC) for row in reader: print(row)
Code language: JavaScript (javascript)

Это выдаст ошибку:

--------------------------------------------------------------------------- ValueError Traceback (most recent call last) <ipython-input-11-81b6f7ce2940> in <module> 1 with open("./data_.csv", newline="") as csvfile: 2 reader = csv.reader(csvfile, delimiter=",", quoting=csv.QUOTE_NONNUMERIC) ----> 3 for row in reader: 4 print(row) ValueError: could not convert string to float: 'a'
Code language: JavaScript (javascript)

Если я теперь использую csv.QUOTE_NONE, он закавычит все и сохранит исходные кавычки (здесь я снова использую файл data.csv).

with open("./data.csv", newline="") as csvfile: reader = csv.reader(csvfile, delimiter=",", quoting=csv.QUOTE_NONE) for row in reader: print(row)
Code language: JavaScript (javascript)

На экране появится следующее:

['"a"', '"b"', '"c"'] ['1', '2', '3'] ['11', '22', '33']
Code language: JSON / JSON with Comments (json)

Вместо reader мы можем использовать DictReader, который позволяет нам читать строки как словарь, используя заголовок в качестве ключа:

with open("./data.csv", newline="") as csvfile: reader = csv.DictReader(csvfile, delimiter=",", quoting=csv.QUOTE_NONNUMERIC) for row in reader: print(row) print(row['a'])
Code language: PHP (php)

Это отобразится на экране:

OrderedDict([('a', 1.0), ('b', 2.0), ('c', 3.0)]) 1.0 OrderedDict([('a', 11.0), ('b', 22.0), ('c', 33.0)]) 11.0
Code language: CSS (css)

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

class MyDialect(csv.Dialect): delimiter = "|" quoting = csv.QUOTE_NONE quotechar = '"' lineterminator = "\n" my_dialect = MyDialect()

Мы будем использовать только что созданный диалект.

Прочитаем файл csv, созданный нами ранее, и собираемся сохранить его, используя наш новый диалект:

with open("./data.csv", newline="") as csvin, open("./data_new.csv", "w") as csvout: reader = csv.DictReader(csvin, delimiter=",", quoting=csv.QUOTE_NONNUMERIC) writer = csv.writer(csvout, dialect=my_dialect) writer.writerow(reader.fieldnames) for row in reader: writer.writerow(row.values())
Code language: JavaScript (javascript)

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

a|b|c 1.0|2.0|3.0 11.0|22.0|33.0

Внутри модуля csv есть очень интересная утилита под названием Sniffer. Представим, что мы получаем файлы, которые могут иметь различные разделители (например, запятые или точки с запятой,…), но при этом мы априори не знаем, какого типа они будут, будет ли у них заголовок,… Sniffer может помочь нам в этом. Давайте посмотрим, как.

snf = csv.Sniffer()

Прочитаем файл, который мы создали.

with open("./data_new.csv", "r") as f: sample = "".join(f.readlines()) print(f"Есть ли заголовки? {snf.has_header(sample)}") dialecto = snf.sniff(sample) print(f"Разделителем является: {dialecto.delimiter}")
Code language: PHP (php)

На экране появится следующее:

Есть ли заголовки? True Разделителем является: |

Второй пример:

sample = """1,2,3 11,22,33 111,222,333 """ print(f"Есть ли заголовки? {snf.has_header(sample)}") dialecto = snf.sniff(sample) print(f"Разделителем является: {dialecto.delimiter}")
Code language: PHP (php)

Вот что он покажет:

Есть ли заголовки? False Разделителем является: ,

Таким образом, мы смогли убедиться, что то, что мы получим, выглядит так, как будто у него нет заголовков, и похоже, что разделитель является ,. Конечно, это не всегда безошибочно, но это лучше, чем ничего.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *