Perl正则表达式匹配中的井号(#)代表什么意思?

10

以下 Perl 语句的含义是什么?

($script = $0) =~ s#^.*/##g;

我正试图理解 operator =~ 以及右侧的语句 s#^.*/##g。

谢谢


1
你尝试在调试器中执行它了吗(perl -de0)? - Jim Garrison
3个回答

28
=~ 将右边的东西(模式匹配或搜索和替换)应用到左边的东西上。有很多关于 =~ 的文档,所以我只是要指向一个相当不错的文档
那里有一些不明显也不太好记录的习语,可能会让您困惑。 让我们来介绍一下。
首先是这个...
($copy = $original) =~ s/foo/bar/;

这是一种将变量复制并在单个步骤中执行搜索和替换的方法。它等效于:

$copy = $original;
$copy =~ s/foo/bar/;
=~操作符对左边的内容进行操作,该内容为左手代码运行后的结果。($copy = $original)的值为$copy,因此=~会对副本进行操作。 s#^.*/##gs/^.*\///g相同,只是使用可替代定界符来避免倾斜的牙签综合症。您几乎可以使用任何字符作为正则表达式定界符。 #很常见,虽然我认为它难看且难以阅读。 我更喜欢{},因为它们平衡。s{^.*/}{}g是等效的代码。
去除习惯用法,你将得到这个:
$script = $0;
$script =~ s{^.*/}{}g;

$0是脚本的名称。以下代码将复制脚本名称并除去最后一个斜杠之前的所有内容(.* 贪婪匹配,尽可能匹配多)。它仅获取脚本文件名。

/g 表示在字符串上执行尽可能多次的匹配。由于这只能匹配一次(^ 将其锚定在字符串开头),所以它没有任何作用。

有一种更好、更安全的方法可以做到这一点。

use File::Basename;
$script = basename($0);

你并没有解释 =~ 到底是做什么的。 - ysth
也许可以用“飞船”运算符?或者“山羊”运算符?:) 或者其他很多习惯用语?:) - gaussblurinc
@ysth 哈哈,说得好!我不认为 OP 真的在问 =~ 运算符,但我已经修改了以涵盖它。 - Schwern

5

非常简单:

Perl的引号类似表达式可以使用许多不同的字符作为分隔符。命令后面的分隔符(在本例中是s)是其余操作的分隔符。例如:

 # Out with the "Old" and "In" with the new

 $string =~ s/old/new/;
 $string =~ s#old#new#;
 $string =~ s(old)(new);
 $string =~ s@old@new@;

这四种表达式都是相同的,它们在我的$string中用new字符串替换old字符串。不论在s后面跟什么分隔符,这个替换操作都会生效。需要注意的是,圆括号、花括号和方括号使用配对。这对于可以代替单引号和双引号的qqq非常方便:
print "The value of \$foo is \"foo\"\n";   # A bit hard to read
print qq/The value of \$foo is "$foo"\n/;  # Maybe slashes weren't a great choice...
print qq(The value of \$foo is "$foo"\n);  # Very nice and clean!
print qq(The value of \$foo is (believe it or not) "$foo"\n); #Still works!

上述代码仍然有效,因为引号运算符会计算开放和关闭括号。当然,在正则表达式中,括号和方括号是正则表达式语法的一部分,因此您不会在替换中经常看到它们。

大多数时候,强烈建议您坚持使用s/.../.../形式,这样更易于阅读。这是人们所习惯的,并且易于理解。但是,如果你遇到了这种情况呢?

$bin_dir =~ s/\/home\/([^\/]+)\/bin/\/Users\/$1\bin/;

那些反斜杠可能会使阅读变得困难,因此传统做法是用其他分隔符替换反斜杠分隔符,以避免产生“山丘和山谷效应”。

$bin_dir =~ s#/home/([^/]+)/bin#/Users/$1/bin#;

这段文字有些难以理解,但至少我不必引用每个正斜杠和反斜杠,所以更容易看到我正在替换什么。正则表达式很难是因为好的引号字符很难找到。各种特殊符号如^*|+都是神奇的正则表达式字符,并且可能会在正则表达式中使用,#是常用的一个。它在字符串中不常见,在正则表达式中也没有任何特殊含义,因此不会被使用。
回到你最初的问题:
($script = $0) =~ s#^.*/##g;

等同于:

($script = $0) =~ s/^.*\///g;

由于原始程序员不想使用反引号来转义斜杠,所以他们更改了分隔符。

至于:

($script = $0) =~ s#^.*/##g;`

它的意思和以下语句相同:

$script = $0;
$script =~ s#^.*/##g;

你正在一步完成变量$script的赋值和替换。这在Perl中很常见,但一开始可能有点难理解。
顺便说一下,如果我理解这个基本表达式(删除所有字符到最后一个正斜杠),那么这将更加简洁:
use File::Basename;
...

$script = basename($0);

更易读且易于理解 -- 即使对于老练的 Perl 程序员也是如此。


"这非常非常简单",接下来有两页内容。 :) - Schwern

4
在perl中,您可以使用多种字符作为引用字符(字符串、正则表达式、列表)。 让我们来看看:
  • $script变量分配为$0的内容(包含调用脚本名称的字符串)。
  • =〜字符是绑定操作符。它调用正则表达式匹配或正则表达式搜索和替换。在这种情况下,它与新变量$script匹配。
  • s字符表示搜索和替换正则表达式。
  • #字符用作正则表达式的分隔符。正则表达式模式引号字符通常是/字符,但您可以使用其他字符,包括此处的#
  • 正则表达式^.*/ 。它的意思是“在字符串开始,搜索零个或多个字符,直到斜杠。这将在每行上继续捕获,除了换行符(默认情况下不匹配.)。
  • #表示“替换”值的起点。通常,在此处有一个模式,该模式使用第一行中的任何捕获部分。
  • 再次使用#。这结束了替换模式。由于在替换模式的开始和结束之间没有任何内容,因此将找到的第一个内容替换为无。
  • g,或全局匹配。搜索和替换将根据它在值中的匹配次数而不断发生。

有效地,搜索并清空值中/之前的每个值,但保留所有换行符,在脚本名称中。它是一种非常懒惰的方式,可以在仅适用于类unix路径的长脚本中调用脚本名称。

如果有机会,请考虑使用Perl中的核心模块File :: Basename进行替换:

use File::Basename;

# later ... 

my $script = fileparse($0);

=~ 是绑定(不是匹配)运算符。 - Michael Carman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接