2010-11-11 4 views

ответ

4

Я согласен с другими ответами, а просто хотел объяснить ошибку в шаблоне. Regex сложно, но стоит того, чтобы хорошо учиться.

Квадратные скобки определяют класс объектов, который будет соответствовать. В вашем случае это будет совпадать с косой чертой, символом слова (от \w), символом + или символом прямой косой черты (это избыточно). Тогда вы говорите, чтобы соответствовать 1 или более из них. Есть несколько строк, которые могут совпадать. Он будет соответствовать раннему стартовому символу, поэтому первый /. Тогда он будет хватать как можно больше.

Это не то, что вы намереваетесь четко. Например, если у вас есть . в одном из ваших имен каталогов, вы остановитесь там. /blah.foo/bar/x.y.z вернется .foo/bar/x.y.z.

Способ думать, что вы хотите совместить все символы до финального /.

Все персонажи затем слэш: /.*\//

Но чтобы быть более безопасным, добавить каретку на фронт, чтобы убедиться, что она начинается там: /^.*\//

И чтобы вперед и обратные косые, сделать класс для этого: /^.*[\/\\]/ (т.е. elusive's answer).

A действительно хорошая ссылка Learning Perl. Есть около 3 действительно хороших глав регулярных выражений. Они применимы и для пользователей, не являющихся Perl-regex.

+1

другой альтернативой является привязка в конце строки и соответствие всем, что не является разделителем каталогов (с использованием отрицаемого класса, '[^ ...]'), между разделителем каталогов и концом строки, например '$ out_fname = ~ m {[\/\\] ([^ \/\\] +) $}; my $ filename_only = $ 1; ' – plusplus

-1

Что об этом:

$out_fname =~ s/^.*[\/\\]//; 

Он должен удалить все перед вашим именем.

15

Вы можете сделать:

use File::Basename; 

my $path = "/bla/bla/folder/file.part.1.file"; 
my $filename = basename($path); 
+0

Не в последнюю очередь это сделает ваш скрипт переносимым в других операционных системах. – justintime

+1

Это правильный ответ. –

+0

Да, не используйте регулярное выражение, когда есть встроенная функция. regex стоит дорого, но иногда вам нужно его оплатить. – Keng

5

Помимо файла :: Basename, существует также Path::Class, что может быть удобно для более сложных операций, особенно при работе с каталогами или кросс-платформенной/файловой системой. Вероятно, в этом случае это может быть излишним, но, возможно, стоит знать.

use Path::Class; 

my $file = file("/bla/bla/folder/file.part.1.file"); 
my $filename = $file->basename; 
1

Использование split в разделителе каталогов - еще одна альтернатива. Это имеет те же предостережения, что и при использовании регулярного выражения (т.с именами файлов лучше использовать модуль, где кто-то еще подумал о случаях кросс, переносимости, разных файловых системах и т. д., и поэтому вам не нужно сопоставлять как обратную, так и прямую слэши), но полезно в качестве другой общей техники, где у вас есть строка с повторным разделителем.

my $file = "/bla/bla/folder/file.part.1.file"; 
my @parts = split /\//, $file; 
my $filename = $parts[-1]; 
+0

Отличная идея! Его лучший «раскол» ... спасибо – KingRider

1

Это точно то, что я бы ожидать, что она сохранить в данной подстановки. Вы говорите, что ничего не меняете самую длинную строку косых черт и слов. Таким образом, он захватывает все символы до тех пор, пока первый символ, который вы не сделал, указывает и удаляет их.

Это делает то, что вы просите об этом. Я присоединяюсь к другим, говоря, что использую File::Basename за то, что вы пытаетесь сделать.

Но здесь это самый быстрый способ сделать то же самое:

my $fname = substr($out_fname, rindex($out_fname, '/') + 1); 

Здесь, говорит найти последнее появления '/' в строке, и дать мне текст, начинающийся один за эту позицию. Я не против регулярного выражения, но это простое выражение того, что вы на самом деле хотите сделать. Я должен был делать такие вещи, как это так долго, я написал last_after суб:

sub last_after { 
    my ($string, $delim) = @_; 
    unless (length($string) and my $ln = length($delim)) { 
     return $string // ''; 
    } 
    my $ri = rindex($string, $delim); 
    return $ri == -1 ? $string : substr($string, $ri + $ln); 
} 
0

Я также необходимый тянуть только последнее поле от связки имен путей. Это сработало для меня:

grep -o '/\([^/]*\)$' inputfile > outputfile 
Смежные вопросы