Как я могу изменить огромный файл в CSV в Python - PullRequest
2 голосов
/ 23 февраля 2009

Я новичок в питоне. У меня есть огромный текстовый файл (сотни ГБ), и я хочу преобразовать файл в CSV-файл. В моем текстовом файле я знаю, что разделителем строк является строка "<> <> <> <> <> <> <>". Если строка содержит эту строку, я хочу заменить ее на ". Есть ли способ сделать это без необходимости читать старый файл и переписывать новый файл.

Обычно я думал, что мне нужно сделать что-то вроде этого:

fin = open("input", "r")
fout = open("outpout", "w")
line = f.readline
while line != "":
   if line.contains("<><><><><><><>"):
      fout.writeline("\"")
   else:
      fout.writeline(line)
   line = f.readline

но копирование сотен ГБ расточительно. Также я не знаю, будет ли open использовать много памяти (обрабатывает ли он обработчик файлов как поток?)

Любая помощь очень ценится.

Примечание: пример файла будет

file.txt
<><><><><><><>
abcdefeghsduai 
asdjliwa
1231214 ""
<><><><><><><>

будет одна строка и один столбец в CSV.

Ответы [ 8 ]

5 голосов
/ 23 февраля 2009

@ ричард-Levasseur

Я согласен, sed похоже на правильный путь. Вот черновик того, что описывает ОП:

 sed -i -e's/<><><><><><><>/"/g' foo.txt 

Это сделает замену на месте в существующем foo.txt. По этой причине я рекомендую иметь оригинальный файл под каким-то контролем версий; любая DVCS должна отвечать всем требованиям.

4 голосов
/ 23 февраля 2009

Да, open () обрабатывает файл как поток, как и readline (). Это будет только читать следующую строку. Однако, если вы вызовете read (), он прочитает все в память.

Ваш пример кода выглядит нормально на первый взгляд. Почти каждое решение потребует от вас скопировать файл в другое место. Не так просто изменить содержимое файла без замены 1: 1.

Возможно, быстрее использовать некоторые стандартные утилиты Unix (скорее всего, awk и sed), но мне не хватает unix и bash-fu, необходимых для полного решения.

1 голос
/ 24 февраля 2009

@ Констатин предполагает, что если вы будете удовлетворены заменой

'<><><><><><><>\n'
на
'"             \n'
тогда строка замены будет той же длины, и в этом случае вы можете создать решение для редактирования на месте с помощью mmap. Вам понадобится Python 2.6. Важно, чтобы файл открывался в правильном режиме!

import mmap, os
CHUNK = 2**20

oldStr = ''
newStr = '"             '
strLen = len(oldStr)
assert strLen==len(newStr)

f = open("myfilename", "r+")
size = os.fstat(f.fileno()).st_size

for offset in range(0,size,CHUNK):
    map = mmap.mmap(f.fileno(),
                    length=min(CHUNK+strLen,size-offset),  # not beyond EOF
                    offset=offset)
    index = 0  # start at beginning
    while 1:
        index = map.find(oldStr,index) # find next match
        if index == -1:  # no more matches in this map
            break
        map[index:index+strLen] = newStr

f.close()

Этот код не отлажен! Он работает для меня в тестовом случае 3 МБ, но он может не работать на большом (> 2 ГБ) файле - модуль mmap все еще кажется немного незрелым, поэтому я бы не стал полагаться на него слишком много.

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

1 голос
/ 23 февраля 2009

С питоном вам придется создавать новый файл для безопасности, это вызовет гораздо меньше головной боли, чем попытка записи на месте.

Приведенный ниже список читает вашу входную строку по 1 за раз и буферизует столбцы (из того, что я понял, ваш тестовый входной файл был 1 строкой), а затем, как только будет достигнут конец разделителя строк, он запишет этот буфер на диск, очистка вручную каждые 1000 строк исходного файла. Это также сэкономит некоторый ввод-вывод вместо записи каждого сегмента, 1000 записей по 32 байта каждая будут быстрее, чем 4000 записей по 8 байтов.

fin = open(input_fn, "rb")
fout = open(output_fn, "wb")
row_delim = "<><><><><><><>"
write_buffer = []

for i, line in enumerate(fin):
    if not i % 1000:
        fout.flush()
    if row_delim in line and i:
        fout.write('"%s"\r\n'%'","'.join(write_buffer))
        write_buffer = []
    else:
        write_buffer.append(line.strip())

Надеюсь, это поможет.

РЕДАКТИРОВАТЬ: Забыл упомянуть, хотя использование .readline () - неплохая вещь, не используйте .readlines (), который переходит и считывает все содержимое файла в список, содержащий каждую строку, что невероятно неэффективно. Использование встроенного итератора, который поставляется с файловым объектом, обеспечивает наилучшее использование памяти и скорость.

1 голос
/ 23 февраля 2009

Чтение строк просто выполняется с помощью файлового итератора :

for line in fin:
       if line.contains("<><><><><><><>"):
           fout.writeline("\"")

Также рассмотрим объект записи CSV для записи файлов CSV, например:

import csv
writer = csv.writer(open("some.csv", "wb"))
writer.writerows(someiterable)
1 голос
/ 23 февраля 2009

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

Это не трата памяти, потому что обработчик файлов - это поток.

0 голосов
/ 23 февраля 2009

Если вы разделяете поля двойными кавычками, похоже, вам нужно избегать двойных кавычек, встречающихся в ваших элементах (например, 1231214 "" должно быть \n1231214 \"\").

Что-то вроде

fin = open("input", "r")
fout = open("output", "w")
for line in fin:
   if line.contains("<><><><><><><>"):
      fout.writeline("\"")
   else:
      fout.writeline(line.replace('"',r'\"')
fin.close()
fout.close()
0 голосов
/ 23 февраля 2009

[Для проблемы в точности как заявлено] Нет способа сделать это без копирования данных, на python или любом другом языке. Если ваша обработка всегда заменяла подстроки новыми подстроками равной длины , возможно, вы могли бы сделать это на месте. Но всякий раз, когда вы заменяете <><><><><><><> на ", вы меняете положение всех последующих символов в файле. Копирование из одного места в другое - единственный способ справиться с этим.

EDIT:

Обратите внимание, что использование sed на самом деле не спасет копирование ... sed также не редактирует на месте. Из руководства GNU sed :

-i [СУФФИКС]
--in место [= СУФФИКС]
Эта опция указывает, что файлы должны редактироваться на месте. GNU sed делает это путем , создавая временный файл и отправляя вывод в этот файл, а не в стандартный вывод.

(выделено мое.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...