2012-04-10 13 views
7

Trong Perl, cách viết biểu thức chính quy chỉ thay thế tối đa N kết quả cho mỗi chuỗi?Chỉ thay thế tối đa N kết quả trên một dòng

I.e., Tôi đang tìm một nền tảng trung gian giữa s/aa/bb/;s/aa/bb/g;. Tôi muốn cho phép nhiều thay thế, nhưng chỉ tối đa N lần.

Trả lời

4

Tôi có thể nghĩ ra ba cách đáng tin cậy. Đầu tiên là thay thế mọi thứ sau trận đấu thứ N với chính nó.

my $max = 5; 
$s =~ s/(aa)/ $max-- > 0 ? 'bb' : $1 /eg; 

Điều đó không hiệu quả nếu có nhiều hơn N so khớp. Cho rằng, chúng ta cần phải di chuyển vòng lặp ra khỏi động cơ regex. Hai phương pháp tiếp theo là cách để làm điều đó.

my $max = 5; 
my $out = ''; 
$out .= $1 . 'bb' while $max-- && $in =~ /\G(.*?)aa/gcs; 
$out .= $1 if $in =~ /\G(.*)/gcs; 

Và thời gian này, tại chỗ:

my $max = 5; 
my $replace = 'bb'; 
while ($max-- && $s =~ s/\G.*?\Kaa/$replace/s) { 
    pos($s) = $-[0] + length($replace); 
} 

Bạn có thể bị cám dỗ để làm một cái gì đó giống như

my $max = 5; 
$s =~ s/aa/bb/ for 1..$max; 

nhưng cách tiếp cận đó sẽ thất bại cho các mẫu khác và/hoặc biểu thức thay thế.

my $max = 5; 
$s =~ s/aa/ba/ for 1..$max; # XXX Turns 'aaaaaaaa' 
          #  into 'bbbbbaaa' 
          #  instead of 'babababa' 
+0

+1 để chỉ ra vấn đề với' s /.../ .../cho 1..N' Nhưng ví dụ có một lỗ hổng nhỏ, 'aaaa' sẽ trở thành' bbba' không 'bbbb'. – Qtax

+0

@Qtax, Cảm ơn.Tôi không muốn downvote Hubert Schölnast vì anh ấy mới và câu trả lời của anh ấy thực sự phù hợp với câu hỏi cụ thể, nhưng tôi nghi ngờ OP thực sự làm việc với 'aa' và' bb'. Vì vậy, tôi đã giải thích tại sao giải pháp của anh ấy mỏng manh ở đây. – ikegami

+0

@ikegami nó đã được một thời gian dài kể từ khi tôi đã thực hiện perl, nhưng nếu bạn có thể theo dõi những gì vị trí trong chuỗi bạn đang có và khởi động lại tìm kiếm regex từ đó, nó sẽ khắc phục vấn đề với 's /.../ .../cho 1..N'. Mặc dù nó sẽ là một chút xấu xí. –

1

Điều bạn muốn không hiển thị trong cụm từ thông dụng. Nhưng bạn có thể đặt thay thế trong một cho vòng lặp:

my $i; 
my $aa = 'aaaaaaaaaaaaaaaaaaaa'; 
for ($i=0;$i<4;$i++) { 
    $aa =~ s/aa/bb/; 
} 
print "$aa\n"; 

kết quả:

bbbbbbbbaaaaaaaaaaaa

+2

'với $ của tôi (0 .. 3) 'là cách perl để viết nó. Một cách đẹp hơn là '$ aa = ~ s/aa/bb/cho 1 .. 3'. – TLP

+0

Có thể đặt một 'cuối trừ $ aa = ~ ...' trong đó để có hiệu quả – mob

+3

Lưu ý rằng phương pháp này có thể thất bại đối với một số cặp biểu thức tìm kiếm và thay thế, ví dụ: '$ aa = ~ s/aa/ba /;' – ikegami

1

Bạn có thể sử dụng cờ /e mà đánh giá bên phải như là một biểu:

my $n = 3;  
$string =~ s/(aa)/$n-- > 0 ? "bb" : $1/ge; 
+1

Tôi sẽ sửa lỗi cú pháp đó cho bạn: '$ n -> 0' (; – Qtax

1

Dưới đây là một giải pháp sử dụng modifier/e, mà bạn có thể sử dụng code perl để tạo ra các chuỗi thay thế:

 
    my $count = 0; 
    $string =~ s{ $pattern } 
       { 
       $count++; 
       if ($count < $limit) { 
        $replace; 
       } else { 
        $&; # faking a no-op, replacing with the original match. 
       } 
       }xeg; 

Với perl 5.10 hay muộn bạn có thể thả $ & (có các biến chứng về hiệu suất kỳ lạ) và sử dụng $ {^ MATCH} thông qua công cụ sửa đổi/p

 
    $string =~ s{ $pattern } 
       { 
       $count++; 
       if ($count < $limit) { 
        $replace; 
       } else { 
        ${^MATCH}; 
       } 
       }xegp; 

Đó là quá xấu bạn không thể chỉ làm điều này, nhưng bạn không thể:

 
    last if $count >= $limit; 

Các vấn đề liên quan