2013-09-30 17 views
6

może ktoś wyjaśnić to dziwne zachowanie:Perl podzielić ciekawym zachowanie

I ścieżkę Hava na sznurku i chcę podzielić ją na każdym backslashem

my $path = "D:\Folder\AnotherFolder\file.txt"; 

my @folders = split('\', $path); 

w przypadku powyżej nie będą działać nawet jeśli ucieczki backslash takiego:

my @folders = split('\\', $path); 

ale w przypadku regexp to będzie działać:

my @folders = split(/\\/, $path); 

dlaczego tak jest?

+2

Twoje ucieczki na ścieżce są nieprawidłowe. W podwójnym cudzysłowu, '\ Folder' jest interpretowany jako sekwencja specjalna' \ F'. – TLP

+4

Zła praktyka polega na używaniu cudzysłowów wokół wzoru separatora dla 'split', ponieważ nie wyraża ona odpowiednio semantyki i powoduje podwójną kompilację. Zawsze używaj ukośników, chyba że chcesz przekazać pojedynczy ciąg spacji, aby wywołać domyślne zachowanie, gdy "" "jest właściwym wyborem. – Borodin

+0

@TLP Tak, wiem, zwykle umieszczam tylko pojedyncze cytaty dla ścieżki. dzięki –

Odpowiedz

2

Jeśli spojrzeć na dokumentację uruchamiając:

perldoc -f split 

widać trzy formy argumentów split może trwać:

split /PATTERN/,EXPR,LIMIT 
split /PATTERN/,EXPR 
split /PATTERN/ 

Oznacza to, że nawet kiedy przechodzą split jakiegoś napisu pierwszy argument, perl, wymusza to na wyrażenie regularne.

Jeśli spojrzymy na ostrzeżeniach możemy uzyskać, gdy próbuje zrobić coś takiego w re.pl:

$ my $string_with_backslashes = "Hello\\there\\friend"; 
Hello\there\friend 
$ my @arry = split('\\', $string_with_backslashes); 
Compile error: Trailing \ in regex m/\/ at (eval 287) line 6. 

widzimy, że po pierwsze, '\\' jest interpolowana jak backsleshem uciec następnie rzeczywistej backslash, który ocenia się pojedynczy ukośnik odwrotny.

split następnie umieszcza backslash daliśmy, i wymusza go regex jakbyśmy pisali:

$ my @arry = split(/\/, $string_with_backslashes); 

który nie działa, ponieważ jest tylko jeden lewy ukośnik, który jest interpretowany jako po prostu ucieka przedni ukośnik po nim (bez zakończenia) /), aby pokazać, że regex został zakończony.

+0

W rzeczywistości, gdyby uciekł z ukośnika, pojawiłby się błąd "Wzór wyszukiwania nie został zakończony", co oznacza, że ​​operator został złamany . To jest coś innego. – TLP

+3

bardziej jawnie: łańcuchy i wyrażenia regularne mają różne reguły ucieczki. Jeśli zamiast wyrażenia regularnego używany jest ciąg, literały łańcuchowe cierpią na podwójne ucieczki. – amon

+0

@ user1436026 Przeczytałem perldoc, ale nie mogłem w pełni zrozumieć innerworks, nie wiedziałem, że cokolwiek włożysz "nadal się zmienia i wygłasza –

5

myślę amon dał najlepszą dosłowne odpowiedzi na swoje pytanie w swoim komentarzu:

bardziej wyraźnie: smyczki i Wyrażenia regularne mają różne zasady ucieczki. Jeśli łańcuch jest używany zamiast regex, że literały ciągów znaków cierpią podwójnie ucieczce

znaczy, że split '\\' wykorzystuje ciąg i split /\\/ używa wyrażenia regularnego.

Jako praktyczny odpowiedzi, chciałem dodać to:

Być może należy rozważyć użycie moduł nadaje się do przecinania ścieżek. File::Spec jest podstawowym modułem w Perlu 5. Poza tym musisz uciec przed ukośnikiem odwrotnym w cudzysłowie, czego jeszcze nie zrobiłeś. Możesz także użyć pojedynczych cudzysłowów, co moim zdaniem wygląda nieco lepiej.

use strict; 
use warnings; 
use Data::Dumper; 
use File::Spec; 

my $path = 'D:\Folder\AnotherFolder\file.txt'; # note the single quotes 
my @elements = File::Spec->splitdir($path); 
print Dumper \@elements; 

wyjściowa:

$VAR1 = [ 
      'D:', 
      'Folder', 
      'AnotherFolder', 
      'file.txt' 
     ]; 
+0

dzięki za napiwek z File :: Spec –

+0

@AndreiDoanca To jest odpowiednie narzędzie do dzielenia ścieżek, a także przenośne i niezawodne. Nie jestem pewien, co próbujesz zrobić, dzieląc ścieżkę, ale wygląda na problem XY. – TLP

+0

tak naprawdę to nie jest problem XY tylko ciekawość mojej miny :) –

2

Jednym z neater sposobów, aby wyodrębnić elementy ścieżki jest wyodrębnić wszystkie sekwencje znaków innych niż separatora ścieżki.

use strict; 
use warnings; 

my $path = 'D:\Folder\AnotherFolder\file.txt'; 
my @path = $path =~ m([^/\\]+)g; 

print "$_\n" for @path; 

wyjście

D: 
Folder 
AnotherFolder 
file.txt 
2

split Kiedy jest stosowany w postaci split STRING nie split REGEX, łańcuch jest przekształcana regex. W twoim przypadku split '\\' zostanie przekonwertowany na split /\/, ponieważ pierwszy ukośnik odwrotny jest uważany za znak ucieczki.

Prawidłowy sposób to zrobić: split '\\\\', który zostanie przetłumaczony na split /\\/.

+0

bardzo dziękuję za wyjaśnienie, naprawdę dobre info :) Rozumiem teraz i widzę, że to działa :) –