Является ли CSV текстовым форматом?

В учебнике видел рекомендацию открывать CSV файл как текстовый:

with open('example.csv', 'r') as csvfile:
    reader = csv.reader(csvfile)
    ...

С другой стороны документация требует открывать CSV-файл без трансляции переводов строки, что делает его не совсем текстовым:

with open('example.csv', 'r', newline='') as csvfile:
    reader = csv.reader(csvfile)
    ...

Кто прав?


Ответы (5 шт):

Автор решения: Stanislav Volodarskiy

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

Пример:

import csv

source = [
    ['10', 'line 1\nline 1'],
    ['20', 'line 2\r\nline 2']
]

with open('temp.csv', 'w', newline='') as csvfile:
    writer = csv.writer(csvfile)
    for row in source:
        writer.writerow(row)

with open('temp.csv', 'r', newline='') as csvfile:
    reader = csv.reader(csvfile)
    target1 = []
    for row in reader:
        target1.append(row)

with open('temp.csv', 'r') as csvfile:
    reader = csv.reader(csvfile)
    target2 = []
    for row in reader:
        target2.append(row)


print('target1')
if source == target1:
    print('OK')
else:
    for s_row, t_row in zip(source, target1):
        if s_row != t_row:
            print(s_row, '!=', t_row)

print('target2')
if source == target2:
    print('OK')
else:
    for s_row, t_row in zip(source, target2):
        if s_row != t_row:
            print(s_row, '!=', t_row)
$ python csv-format.py
target1
OK
target2
['20', 'line 2\r\nline 2'] != ['20', 'line 2\nline 2']

Этот пример записывает файл temp.csv в текущий каталог и читает его обратно двумя способами. Первый раз файл читается как двоичный, без трансляции переводов строк. Второй раз файл читается как текстовый. И данные после чтения отличаются от тех, что были записаны.

А именно в строки встроены переводы строки в стиле Linux и в стиле Windows. Соответственно на Windows будет искажена первая запись, на Linux будет искажена вторая запись. Причина в том что CSV формат записывает переводы строк в файл ни как их не экранируя:

$ cat temp.csv
10,"line 1
line 1"
20,"line 2
line 2"

$ od -c temp.csv
0000000   1   0   ,   "   l   i   n   e       1  \n   l   i   n   e    
0000020   1   "  \r  \n   2   0   ,   "   l   i   n   e       2  \r  \n
0000040   l   i   n   e       2   "  \r  \n
0000051

P.S. Кстати, файл я писал на Linux, а переводы строки между записями в стиле Windows (\r\n). Это вот прямо совсем не текстовый формат. Но очень похож.

→ Ссылка
Автор решения: Fox Fox

Является текстовым. Откройте его и прочтите содержимое. Оно состоит из тех символов, которые характерны для обычных текстовых файлов. Не стоит путать структуру текстового файла и его тип как таковой. Открытие файла в режиме чтения не искажает содержимое файла, потому что никакой записи не производится. В документации, которая в условии примера, НИЧЕГО о порче файла при открытии его в режиме чтения нет. Там о своём, сокровенном.

Далее. Открыть файл - не то же, что прочесть содержимое и что-то с ним делать дальше. В ответах приводятся примеры, но они некорректны. Данные могут быть изменены в дальнейшем, а НЕ ПРИ ОТКРЫТИИ. Можете сколько угодно открывать, копировать в космос содержимое и тут же закрывать текстовый файл со структурой csv в режиме чтения - ничего не произойдёт. И в других языках программирования (не только в Питон) то же самое.

В Питоне функция open имеет только два режима открытия: текстовый и бинарный. И правильно. Так оно и есть.

P.S. Вот ответ ИИ на всякий случай. Можете, конечно, игнорировать его, но этот ответ не взят с потолка:

Да, текстовый файл — это файл, содержащий текстовые данные, которые можно прочитать и интерпретировать как текст. Он состоит из последовательности символов, организованных в строки, и может быть открыт и прочитан любым текстовым редактором. Примеры текстовых файлов включают файлы с расширениями .txt, .csv, .html и .xml. Текстовые файлы обычно используют кодировку символов, такую как ASCII или UTF-8, чтобы представлять текстовые данные. Важно отметить, что текстовые файлы не содержат форматирования, как это делают файлы, созданные в текстовых процессорах (например, .docx или .odt).

В этом примере никаких изменений файла не произойдёт:

import csv

# Открываем CSV-файл в режиме чтения
with open('example.csv', 'r', newline='') as csvfile:
    reader = csv.reader(csvfile)
    
    # Копируем содержимое файла в переменную
    data = [row for row in reader]

# Теперь содержимое файла находится в переменной data
print(data)

Как именно считает модуль csv содержимое, коряво или нет, никакого отношения к типу файла, к способу его открытия НЕ ИМЕЕТ. Он всё равно текстовый, со структурой (или формата) csv.

Вот пример прямо из моего проекта. Читается текстовый файл csv с 3.5 млн. записей, которые заполняют собой базу данных. Хоть бы одна ошибка за всё время. Когда руки-не-крюки, всё читается и пишется.

with open(v_csv, "r", encoding = "cp1251") as h:
 v_reader = csv.reader(h)
 next(v_reader)
 for v_row in v_reader:
  if len(v_row) == 2:
   v_cursor.execute("insert into rhymes (word, rhyme) values (?, ?)", (v_row[0], v_row[1]))
v_connection.commit()
→ Ссылка
Автор решения: Qwertiy

Как уже сказано в соседнем ответе (точнее, в комментарии к нему), переводы строк между записями в csv могут быть любыми. А вот переводы строк внутри записей могут испортиться при чтении в текстовом режиме.

Тем не менее, на мой взгляд вопрос не настолько простой.

Как правило, в csv всё же помещают некоторые таблицы, которые предполагаются человекочитаемыми, а не произвольные бинарные данные, что в теории тоже возможно.

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

Если же помимо человекочитаемых данных там ожидается что-то бинарное, например, в одной из колонок лежит фото пользователя, то надо читать файл как бинарный, а текстовые данные в нём (при необходимости) обрабатывать отдельно. Иначе получится как минимум вот так. А может даже что-то более изощрённое.

→ Ссылка
Автор решения: Akina

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

RFC 4180 Common Format and MIME Type for Comma-Separated Values (CSV) Files

This RFC documents the format of comma separated values (CSV) files and formally registers the "text/csv" MIME type for CSV in accordance with RFC 2048

Данный документ совершенно однозначно интерпретирует формат CSV как текстовый.

Также этот документ однозначно и безальтернативно устанавливает, что в качестве разделителя строк должна использоваться комбинация символов CR+LF (она же \r\n).

Формально - всё, что не соответствует описанию данного формата в RFC, не является CSV, даже если называется "файл CSV".

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

Все остальные форматы данного семейства (общее название - DSV, Delimiter-Separated Values) лучше всего обрабатывать аналогичным порядком.

→ Ссылка
Автор решения: rotabor

Признаком текстового файла является то, что содержимое файла декодируется в значимую информацию посредством таблицы кодов ASCII (или Unicode).

Например, если это чистый ASCII, то где бы ни встретился код 0xA, он всегда означает перевод строки.

Соответственно, если файл не текстовый, то он двоичный. Понятно, что двоичный файл может содержать текстовые фрагменты. Можно добавить промежуточную категорию - смешанный, но это не меняет сути.

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

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

→ Ссылка