2012-03-15 9 views
13

Tôi đang cố gắng để thoát khỏi một số ký tự đặc biệt trong một chuỗi đã cho bằng cách sử dụng regex perl. Nó hoạt động tốt cho tất cả các ký tự ngoại trừ ký hiệu đô la. Tôi thử như sau:

my %special_characters; 
$special_characters{"_"} = "\\_"; 
$special_characters{"$"} = "\\$"; 
$special_characters{"{"} = "\\{"; 
$special_characters{"}"} = "\\}"; 
$special_characters{"#"} = "\\#"; 
$special_characters{"%"} = "\\%"; 
$special_characters{"&"} = "\\&"; 

my $string = '$foobar'; 
foreach my $char (keys %special_characters) { 
    $string =~ s/$char/$special_characters{$char}/g; 
} 
print $string; 

Trả lời

17

Hãy thử điều này:

my %special_characters; 
$special_characters{"_"} = "\\_"; 
$special_characters{"\\\$"} = "\\\$"; 
$special_characters{"{"} = "\\{"; 
$special_characters{"}"} = "\\}"; 
$special_characters{"#"} = "\\#"; 
$special_characters{"%"} = "\\%"; 
$special_characters{"&"} = "\\&"; 

Trông lạ, phải không? Regex của bạn cần phải có dạng như sau:

s/\$/\$/g 

Trong phần đầu của regex, "$" cần được thoát, vì đó là ký tự regex đặc biệt biểu thị phần cuối của chuỗi.

Phần thứ hai của regex được coi là chuỗi "bình thường", trong đó "$" không có ý nghĩa đặc biệt. Vì vậy, dấu gạch chéo ngược là một dấu gạch chéo ngược thực sự trong khi trong phần đầu tiên nó được sử dụng để thoát khỏi ký hiệu đô la.

Ngoài ra trong định nghĩa biến, bạn cần phải thoát dấu gạch chéo ngược cũng như ký hiệu đô la, vì cả hai đều có ý nghĩa đặc biệt trong chuỗi được trích dẫn kép.

+2

Cách tiếp cận tốt hơn: sử dụng 'quotemeta()' hoặc 's/\ Q $ char \ E/...' Bạn nên nhớ làm điều này cho mỗi biến $, vì regexps nội suy chúng. – hhaamu

0

$ có ý nghĩa đặc biệt trong regexp, cụ thể là "kết thúc chuỗi". Bạn sẽ được tốt hơn off với một cái gì đó như thế này:

# escape special characters, join them into a single line 
my $chars = join '', map { "\\$_" } keys %special_characters; 
$string =~ s/([$chars])/$special_characters{$1}/g; 

Ngoài ra, perl không thích "$" nhiều, tốt hơn sử dụng '$' (dấu nháy đơn => không có suy).

UPDATE: Xin lỗi, tôi đang viết điều này trong một vội vàng => quá nhiều chỉnh sửa :(

+0

Cảm ơn phản hồi của bạn, giải pháp của bạn trông thực sự lạ mắt! Tuy nhiên, tôi buộc phải sử dụng mã đơn giản hơn (làm việc theo nhóm) ... Cảm ơn bạn đã đọc báo giá đơn –

1

Bạn không cần một hash nếu bạn đang thay thế mỗi nhân vật với chính nó trước bởi một dấu chéo ngược Just. phù hợp với những gì bạn cần và đặt một dấu chéo ngược ở phía trước của nó:.

s/($re)/"\\$1"/eg; 

để xây dựng biểu thức chính quy cho tất cả các nhân vật, Regexp::Assemble là thật sự tốt đẹp

use v5.10.1; 
use Regexp::Assemble; 

my $ra = Regexp::Assemble->new; 

my @specials = qw(_ $ { } # % &); 

foreach my $char (@specials) { 
    $ra->add("\\Q$char\\E"); 
    } 

my $re = $ra->re; 
say "Regex is $re"; 

while(<DATA>) { 
    s/($re)/"\\$1"/eg; 
    print; 
    } 

__DATA__ 
There are $100 dollars 
Part #1234 
Outside { inside } Outside 

Lưu ý cách thức, trong dòng đầu vào đầu tiên, Regexp :: Assemble đã sắp xếp lại mẫu của tôi. Nó không chỉ là dán lại với nhau bit trong những phần tôi đã thêm:

Regex is (?^:(?:[#$%&_]|\{|\})) 
There are \$100 dollars 
Part \#1234 
Outside \{ inside \} Outside 

Nếu bạn muốn thêm ký tự hơn, bạn chỉ cần đặt nhân vật trong @specials. Mọi thứ khác xảy ra cho bạn.