Найдите текстовый файл размером 80 ГБ, используя строки в другом, и сохраните результаты для каждой строки в отдельных файлах.

У меня есть массивный файл размером 80 ГБ, который мне нужно искать, используя строки в другом текстовом файле меньшего размера, и (вот кикер) мне нужно затем сохранить результаты для каждой подходящей строки в отдельные файлы, названные в строке поиска.

Какой самый эффективный способ справиться с этой задачей с помощью PHP или AWK?

Пример строки:

Оригинальный текстовый файл 80 ГБ:

line1 "value001","value002","Value003"line2 "Value004","Value005","Value006","Value007"line3 "value001","value002","Value003"line4 "value001","value002","Value003"line5 "value001","value002","Value003"line6 "Value004","Value005","Value006","Value007"line7 "value010","value022","Value009"

Поиск строки текстового файла search.txt содержит эти значения:

Value003
Value007
Value009

Три текстовых файла будут содержать все соответствующие строки для каждой строки поиска:

Value003.txt would contain lines 1, 3, 4, 5
Value007.txt would contain lines 2 and 6
Value009.txt would contain line 7

Дополнительные разъяснения:
Если быть точным, строки представляют собой списки доменов и телефонных номеров, например:

joes.com
brick.net
moes.com
sams.net
2125551212
2025551212
(202)555-1212

В настоящее время я выполняю поиск с использованием длинной строки регулярного выражения в текстовой панели, например:

brick.net|joes.com|moes.com|sams.net|2125551212|2025551212|(202)555-1212

Этот поиск является одновременно громоздким, медленным и приводит к значительному количеству ложных срабатываний, таких как «сеть Самс» и «сеть из желтого кирпича».

Я пытаюсь зафиксировать выставленные значения, такие как sam@sam.net, но не «сеть sams».

0

Решение

Баш и Греп

Цикл поиска в файле поиска и поиск по каждой строке, перенаправление результата в файл с правильным именем:

while read str; do grep -F "$str" infile > "$str".txt; done < search.txt

где infile это ваш большой файл. Это приводит к следующим файлам:

==> Value003.txt <==
line1"value001","value002","Value003"line3"value001","value002","Value003"line4"value001","value002","Value003"line5"value001","value002","Value003"
==> Value007.txt <==
line2"Value004","Value005","Value006","Value007"line6"Value004","Value005","Value006","Value007"
==> Value009.txt <==
line7"value010","value022","Value009"

Обратите внимание, что это обрабатывает очень большой файл несколько раз, и, несмотря на то, что grep работает быстро, цикл по файлу с помощью Bash выполняется медленно, поэтому это возможно только в том случае, если search.txt относительно небольшой.

Awk

Чтобы обработать большой файл только один раз, вы можете перебрать его с помощью awk, и для каждой строки проверить, совпадает ли какая-либо из строк:

#!/usr/bin/awk -f

# Read search file into array
NR == FNR {
searchstr[$0]
next
}

{
# Iterate over search strings
for (str in searchstr) {
# Print to file if matches
if (index($0, str)) {
print $0 > str ".txt"# next  # Uncomment if only one search string can occur per line
# close(str ".txt") # Uncomment if there are too many open files
}
}
}

Это должно быть вызвано следующим образом:

awk -f script.awk search.txt infile

В менее читаемой однострочной версии:

awk 'NR==FNR{ss[$0];next}{for(s in ss)if(index($0,s))print$0>s".txt"}' search.txt infile

Обратите внимание, что у некоторых awk есть ограничение на количество открытых файловых дескрипторов.1, и другие (GNU awk) могут управлять большим количеством ресурсов, но замедлять его сверх этого предела — это зависит от размера вашего search.txt, Если это станет проблемой, мы можем добавить close(str ".txt") к if пункт, чтобы закрыть файл после каждой записи.

Если в каждой строке может присутствовать только одна строка поиска, мы можем раскомментировать next Заявление в цикле.


1 Оригинальный awk имел ограничение в 15 открытых файлов!

1

Другие решения

Если ваш ввод действительно такой, как показано, то все, что вам нужно с GNU awk, это:

NR==FNR{s=(s ? s "|" : "") $0; next} match($0,s,a){print > (a[0] ".txt")}

например.:

$ awk 'NR==FNR{s=(s ? s "|" : "") $0; next} match($0,s,a){print $0 "\t> " (a[0] ".txt")}' search.txt bigfile
line1"value001","value002","Value003"   > Value003.txt
line2"Value004","Value005","Value006","Value007"        > Value007.txt
line3"value001","value002","Value003"   > Value003.txt
line4"value001","value002","Value003"   > Value003.txt
line5"value001","value002","Value003"   > Value003.txt
line6"Value004","Value005","Value006","Value007"        > Value007.txt
line7"value010","value022","Value009"   > Value009.txt

Если это не сработает, потому что ваши входные данные не совсем соответствуют показанным в вашем вопросе, тогда, очевидно, отредактируйте свой вопрос, чтобы показать более точный репрезентативный пример ввода и вывода.

0