使用Perl按点(.)分割字符串

8

我使用 split 函数有两种方式。第一种方式(将字符串作为参数传递给 split):

my $string = "chr1.txt";
my @array1 = split(".", $string);
print $array1[0];

我遇到了这个错误:

在打印时使用了未初始化的值

当我使用第二种方式(将正则表达式作为参数传递给 split)进行分割时,没有出现任何错误。
my @array1 = split(/\./, $string); print $array1[0];

我的第一个分割方法只对点号无效。

这背后的原因是什么?


7
这个答案是错误的,因为split函数的参数总是解释为一个模式。传递参数"."和传递参数/./是一样的:都会以非换行符 [^\n] 或者 \N 作为分隔符进行分割—— 除非 use re "/m" 在作用域内,这时它将以任何单个 Perl 代码点(包括在 0x1F_FFFF 以上的非 Unicode 代码点)作为分隔符进行分割。 - tchrist
3个回答

12

"\." 就是表示 .,需要注意转义字符。

如果你想在双引号字符串中使用反斜杠和点号,需要写成 "\\."。或者使用单引号:'\.'


1
确实如此。顺便说一下,在模式中命名的字符不受元字符解释的影响。也就是说,split /\N{FULL STOP}/ 将是一个字面量。这与使用类似 split "\N{FULL STOP} 的字符串不同,后者只是元字符,因为模式引擎从未看到它在语法上是一个命名字符。比较一下 perl5.16.0 -lE 'say for split /\N{FULL STOP}/, "foo.bar.glarch"'perl5.16.0 -lE 'say for split "\N{FULL STOP}", "foo.bar.glarch"' 的输出,你就会明白我的意思了。 - tchrist

6

如果您只想解析文件并获取它们的后缀名,最好使用File::Basename中的fileparse()方法。


2
fileparse并不是这个问题的改进解决方案,因为它只是提供了一种更加笨拙的应用正则表达式的方式。如果后缀总是以点号开头,则应使用my ($name, $suffix) = $filename =~ /(.*)(\..*)/来分割基本名称。 - Borodin

3

除了Mat提供的信息外,还有一些细节需要补充

split "\.", ...中,split的第一个参数首先被解释为双引号字符串,然后再传递给正则表达式引擎。正如Mat所说,在双引号字符串中,\是转义字符,表示“按照字面意思取下一个字符”,例如用于在双引号字符串中放置双引号:"\""

因此,split的模式参数是"."。单个点表示“以任何字符分割”。正如您所知,分割模式本身不是结果的一部分。因此,您将得到几个空字符串作为结果。

但是,为什么第一个元素未定义而不是空?答案在于split的文档:如果您没有限制split返回的元素数量(它的第三个参数),则它将默默地从列表末尾删除空结果。由于所有项都为空,因此列表为空,因此第一个元素不存在且未定义。

您可以通过此特定代码片段看到差异:
 my @p1 = split "\.", "thing";
 my @p2 = split "\.", "thing", -1;
 print scalar(@p1), ' ', scalar(@p2), "\n";

它输出 0 6

然而,处理这个问题的“正确”方式是@soulSurfer2010在他的帖子中所说的。


没有任何人使用名字“soulSurfer2010”(被删除的回答是由用户“ole”发布的)。如果可能的话(可能是一条已删除的评论),您能否添加一个直接引用(可能是一个答案)?如果它是一个未被删除的答案,那么它就是snoofkin的答案。或者它是指一条评论吗? - Peter Mortensen

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