Thay thế chuỗi bằng từ điển


8

Một cách tốt để thực hiện thay thế chuỗi trong một tệp bằng cách sử dụng một từ điển với rất nhiều cặp nhóm thế? Và rất nhiều , tôi thực sự có nghĩa là khoảng 20 - không nhiều, nhưng nhiều đến mức tôi muốn sắp xếp chúng gọn gàng.

Tôi muốn thu thập tất cả các cặp nhóm thế trong một tệp dictionary.txttheo cách dễ quản lý, vì tôi cần thay thế nhiều thứ, nói như sau:

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

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

Bây giờ tôi muốn áp dụng những thay thế này trong một số tập tin novel.txt.

Sau đó, tôi muốn chạy magiccommand --magicflags dictionary.txt novel.txtsao cho tất cả các trường hợp yestrong novel.txtđược thay thế bằng no(vì vậy ngay cả Bayesiansẽ được thay thế bằng Banoian) và tất cả các trường hợp goodbyetrong novel.txtsẽ được thay thế bằng hellovà vân vân.

Cho đến nay, các chuỗi tôi cần thay thế (và thay thế bằng) không có bất kỳ dấu ngoặc kép nào (không đơn lẻ hay gấp đôi) trong chúng. (Tuy nhiên, thật tuyệt khi thấy một giải pháp hoạt động tốt với các chuỗi chứa dấu ngoặc kép, tất nhiên.)

Tôi biết sedawk/ gawkcó thể làm những thứ đó một cách chủ yếu, nhưng chúng cũng có thể làm việc với các tệp từ điển như vậy không? Có vẻ như gawksẽ là ứng cử viên phù hợp cho magiccommand, những gì là đúng magicflags? Làm thế nào để tôi cần định dạng của tôi dictionary.txt?


1
Perl có được chấp nhận không? Có một mẹo hay để biên dịch một tập các mẫu tìm kiếm và thay thế.
Sobrique

Câu trả lời:


3

Đây là một cách với 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

Cách thức hoạt động:
Cái thứ nhất sedbiến dictionary.txtthành tập tin script (chỉnh sửa các lệnh, mỗi lệnh trên một dòng). Điều này được dẫn đến lần thứ 2 sed(lưu ý -f -có nghĩa là đọc các lệnh từ stdin) thực thi các lệnh đó, chỉnh sửa novel.txt.
Điều này đòi hỏi phải dịch định dạng của bạn

"STRING"   :   "REPLACEMENT"

vào một sedlệnh và thoát khỏi bất kỳ ký tự đặc biệt nào trong quá trình cho cả hai LHSRHS:

s/ESCAPED_STRING/ESCAPED_REPLACEMENT/g

Vì vậy, sự thay thế đầu tiên

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

biến "STRING" : "REPLACEMENT"thành STRING\nREPLACEMENT( \nlà một char dòng mới). Kết quả sau đó được sao chép trên hkhông gian cũ.
s|.*\n||chỉ xóa phần đầu tiên REPLACEMENTsau đó s|[\&/]|\\&|gthoát khỏi các ký tự dành riêng (đây là phần RHS).
Sau đó, e xthay đổi bộ đệm giữ với không gian mẫu và s|\n.*||xóa phần thứ hai chỉ giữ STRINGs|[[\.*^$/]|\\&|gthực hiện thoát (đây là LHS).
Nội dung của bộ đệm giữ sau đó được thêm vào không gian mẫu thông qua Gvì vậy bây giờ nội dung không gian mẫu là ESCAPED_STRING\nESCAPED_REPLACEMENT.
Sự thay thế cuối cùng

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

biến nó thành s/ESCAPED_STRING/ESCAPED_REPLACEMENT/g


1

Đây là một phiên bản perl. Nó tạo ra một hàm băm chứa các biểu thức chính quy được biên dịch trước và sau đó lặp qua từng dòng đầu vào áp dụng tất cả các biểu thức chính cho mỗi dòng. perl's -iđược sử dụng để "chỉnh sửa tại chỗ" của tệp đầu vào. Bạn có thể dễ dàng thêm hoặc thay đổi bất kỳ chuỗi regex hoặc chuỗi thay thế nào.

Biên dịch trước các biểu thức chính bằng cách sử dụng qr//cải thiện đáng kể tốc độ của tập lệnh, điều này sẽ rất đáng chú ý nếu có nhiều biểu thức chính và / hoặc nhiều dòng đầu vào để xử lý.

#! /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;
      }
}

Đây là một phiên bản khác đọc trong từ điển từ tên tệp đầu tiên trên dòng lệnh, trong khi vẫn xử lý tên tệp thứ hai (và tùy chọn tiếp theo):

#! /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

Bắt đầu viết bài này như một bình luận, nhưng nó quá phức tạp, do đó câu trả lời thứ hai. Đưa ra tệp nguồn của bạn, bạn có thể sử dụng thủ thuật perl gọn gàng để xây dựng biểu thức chính quy:

#!/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"

Chúng tôi tạo ra một hàm băm bằng cách sử dụng khớp mẫu nhiều dòng và mapđể tạo các cặp giá trị chính.

Chúng tôi xây dựng một biểu thức tìm kiếm và sử dụng các giá trị được ghi lại trong đó để thay thế.

Sử dụng <>STDINtập tin ma thuật của perl - hoặc các tập tin được chỉ định trên dòng lệnh. Làm thế nào nhiều sed làm điều đó. (Bạn có thể sử dụng một tệp và đọc nó 'bình thường' cho mẫu, việc sử dụng DATAhoàn toàn mang tính minh họa).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.