Огромный (до 2 ГиБ) мой текстовый файл содержит около 100 точных дубликатов каждой строки в нем (в моем случае это бесполезно, поскольку файл представляет собой таблицу данных, похожую на CSV).
Что мне нужно, так это удалить все повторения, при этом (желательно, но этим можно пожертвовать ради значительного повышения производительности), сохраняя первоначальный порядок последовательности. В результате каждая строка должна быть уникальной. Если было 100 одинаковых строк (обычно дубликаты распределяются по файлу и не будут соседями), остается только один из них.
Я написал программу на Scala (рассмотрим Java, если вы не знаете о Scala), чтобы реализовать это. Но, может быть, есть более быстрые собственные инструменты, написанные на C, способные сделать это быстрее?
ОБНОВЛЕНИЕ: awk '!seen[$0]++' filename
решение, казалось, работало очень хорошо для меня, пока файлы были около 2 ГБ или меньше, но теперь, когда я собираюсь очистить файл 8 ГБ, оно больше не работает. Кажется, что бесконечность на Mac с 4 ГБ ОЗУ и 64-битном ПК с Windows 7 с 4 ГБ ОЗУ и подкачкой 6 ГБ просто не хватает памяти. И я не испытываю энтузиазма по поводу того, чтобы попробовать это на Linux с 4 ГБ RAM, учитывая этот опыт.
sort -u
вероятно, будет быстрее.Ответы:
awk
Решение видно на #bash (Freenode):источник
awk
версия, использующая 2 поиска в массивах (показано в расширенном объяснении в ответе Жиля): 0m36.132s против 0m49.958s .. для 50 миллионов строк ... Я думал, что узким местом будет ввод / вывод, но дополнительный поиск в массиве ... 1 миллион элементов в массиве, кажется, делает довольно существенную вмятину ...Существует простой (не сказать очевидный) метод, использующий стандартные утилиты, который не требует большого объема памяти, кроме как для запуска
sort
, который в большинстве реализаций имеет специфические оптимизации для больших файлов (хороший алгоритм внешней сортировки). Преимущество этого метода в том, что он зацикливается только на всех строках внутри специальных утилит, а не внутри интерпретируемых языков.Если все строки начинаются с непробельного символа, вы можете обойтись без некоторых параметров:
Для большого количества дублирования метод, который требует только сохранения одной копии каждой строки в памяти, будет работать лучше. С некоторыми дополнительными интерпретациями для этого есть очень лаконичный сценарий awk (уже опубликованный enzotib ):
Менее сжато:
!seen[$0] {print} {seen[$0] += 1}
то есть вывести текущую строку, если она еще не видна, затем увеличитьseen
счетчик для этой строки (неинициализированные переменные или элементы массива имеют числовое значение 0).Для длинных строк вы можете сэкономить память, сохраняя только несанкционированную контрольную сумму (например, криптографический дайджест) каждой строки. Например, используя SHA-1, вам нужно всего 20 байтов плюс постоянные издержки на строку. Но вычисление дайджестов происходит довольно медленно; Этот метод выиграет, только если у вас быстрый ЦП (особенно с аппаратным ускорителем для вычисления дайджестов) и не достаточно памяти относительно размера файла и достаточно длинных строк. Никакая базовая утилита не позволяет вычислить контрольную сумму для каждой строки; вам придется нести ответственность за интерпретацию Perl / Python / Ruby /… или написать специальную скомпилированную программу.
источник
awk '!seen[$0]++'
, означает ли это, что если awk видит 2 повторяющиеся строки, он всегда будет сохранять первую и игнорировать все последующие? (Или он сохранит последний?)sort -u
меняет порядок. Мой ответ показывает решения, которые сохраняют порядок (точнее, порядок первых вхождений).Обратите внимание, что выходной файл будет отсортирован.
источник
awk
команда в других ответах, но концептуально просто!sort -u
для удаления дубликатов во время сортировки, а не после. (И экономит пропускную способность памяти), передавая его в другую программу). Это лучше, чемawk
версия, если вы тоже хотите отсортировать вывод. (ОП по этому вопросу хочет сохранить свое первоначальное упорядочение , так что это хороший ответ для немного другого варианта использования.)Предполагая, что вы можете позволить себе хранить в памяти столько же дедуплицированного файла (если ваши данные действительно дублируются с коэффициентом 100, что должно составлять около 20 МБ + накладные расходы), вы можете сделать это очень легко с помощью Perl.
Это сохраняет порядок тоже.
Вы можете извлечь количество вхождений каждой строки из
%dup
хэша, если хотите, в качестве дополнительного бесплатного бонуса.Если вы предпочитаете
awk
, это должно быть сделано тоже самое (та же логика, что и в версии perl, тот же порядок, те же данные, собранные вdup
переменной):источник
uniq
делает это все сам по себеПоскольку никакой другой ответ не предоставил поддержку на месте, вот один:
источник
GNU Awk 4.0.2
Вы можете использовать
uniq
http://www.computerhope.com/unix/uuniq.htmuniq
сообщает или отфильтровывает повторяющиеся строки в файле.источник
'uniq' does not detect repeated lines unless they are adjacent.
так что вам нужно сначала отсортировать его и потерять порядок не повторяющихся строк.Лайнеры Python One:
источник
OrderedDict
Ни один из ответов здесь не работал для меня на моем Mac, поэтому я написал простой скрипт на python, который работает для меня. Я игнорирую начальные / конечные пробелы, а также не заботится о потреблении памяти.
Сохраните вышеупомянутое в unique.py и запустите так:
источник
С bash 4 можно использовать решение с чистым bash, использующее преимущества ассоциативных массивов . Вот пример
источник
read
циклы для обработки больших текстовых файлов. bash должен читать по одному байту за раз, чтобы избежать перекоса новой строки. Bash также не очень быстр в обработке текста по сравнению с awk. Если вы воспользуетесь этим, вы не будете использоватьread -ra
обратную косую черту в своем входе. Кроме того, не забудьтеunset llist
после цикла, если вы поместите это в функцию оболочки или используете ее в интерактивном режиме.