Python — наиболее эффективно перезаписывать определенную строку в файле CSV.

Учитывая следующий файл csv:

01;blue;brown;black
02;glass;rock;paper
03;pigeon;squirel;shark

Моя цель - заменить (уникальную) строку, содержащую «02» в 1-й позиции.

Я написал этот фрагмент кода:

with open("csv", 'r+', newline='', encoding='utf-8') as csvfile, open('csvout', 'w', newline='', encoding='utf-8') as out:
    reader = csv.reader(csvfile, delimiter=';')
    writer = csv.writer(out, delimiter=';')
    for row in reader:
        if row[0] != '02':
            writer.writerow(row)
        else:
            writer.writerow(['02', 'A', 'B', 'C'])

Но переписывание всего CSV в другом виде не кажется самым эффективным способом, особенно для больших файлов:

  1. Как только совпадение найдено, мы продолжаем читать до конца.
  2. Мы должны переписать каждую строку одну за другой.
  3. Запись второго файла не очень практична и неэффективна для хранения.

Я написал второй фрагмент кода, который, кажется, отвечает на эти две проблемы:

with open("csv", 'r+', newline='', encoding='utf-8') as csvfile:
    content = csvfile.readlines()
    for index, row in enumerate(content):
        row = row.split(';')
        if row[2] == 'rock':
            tochange = index
            break
    content.pop(tochange)
    content.insert(tochange, '02;A;B;C\n')
    content = "".join(content)
    csvfile.seek(0)
    csvfile.truncate(0)     # Erase content
    csvfile.write(content)

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

РЕДАКТИРОВАНИЕ: количество символов в строке может варьироваться.

EDIT 2: я, по-видимому, обязан прочитать и переписать все, если я не хочу использовать отступы. Возможным решением было бы решение, подобное базе данных, я рассмотрю его в будущем.

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


person SecT0uch    schedule 28.02.2019    source источник
comment
Отсортирован ли файл CSV по этому первому столбцу? Если это так, вы можете выполнить двоичный поиск и перезаписать эту конкретную строку в O(logn), но вы должны перезаписать ее с помощью одинаковое количество символов.   -  person tobias_k    schedule 28.02.2019
comment
Кстати, во втором коде вы все еще читаете и записываете весь файл.   -  person tobias_k    schedule 28.02.2019
comment
Мой CSV фактически отсортирован по 4-му столбцу, начиная с 00000001 из второй строки (первая строка для описания столбца). Количество символов может варьироваться.   -  person SecT0uch    schedule 28.02.2019
comment
Что ж, вы все равно можете выполнить линейный поиск этой строки, вернуться к началу строки с помощью seek, а затем перезаписать только эту строку, дополнив ее, например, пространство, чтобы получить старую длину, если старая строка не слишком короткая, тогда вам также придется перезаписать все следующие строки в файле (пока вы не найдете другую строку, которая была дополнена ранее, и вы можете удалить это дополнение).   -  person tobias_k    schedule 28.02.2019
comment
Это действительно было бы ответом на проблему, но я хотел бы избежать использования заполнения.   -  person SecT0uch    schedule 28.02.2019
comment
О вашем последующем вопросе: правильная база данных действительно может быть лучше. Определенно быстрее для получения/редактирования/обновления/удаления отдельных записей.   -  person tobias_k    schedule 28.02.2019


Ответы (1)


Поскольку символ в строке может различаться, мне нужно либо прочитать/записать весь файл, либо; как сказал @tobias_k, используйте seek(), чтобы вернуться к началу строки и:

  • Если строка короче, напишите только строку и дополните пробелами;
  • Если одинаковая длина, напишите только строку;
  • Если он длиннее, перепишите эту строку и следующую.

Я хочу избежать использования заполнения, поэтому я использовал time.perf_counter() для измерения времени выполнения обоих кодов, и второе решение оказалось (почти в 2*) быстрее (CSV из 10 000 строк, совпадение на 6000-м).

Одним из вариантов может быть переход на реляционную базу данных.

person SecT0uch    schedule 28.02.2019