Замена строки с использованием словаря

Вы можете сделать это с помощью rsync :

rsync -avWxP --exclude Desktop /home/user/ /home/user/Desktop/
7
12.03.2016, 22:05
3 ответа

Вот один из способов с помощью sed:

sed '
s|"\(.*\)"[[:blank:]]*:[[:blank:]]*"\(.*\)"|\1\
\2|
h
s|.*\n||
s|[\&/]|\\&|g
x
s|\n.*||
s|[[\.*^$/]|\\&|g
G
s|\(.*\)\n\(.*\)|s/\1/\2/g|
' dictionary.txt | sed -f - novel.txt

Как это работает:
1-й sed превращает dictionary.txt в скрипт-файл (команды редактирования, по одной на строку). Он передается во второй sed (обратите внимание на -f -, что означает чтение команд из stdin), который выполняет эти команды, редактируя novel.txt.
Это требует перевода вашего формата

"STRING"   :   "REPLACEMENT"

в команду sed и экранирования любых специальных символов в процессе как для LHS, так и для RHS:

s/ESCAPED_STRING/ESCAPED_REPLACEMENT/g

Итак, первая подстановка

s|"\(.*\)"[[:blank:]]*:[[:blank:]]*"\(.*\)"|\1\
\2|

превращает "STRING" : "REPLACEMENT" в STRING\nREPLACEMENT (\n - символ новой строки). Затем результат копируется поверх hстарого пробела.
s|.*\n||| удаляет первую часть, оставляя только REPLACEMENT, затем s|[\&/]|\\\\&|g экранирует зарезервированные символы (это RHS).
Затем exизменяет буфер удержания с пробелом шаблона и s|\n.*|| удаляет вторую часть, оставляя только STRING и s|[[\.*^$/]|\\\\&|g выполняет экранирование (это LHS).
Содержимое буфера удержания затем добавляется в пространство шаблона через G, так что теперь содержимое пространства шаблона - ESCAPED_STRING\nESCAPED_REPLACEMENT.
Заключительная подстановка

s|\(.*\)\n\(.*\)|s/\1/\2/g|

преобразует его в s/ESCAPED_STRING/ESCAPED_REPLACEMENT/g

3
27.01.2020, 20:18

Вот версия на perl. Она создает хэш, содержащий предварительно скомпилированные регулярные выражения, а затем циклически просматривает каждую строку ввода, применяя все регексы к каждой строке. perl's -i используется для "редактирования на месте" входного файла. Вы можете легко добавить или изменить любой из регексов или заменяющих строк.

Предварительная компиляция регексов с помощью qr// значительно повышает скорость работы скрипта, что будет очень заметно при большом количестве регексов и/или большом количестве входных строк для обработки.

#! /usr/bin/perl -i

use strict;

# the dictionary is embedded in the code itself.
# see 2nd version below for how to read dict in
# from a file.
my %regex = (
    qr/yes/      => 'no',
    qr/stop/     => 'go, go, go!',
    qr/wee-ooo/  => 'ooooh nooo!',
    qr/gooodbye/ => 'hello',
    qr/high/     => 'low',
    qr/why\?/    => 'i don\'t know',
);

while (<>) {
      foreach my $key (keys %regex) {
            s/$key/$regex{$key}/g;
      }
}

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

#! /usr/bin/perl -i

use strict;

# the dictionary is read from a file.
#
# file format is "searchpattern replacestring", with any
# number of whitespace characters (space or tab) separating
# the two fields.  You can add comments or comment out dictionary
# entries with a '#' character.
#
# NOTE: if you want to use any regex-special characters as a
# literal in either $searchpattern or $replacestring, you WILL
# need to escape them with `\`.  e.g. for a literal '?', use '\?'.
#
# this is very basic and could be improved.  a lot.

my %regex = ();

my $dictfile = shift ;
open(DICT,'<',$dictfile) || die "couldn't open $dictfile: $!\n";
while(<DICT>) {
    s/#.*// unless (m/\\#/); # remove comments, unless escaped.
                             # easily fooled if there is an escaped 
                             # '#' and a comment on the same line.

    s/^\s*|\s*$//g ;         # remove leading & trailing spaces
    next if (/^$/) ;         # skip empty lines

    my($search, $replace) = split;
    $regex{qr/$search/} = $replace;
};
close(DICT);


# now read in the input file(s) and modify them.
while (<>) {
      foreach my $key (keys %regex) {
            s/$key/$regex{$key}/g;
      }
}
1
27.01.2020, 20:18

Начал писать это как комментарий, но получилось слишком сложно, поэтому второй ответ на perl. Учитывая ваш исходный файл, вы можете использовать аккуратный трюк perl для построения regex:

#!/usr/bin/env perl

use strict;
use warnings; 
use Data::Dumper;

#build key-value pairs
my %replace = map { /"(.+)"\s*:\s*"(.+)"/ } <DATA>;
print Dumper \%replace; 

#take the keys of your hash, then build into capturing regex
my $search = join ( "|", map {quotemeta} keys %replace ); 
$search = qr/($search)/;

print "Using match regex of: $search\n";

#read stdin or files on command line, line by line
while ( <> ) { 
    #match regex repeatedly, replace with contents of hash. 
    s/$search/$replace{$1}/g;
    print;
}

__DATA__
"yes"      : "no"
"stop"     : "go, go, go!"
"wee-ooo"  : "ooooh nooo!"
"gooodbye" : "hello"

"high"     : "low"
"why?"     : "i don't know"

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

Мы создаем поисковый regex и используем полученные в нем значения для замены.

Использование <> - магического файлового интерфейса perl - STDIN или файлов, указанных в командной строке. Примерно так же, как это делает sed. (Вы можете использовать файл и читать его "нормально" для получения шаблона, использование DATA чисто иллюстративное).

1
27.01.2020, 20:18

Теги

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