Страницы

Поиск по вопросам

понедельник, 30 декабря 2019 г.

заменить строки в одном текстовом файле информацией из второго

#python #bash #perl #cmd #shell


Именются 2 гигантских текстовых файла.

file1.txt (3,6 Гб) содержит только одну колонку (в том числе много дубликатов):

123456
123456
123456
абвгд
абвгд
01щенок
01щенок
01щенок
01щенок
01щенок
a0125uß
a0125uß


file2.txt (1,5 ГБ) содержит ту же колонку, но без дубликатов плюс вторую колонку.

123456:artur
абвгд:sergey
01щенок:max
a0125uß:stasik


Задача: сравнить первые колонки в обеих  файлах и заменить одинаковые строки в первом
файле, строками из второй колонки второго файла, чтобы получилось следующее (дубликаты
в первом файле должны остаться):

artur
artur
artur
sergey
sergey
max
max
max
max
stasik
stasik


У меня есть такой код:

import io
STRFILE1 = 'file1.txt'
STRFILE2 = 'file2.txt'
STRFILERESULT = 'result.txt'
fIN = open(STRFILE1,'r')
strContent = fIN.read()
fIN.close()

with open(STRFILE2,'r') as f:
    for line in f:
        mapping = line.split(":",1)
        strContent = strContent.replace(mapping[0],mapping[1].rstrip("\n"))

fOUT = open(STRFILERESULT,'w')
fOUT.write(strContent)
fOUT.close()


но он работает вечно с таким объёмом строк (102.600.000 - файл 1 и 50.000.000 - файл
2). Как можно ускоритъ процесс обработки?
    


Ответы

Ответ 1



Если на python, то можно так: def story_key(): with open('te2', 'r') as f2: my_key = {i.split(':')[0]: i.split(':')[1].strip() for i in f2} return my_key all_key = story_key() def read_small(f_object, f_size=1024): while True: data = f_object.read(f_size) if not data: break yield data def f_write(): with open('te1') as f1: with open('te3', 'a') as f3: for i in read_small(f1): a = [all_key[j] + '\n' for j in i.split('\n') if j] f3.write(''.join(a)) f_write()

Ответ 2



Как можно ускоритъ процесс обработки? например, можно воспользоваться не языком программирования, а самыми обыкновенными gnu-утилитами join, sort, cut, nl. с большой долей вероятности это будет работать быстрее, нежели манипуляции со «словарями», или, тем более, базой данных (время на создание индекса, скорее всего, значительно превысит время, за которое отработают предлагаемые утилиты). если порядок строк не важен, то замена будет произведена довольно быстро: $ join -t : -o 2.2 <(sort файл1) <(sort файл2) max max max max max artur artur artur stasik stasik sergey sergey если порядок важен, вычислений понадобится значительно больше. что-нибудь в этом духе: $ join -t : -1 2 -o 1.1,2.2 <(nl -n rz -s ':' файл1 | sort -t ':' -k 2) <(sort файл2) | sort -n | cut -d : -f 2 artur artur artur sergey sergey max max max max max stasik stasik

Ответ 3



Ваш скрипт медленный потому что вы для каждого уникального значения создаёте 3.6GB строчку заново. Предполагая что все дубликаты подряд идут и порядок уникальных значений одинаковый в обоих файлах, можно читать файл с заменами и писать их в выходной файл столько раз, сколько текущий ключ повторяется в файле с дубликатами: #!/usr/bin/env python from itertools import groupby with open('file1.txt', 'rb') as dups_file, \ open('file2.txt', 'rb') as replacements_file, \ open('result.txt', 'wb') as output_file: groups = groupby(dups_file) for line in replacements_file: key, value = line.split(b':') dupe_key, dupes = next(groups) assert key == dupe_key.rstrip(), (key, dupe_key) for _ in dupes: output_file.write(value) Чтобы сделать код более устойчивым к неправильному вводу, в зависимости от вашей ситуации, добавьте try/except и пропускайте соответствующие строки. Для ожидаемого ввода, код как есть работает. Этот код читает по одной строчке из каждого файла за раз (минимально памяти требует O(1)). Использование 'b' режима предполагает, что текст в файлах закодирован используя одинаковые кодировки.

Ответ 4



Я не зря задавал вопрос про упорядоченность повторяющихся данных. Эту мысль подхватил @jfs и прислал практически оптимальное решение с учетом регулярности данных. Добавлю только, что основное время уходит на обработку строки: strContent = strContent.replace(mapping[0],mapping[1].rstrip("\n")) которую вы постоянно держите "в памяти". Чтобы было понятно - сколько примерно времени уходит на "ворочанье" в памяти strContent приведу близкий по смыслу код. Первый пример - почти как у вас - "все в одно": # -*- coding: utf-8 -*- from time import time fw = open('fileDict.txt',"w", buffering=4096, encoding='utf-8') out_text = '' prefix = "key" t_start = time() for i in range(10000000): out_text += prefix+str(i)+":" + str(i)+ '\n' fw.write(out_text) t_end = time() fw.close() print('work_time=', t_end - t_start ) # results: # without buffering in line #4 # 1 000 000 time 27.8 sec # 10 000 000 time 3403.8 sec # with buffering in line #4 # 1 000 000 time 26.8 sec # 10 000 000 time 3350.8 sec второй вариант - запхнем кусочками в список: from time import time fw = open('fileDict.txt',"w",encoding='utf-8') out_text = '' prefix = "key" out_list = [] t_start = time() for i in range(10000000): out_list.append( prefix+str(i)+":" + str(i)+ '\n' ) t_middle = time() for line in out_list: fw.write(line) t_end = time() fw.close() print('work_time=', t_end - t_start, "parsing time:", t_middle-t_start, \ " + writing_time: ", t_end - t_middle ) # results: # 1 000 000 time 2.2 sec # 10 000 000 time 21.8 sec печать для 1 000 000: work_time= 2.2221806049346924 parsing time: 1.2595593929290771 + writing_time: 0.9626212120056152 печать для 10 000 000: work_time= 21.796194791793823 parsing time: 12.18700361251831 + writing_time: 9.609191179275513 Из чего следует, что представление обрабатываемых данных вашим программистом было выбрано не лучшим образом. Действительно разница в обработке как 3340 к 12 ... на построении данных для файла в 10 млн строк размером почти 200Мб

Комментариев нет:

Отправить комментарий