2012-03-15 20 views
13

Perl regex kullanarak belirli bir dizede birkaç özel karakter kaçmaya çalışıyorum. Dolar işareti hariç tüm karakterler için iyi çalışıyor. Denedim aşağıdadır:

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; 

cevap

17

bu deneyin:

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

garip, görünüyor? Sizin düzenli ifade aşağıdaki gibi bakmak gerekir:

s/\$/\$/g 

regex ilk bölümünde, "$" Bu dizenin sonunu gösteren özel bir regex karakter, çünkü kaçmış gerekmektedir.

regex ikinci bölümü "$" özel bir anlam yoktur "normal" bir dize olarak kabul edilir. Bu nedenle ters eğik çizgi gerçek bir ters eğik çizgi olurken, ilk bölümde dolar işaretinden kaçmak için kullanılır. Bunların her ikisi de çift tırnak içindeki özel bir anlamı var çünkü

Ayrıca değişken tanımında sen ters eğik çizgi yanı sıra dolar işareti kaçmak gerekir.

+2

Daha iyi bir yaklaşım: kullanmak 'quotemeta()' veya 's/\ Q $ Char \ e/...' Sen regexpleri onları interpole beri, her $ değişken için bunu hatırlamalıyız. – hhaamu

0

$ regexp içinde özel bir anlam, yani "dizesi sonu" vardır. Böyle bir şey ile daha iyi olurdu:

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

Ayrıca perl daha iyi, daha "$" gibi '$' (tek tırnak => hiçbir enterpolasyon) kullanmaz.

GÜNCELLEME: Üzgünüm, acele bu yazıyordu => çok fazla düzenlemeler :(kendisi sola eğik çizginin ile her karakteri yerine eğer

+0

Geri bildiriminiz için teşekkürler, çözümünüz gerçekten çok hoş görünüyor! Ancak daha kolay kod (takım çalışması) kullanmak zorundayım ... Tek tırnak işaretleri için teşekkürler –

1

Bir karma Sadece gerekmez. neye ihtiyacınız maç ve önünde ters eğik çizgi koyun:. tüm karakterler için normal ifade oluşturmak için

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

, Regexp::Assemble gerçekten güzel

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 

Girdi ilk satırında Regexp :: Assemble modelimi nasıl yeniden düzenlediğime dikkat edin. Sadece ben ekledi parçaların bit birbirlerine yapışık değil:

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

Daha fazla karakter eklemek isterseniz

, sadece @specials karakteri koydu. Her şey senin için olur.