Trường hợp phù hợp thay thế mô hình với sed


14

Tôi có một mã nguồn trải rộng trên một số tập tin.

  • Nó có một mô hình abcdefmà tôi cần phải thay thế bằng pqrstuvxyz.
  • Mẫu có thể là Abcdef(Trường hợp câu) sau đó nó cần được thay thế bằng Pqrstuvxyz.
  • Mẫu có thể là AbCdEf(trường hợp chuyển đổi) sau đó nó cần được thay thế bằng PqRsTuVxYz.

Tóm lại, tôi cần khớp với trường hợp của mẫu nguồn và áp dụng mẫu đích thích hợp.

Làm thế nào tôi có thể đạt được điều này bằng cách sử dụng sedhoặc bất kỳ công cụ nào khác?


Và nếu đó là ABcDeF?
Stéphane Chazelas

PQrStUvxyz - Tôi hiểu ý của bạn.
1263746

Vì vậy, nếu ABcDeF-> PQrStUvxyz, thì chắc chắn AbCdEf-> PqRsTuvxyzsẽ nhất quán về mặt logic. Nếu trường hợp được sao chép từ chuỗi này sang chuỗi khác, điều gì sẽ xảy ra nếu chuỗi thay thế thứ hai dài hơn.
Graeme

Vâng, hãy cắt thay thế thành "pqrstu" vì lý do ngắn gọn.
1263746

Câu trả lời:


9

Giải pháp di động sử dụng sed:

sed '
:1
/[aA][bB][cC][dD][eE][fF]/!b
s//\
&\
pqrstu\
PQRSTU\
/;:2
s/\n[[:lower:]]\(.*\n\)\(.\)\(.*\n\).\(.*\n\)/\2\
\1\3\4/;s/\n[^[:lower:]]\(.*\n\).\(.*\n\)\(.\)\(.*\n\)/\3\
\1\2\4/;t2
s/\n.*\n//;b1'

Nó dễ dàng hơn một chút với GNU sed:

search=abcdef replace=pqrstuvwx
sed -r ":1;/$search/I!b;s//\n&&&\n$replace\n/;:2
    s/\n[[:lower:]](.*\n)(.)(.*\n)/\l\2\n\1\3/
    s/\n[^[:lower:]](.*\n)(.)(.*\n)/\u\2\n\1\3/;t2
    s/\n.*\n(.*)\n/\1/g;b1"

Bằng cách sử dụng &&&ở trên, chúng tôi sử dụng lại mẫu trường hợp của chuỗi cho phần còn lại của thay thế, Vì vậy, ABcdefsẽ được thay đổi thành PQrstuVWxAbCdEfthành PqRsTuVwX. Thay đổi nó để &chỉ ảnh hưởng đến trường hợp của 6 ký tự đầu tiên.

(lưu ý rằng nó có thể không làm những gì bạn muốn hoặc có thể chạy vào một vòng lặp vô hạn nếu sự thay thế có thể bị thay thế (ví dụ nếu thay thế foocho foohoặc bcdcho abcd)


8

Giải pháp di động sử dụng awk:

awk -v find=abcdef -v rep=pqrstu '{
  lwr=tolower($0)
  offset=index(lwr, tolower(find))

  if( offset > 0 ) {
    printf "%s", substr($0, 0, offset)
    len=length(find)

    for( i=0; i<len; i++ ) {
      out=substr(rep, i+1, 1)

      if( substr($0, offset+i, 1) == substr(lwr, offset+i, 1) )
        printf "%s", tolower(out)
      else
        printf "%s", toupper(out)
    }

    printf "%s\n", substr($0, offset+len)
  }
}'

Ví dụ đầu vào:

other abcdef other
other Abcdef other
other AbCdEf other

Ví dụ đầu ra:

other pqrstu other
other Pqrstu other
other PqRsTu other

Cập nhật

Như đã chỉ ra trong các ý kiến, ở trên sẽ chỉ thay thế phiên bản đầu tiên của findmỗi dòng. Để thay thế tất cả các trường hợp:

awk -v find=abcdef -v rep=pqrstu '{
  input=$0
  lwr=tolower(input)
  offset=index(lwr, tolower(find))

  if( offset > 0 ) {
    while( offset > 0 ) {

      printf "%s", substr(input, 0, offset)
      len=length(find)

      for( i=0; i<len; i++ ) {
        out=substr(rep, i+1, 1)

        if( substr(input, offset+i, 1) == substr(lwr, offset+i, 1) )
          printf "%s", tolower(out)
        else
          printf "%s", toupper(out)
      }

      input=substr(input, offset+len)
      lwr=substr(lwr, offset+len)
      offset=index(lwr, tolower(find))
    }

    print input
  }
}'

Ví dụ đầu vào:

other abcdef other ABCdef other
other Abcdef other abcDEF
other AbCdEf other aBCdEf other

Ví dụ đầu ra:

other pqrstu other PQRstu other
other Pqrstu other pqrSTU
other PqRsTu other pQRsTu other

Lưu ý rằng chỉ xử lý một thể hiện trên mỗi dòng.
Stéphane Chazelas

@StephaneChazelas, được cập nhật để xử lý nhiều trường hợp.
Graeme

6

Bạn có thể sử dụng perl. Trực tiếp từ faq - trích dẫn từ perldoc perlfaq6:

Làm cách nào để thay thế trường hợp không nhạy cảm với LHS trong khi bảo quản trường hợp trên RHS?

Đây là một giải pháp Perlish đáng yêu của Larry Rosler. Nó khai thác các thuộc tính của bitwise xor trên chuỗi ASCII.

   $_= "this is a TEsT case";

   $old = 'test';
   $new = 'success';

   s{(\Q$old\E)}
   { uc $new | (uc $1 ^ $1) .
           (uc(substr $1, -1) ^ substr $1, -1) x
           (length($new) - length $1)
   }egi;

   print;

Và đây là một chương trình con, được mô phỏng theo cách trên:

       sub preserve_case($$) {
               my ($old, $new) = @_;
               my $mask = uc $old ^ $old;

               uc $new | $mask .
                       substr($mask, -1) x (length($new) - length($old))
   }

       $string = "this is a TEsT case";
       $string =~ s/(test)/preserve_case($1, "success")/egi;
       print "$string\n";

Bản in này:

           this is a SUcCESS case

Thay vào đó, để giữ trường hợp của từ thay thế nếu nó dài hơn từ gốc, bạn có thể sử dụng mã này, bởi Jeff Pinyan:

   sub preserve_case {
           my ($from, $to) = @_;
           my ($lf, $lt) = map length, @_;

           if ($lt < $lf) { $from = substr $from, 0, $lt }
           else { $from .= substr $to, $lf }

           return uc $to | ($from ^ uc $from);
           }

Điều này thay đổi câu thành "đây là trường hợp SUcCess."

Chỉ cần chỉ ra rằng các lập trình viên C có thể viết C bằng bất kỳ ngôn ngữ lập trình nào, nếu bạn thích một giải pháp giống C hơn, tập lệnh sau đây làm cho sự thay thế có cùng trường hợp, từng chữ cái, như bản gốc. (Nó cũng xảy ra để chạy chậm hơn khoảng 240% so với giải pháp Perlish chạy.) Nếu thay thế có nhiều ký tự hơn chuỗi được thay thế, trường hợp của ký tự cuối cùng được sử dụng cho phần còn lại của thay thế.

   # Original by Nathan Torkington, massaged by Jeffrey Friedl
   #
   sub preserve_case($$)
   {
           my ($old, $new) = @_;
           my ($state) = 0; # 0 = no change; 1 = lc; 2 = uc
           my ($i, $oldlen, $newlen, $c) = (0, length($old), length($new));
           my ($len) = $oldlen < $newlen ? $oldlen : $newlen;

           for ($i = 0; $i < $len; $i++) {
                   if ($c = substr($old, $i, 1), $c =~ /[\W\d_]/) {
                           $state = 0;
                   } elsif (lc $c eq $c) {
                           substr($new, $i, 1) = lc(substr($new, $i, 1));
                           $state = 1;
                   } else {
                           substr($new, $i, 1) = uc(substr($new, $i, 1));
                           $state = 2;
                   }
           }
           # finish up with any remaining new (for when new is longer than old)
           if ($newlen > $oldlen) {
                   if ($state == 1) {
                           substr($new, $oldlen) = lc(substr($new, $oldlen));
                   } elsif ($state == 2) {
                           substr($new, $oldlen) = uc(substr($new, $oldlen));
                   }
           }
           return $new;
   }

Lưu ý rằng nó giới hạn ở các chữ cái ASCII.
Stéphane Chazelas

5

Nếu bạn cắt thay thế pqrstu, hãy thử điều này:

Đầu vào:

abcdef
Abcdef
AbCdEf
ABcDeF

Ouput:

$ perl -lpe 's/$_/$_^lc($_)^"pqrstu"/ei' file
pqrstu
Pqrstu
PqRsTu
PQrStU

Nếu bạn muốn thay thế bằng prstuvxyz, có thể là:

$ perl -lne '@c=unpack("(A4)*",$_);
    $_ =~ s/$_/$_^lc($_)^"pqrstu"/ei;
    $c[0] =~ s/$c[0]/$c[0]^lc($c[0])^"vxyz"/ei;
    print $_,$c[0]' file
pqrstuvxyz
PqrstuVxyz
PqRsTuVxYz
PQrStUVXyZ

Tôi không thể tìm thấy bất kỳ quy tắc nào trên bản đồ ABcDeF-> PQrStUvxyz.


Lưu ý rằng nó giới hạn ở các chữ cái ASCII.
Stéphane Chazelas

3

Một cái gì đó như thế này sẽ làm những gì bạn mô tả.

sed -i.bak -e "s/abcdef/pqrstuvxyz/g" \
 -e "s/AbCdEf/PqRsTuVxYz/g" \
 -e "s/Abcdef/Pqrstuvxyz/g" files/src
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.