Как можно избежать одновременного считывания 2 × 12 ГБ в память, но при этом обрабатывать все данные?
Загружая эти 24 ГБ по частям и отбрасывая данные, которые вам больше не нужны, так как вы go. Поскольку ваши файлы основаны на строках, чтение и обработка построчно кажется разумным. Наличие 4000-и sh символов в памяти одновременно не должно создавать проблем на современных персональных компьютерах.
Объединение файлов
Требуется упорядочить конечный результат (или, возможно, даже отсортировать) по содержанию строки A.txt
. Чтобы не потерять связь между строками в A.txt
и B.txt
при изменении их порядка, нам нужно сначала объединить их содержимое.
Сделать это путем
- , открыв оба файла пока не прочитав их
- открывая новый файл
AB.txt
для записи - , повторяя следующее до тех пор, пока все
A.txt
и B.txt
не будут обработаны: - чтение a строка
A.txt
- , читающая строку
B.txt
- , добавьте объединенное содержимое в качестве новой строки к
AB.txt
- , чтобы отменить то, что мы прочитали до сих пор. из памяти
Если вы знаете, что определенный символ (скажем, '\t'
) не может встречаться в A.txt
, вы можете использовать его в качестве разделителя:
with \
open("A.txt") as a_file, \
open("B.txt") as b_file, \
open("AB.txt", "w") as ab_file:
for a_line, b_line in zip(a_file, b_file):
# get rid of the line endings, whatever they are
a_line, = a_line.splitlines()
b_line, = b_line.splitlines()
# output the combined content to AB.txt
print(f"{a_line}\t{b_line}", file=ab_file)
(Обратите внимание, что это zip
ведет себя "лениво" и возвращает генератор, а не читает файлы полностью и возвращает огромный список, как это было бы в Python 2.)
Если все строки в A.txt
имеют одинаковую фиксированную длину, вам вообще не нужен разделитель. (Для сохранения вашего здравомыслия во время отладки вы все равно можете использовать его.) Если вы не знаете ни одного персонажа, который не может появиться в A.txt
, вы можете создать csv.writer
и используйте его writerow
метод , чтобы записать строки в AB.txt
. Он позаботится о необходимом экранировании или цитировании для вас.
Вы можете задаться вопросом, где шаг
отбрасывает то, что мы прочли из памяти
реализовано. Это происходит здесь неявно, потому что единственные переменные, которые содержат данные из файлов, a_line
и b_line
перезаписываются для каждой итерации.
Упорядочение объединенного файла
Для упорядочения всего файла мы должны полностью загрузить его в память, верно?
Нет. Ну, на самом деле да, но, опять же, не все сразу. Мы можем использовать внешнюю сортировку . Вы можете попробовать реализовать это самостоятельно в Python, или вы можете просто использовать UNIX инструмент командной строки sort
( справочная страница ), который делает именно это. В системе Linux или macOS она уже доступна. На Windows он должен быть включен в любой эмулятор UNIX, такой как Cygwin или MinGW. Если у вас уже установлена Git с установщиком Git по умолчанию для Windows, вы можете использовать UNIX sort
из включенного «Git Bash».
Обратите внимание, что из-за По порядку нашего содержимого в каждой строке файл будет отсортирован сначала по содержимому, полученному с A.txt
, а затем (если он такой же) по содержимому, полученному с B.txt
.
Подсчет
После того, как у вас есть отсортированный комбинированный файл, вы можете снова обрабатывать его построчно, но вы должны хранить некоторые данные между строками.
Что мы хочу сделать это:
- Для каждого блока последующих строк с одинаковым A-содержимым:
- в пределах этого, для каждого блока последующих строк с таким же B-содержимым:
- считать его строки
- следить за тем, у какого B-контента, который еще просматривался (в блоке A-контента), было наибольшее количество строк
- в конце блока A-контента: вывести строку с A-контентом и наиболее частым B-контентом для этого A-контента
Поскольку мы можем положиться на порядок, который мы установили выше, это приведет к желаемому результату.
Примерно так должно работать:
- читать строку
- разделить его на A-контент и B-контент
- , если A-контент такой же, как в предыдущей строке *:
- если B-контент такой же, как в предыдущей строке *:
- увеличить счетчик для текущей комбинации ab-контента
- else (т. Е. если B-контент отличается от предыдущей строки):
- запоминает, какой B-контент наиболее часто встречается на данный момент, и его подсчет
- (это либо наиболее часто встречающийся ранее B-контент или тот, что в предыдущей строке)
- сбросить счетчик для текущей комбинации ab-content
- увеличить этот счетчик на единицу (для текущей строки)
- где-то хранит B-контент, так что мы можем сравнить его с содержимым следующей строки в следующей итерации
- else (т. Е. Если A -content отличается от предыдущей строки) **:
- выводит A-контент предыдущей строки и наиболее заметный B-контент из предыдущей строки
- сбрасывает счетчик для текущая комбинация ab-content
- сбросить информацию о том, что наиболее часто встречается в B-контенте t и его подсчет
- хранят A-контент и B-контент текущей строки, поэтому их можно сравнить с данными следующей строки в следующей итерации
- повторять до тех пор, пока весь файл не будет обработан
* для первой строки, это неявно false
** также сделайте это, когда вы достигнете конца файл
На самом деле реализация этого в Python оставлена как упражнение для читателя. Обратите внимание, что вам нужно определить некоторые из используемых переменных перед шагом, на котором они упоминаются в приведенном выше описании, чтобы они имели правильную область действия.
Обратите внимание, что вы также можете сделать шаг подсчета более умным чем описано здесь, используя возможности всеобъемлющей стандартной библиотеки Python. См. Ответ переполнения кучи для хорошего примера.