Для каждой строки в файле A заменить все совпадающие строки в файле B шаблоном

Использование bashи csvformatиз набораcsvkitдля изменения разделителя полей с запятой на новую строку:

$ csvformat -D $'\n' file
field1
field2
field3
field4

Предполагается, что ввод в fileпредставляет собой правильно отформатированный CSV. Это также правильно обработает встроенные запятые:

$ cat file
"field,1",field2,field3,field4
$ csvformat -D $'\n' file
field,1
field2
field3
field4

3
01.07.2020, 19:38
3 ответа
$ cat tst.awk
BEGIN {
    dots = sprintf("%*s",1000,"")
    gsub(/ /,".",dots)
    resSingle = "res-single"
    resLength = "res-length"
}
{ lc = tolower($0) }
NR==FNR {
    lgth = length($0)
    str2lgth[lc] = lgth
    str2dots[lc] = substr(dots,1,lgth)
    next
}
{
    for (str in str2lgth) {
        if ( s=index(lc,str) ) {
            bef = substr($0,1,s-1)
            aft = substr($0,s+str2lgth[str])
            print bef "." aft > resSingle
            print bef str2dots[str] aft > resLength
        }
    }
}

.

$ awk -f tst.awk fileA fileB

$ cat res-single
12.1991
ari.#!
.agnes#!
.45

$ cat res-length
12....1991
ari.....#!
...agnes#!
...45

Вышеприведенное предполагает, что ни одна строка в файле A не будет длиннее 1000 символов, если это не так, выберите большее число или мы можем добавить код для его вычисления, если это необходимо. Это также предполагает, что вам все равно, в каком порядке строки из файла A ищутся в файле B, и что вы хотите выполнить сравнение строк, а не регулярное выражение, оба снова тривиальные настройки, если это не то, что вы хотите.


РЕДАКТИРОВАТЬ в ответ на ваш комментарий ниже, вот как изменить приведенное выше, если вы не можете статически определить максимальную длину для строк из файла A (, даже не 100 000 символов? )и так нужно выяснить макс, а строки из файла А все в нижнем регистре:

NR==FNR {
    lgth = length($0)
    str2lgth[$0] = lgth
    maxLgth = (lgth > maxLgth ? lgth : maxLgth)
    next
}
FNR==1 {
    dots = sprintf("%*s",maxLgth,"")
    gsub(/ /,".",dots)
    for ( str in str2lgth ) {
        str2dots[str] = substr(dots,1,str2lgth[str])
    }
    resSingle = "res-single"
    resLength = "res-length"
}
{
    lc = tolower($0)
    for (str in str2lgth) {
        if ( s=index(lc,str) ) {
            bef = substr($0,1,s-1)
            aft = substr($0,s+str2lgth[str])
            print bef "." aft > resSingle
            print bef str2dots[str] aft > resLength
        }
    }
}
1
18.03.2021, 23:23

Здесь вы можете использовать простой подход, основанный на Perl.

Метод:

заполнить хэш %h, ключами которого являются строчные строки (без новых строк )файла A, а значениями являются эквивалентное количество точек.

Затем для каждой строки файла B мы проверяем наличие какого-либо ключа хеша %h без учета регистра. Если да, то мы печатаем данные прематча, совпадения и постматча в файлы res -single и res -length. Если вам нужен только первый матч, раскомментируйте «последний» оператор.

$ perl -Mautodie -lne '
    BEGIN {
     open *{"FH$_"}, ">", qw[res-single res-length][$_] for 0..1;
     do{
       local @ARGV = pop;
       $h{do{chomp;lc;}} = s/././gr =~ tr/\n//dr while <>;
       @h = keys %h;
      };
    }
    for my $h ( @h ) {
      if ( /\Q$h/pi ) {
        my($p, $q) = (${^PREMATCH}, ${^POSTMATCH});
        print {*{"FH$_"}} $p, (".", $h{$h})[$_], $q for 0..1;
        #last;
      }
    }
' fileB fileA

$ more res-*

::::::::::::::
res-length
::::::::::::::
12....1991
ari.....#!
...agnes#!
...45

::::::::::::::
res-single
::::::::::::::
12.1991
ari.#!
.agnes#!
.45
1
18.03.2021, 23:23

Оптимизированное решение Chttps://github.com/dizcza/people-names-as-passwords/blob/master/src/create_masks.c

Я использовал структуру данных trie, которая позволила мне разобрать 2 миллиарда строк fileBс 43 тысячами строк fileAвсего за 12 минут!

Спасибо всем за участие.

0
18.03.2021, 23:23

Теги

Похожие вопросы