2012-03-15 16 views
13

Próbuję uciec kilka znaków specjalnych w danym ciągu za pomocą wyrażenia perl. Działa dobrze dla wszystkich postaci oprócz znaku dolara. Próbowałem następujące:

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; 

Odpowiedz

17

Spróbuj tego:

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

wygląda dziwnie, prawda? Twój regex musi wyglądać następująco:

s/\$/\$/g 

W pierwszej części regex, „$” musi być uciekł, bo to specjalny regex znak oznaczający koniec łańcucha.

Druga część wyrażenia regularnego jest traktowana jako "normalny" ciąg znaków, gdzie "$" nie ma specjalnego znaczenia. Dlatego odwrotny ukośnik jest prawdziwym ukośnikiem odwrotnym, podczas gdy w pierwszej części jest używany do uniknięcia znaku dolara.

Ponadto w definicji zmiennej trzeba uciec zarówno od ukośnika, jak i od znaku dolara, ponieważ oba mają specjalne znaczenie w ciągach podwójnie cytowanych.

+2

Lepsze podejście: użyj 'quotemeta()' lub 's/\ Q $ char \ E/...' Powinieneś zapamiętać zrobić to dla każdej zmiennej $, ponieważ regexps interpolują je. – hhaamu

0

$ ma specjalne znaczenie w wyrażeniu regularnym, a mianowicie "koniec ciągu znaków". Użytkownik będzie lepiej z czegoś takiego:

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

Ponadto, Perl nie lubi "$" znacznie lepiej wykorzystywać '$' (apostrofów => bez interpolacji).

UPDATE: Niestety, pisałem to w pośpiechu => zbyt wiele modyfikacji :(

+0

Dzięki za opinie, Twoje rozwiązanie wygląda naprawdę fantazyjnie! Jednak jestem zobowiązany do korzystania z łatwiejszego kodu (praca zespołowa) ... Dzięki za heads up na pojedyncze cytaty –

1

Nie musisz hash jeśli zastępując każdy znak z Sam poprzedzone odwrotnym ukośnikiem Wystarczy. zgadza się, co trzeba i wprowadzić odwrotny ukośnik przed nim.

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

Aby zbudować wyrażenie regularne dla wszystkich znaków, Regexp::Assemble jest naprawdę ładne

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 

Zauważ, jak w pierwszym wierszu wprowadzania, Regexp :: Assemble ponownie uporządkował mój wzorzec. To nie tylko sklejone bity części I dodaje:

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

Jeśli chcesz dodać więcej znaków, po prostu umieścić znak w @specials. Wszystko inne dzieje się dla ciebie.