perl regex从字符串中删除初始的全空白行:它为什么会起作用?

nue99wik  于 2022-11-15  发布在  Perl
关注(0)|答案(2)|浏览(155)

正则表达式s/\A\s*\n//删除字符串开头的所有空白行。它保留所有其他内容,包括第一个可见行开头的所有空白。所谓“可见行”,我指的是满足/\S/的行。下面的代码演示了这一点。
但它是如何工作的呢?
\A定位字符串的开头
\s*贪婪地抓取所有的空白。但是如果没有(?s)修饰符,它应该停在第一行的末尾,不是吗?请参阅https://perldoc.perl.org/perlre
假设没有(?s)修饰符,它仍然“将字符串视为一行”,那么我会认为贪婪的\s*会抓取它看到的每一个空格字符,包括换行符,所以它会传递“dogs”字符串前面的换行符,继续抓取空格,遇到“d”,我们永远不会得到匹配。
尽管如此,这段代码确实做到了我想要的。因为我无法解释它,它就像是一个拼凑的东西,通过尝试和错误发现它碰巧工作了。它工作的原因是什么?

#!/usr/bin/env perl 
use strict; use warnings;
print $^V; print "\n";

my @strs=(
    join('',"\n", "\t", ' ', "\n", "\t", ' dogs',),
    join('',
              "\n",
              "\n\t\t\x20",
              "\n\t\t\x20",
    '......so what?',
              "\n\t\t\x20",
    ),
);

my $count=0;
for my $onestring(@strs)
{
    $count++;
    print "\n$count ------------------------------------------\n"; 
    print "|$onestring|\n";
    (my $try1=$onestring)=~s/\A\s*\n//;
    print "|$try1|\n";
}
qkf9rpyu

qkf9rpyu1#

但它是如何工作的呢?
...
我认为贪婪的\s* 会抓取它看到的每一个空格字符,包括换行符,所以它会传递“dogs”字符串前面的换行符,继续抓取空格,遇到“d”,我们永远不会得到匹配。
正确--\s*首先抓取d(在dogs中)之前的所有内容,这样匹配就会失败......所以它会备份,一次一个字符,缩短贪婪的抓取,以便给后面的模式(这里是\n)提供匹配的机会。
这样就成功了!所以\s*匹配到(最后一个!)\n,这个匹配到模式中的\n,一切都很好。这个被删除了,我们保留了打印出来的"\tdogs"
回溯可以被抑制,最明显的是通过所有格形式(如\w++等),或者更确切地说是通过扩展构造(?>...)
但是如果没有(?s)修饰符,它应该停在第一行的末尾,不是吗?
这里您可能会混淆\s.,后者实际上与\n不匹配(没有/s

nmpmafwu

nmpmafwu2#

这里有两个问题。
第一个是关于\s和(缺乏)(?s)的相互作用。很简单,没有相互作用。
\s匹配空格字符,包括换行符(LF)。它不受(?s)的任何影响。
(?s)只会影响.

  • (?-s)使.匹配除LF以外的所有字符。[默认值]
  • (?s)使.匹配所有字符。

如果要匹配当前行上的空白,可以使用\h代替\s。它只匹配水平空白,因此不包括CR和LF(以及其他)。
或者,(?[ \s - \n ]) [1]、[^\S\n] [2]和\s(?<!\n) [3]都匹配除LF以外的空白字符。
第二个是关于贪婪的误解。
贪婪或缺乏贪婪不会影响模式是否匹配,只会影响它匹配什么。例如,对于给定的输入,/a+//a+?/都匹配,或者都不匹配。不可能一个匹配而另一个不匹配。

"aaaa" =~ /a+/    # Matches 4 characters at position 0.
"aaaa" =~ /a+?/   # Matches 1 character  at position 0.

"bbbb" =~ /a+/    # Doesn't match.
"bbbb" =~ /a+?/   # Doesn't match.

当某个东西是贪婪的,它意味着它将在当前位置 * 匹配最有可能的东西,这允许整个模式匹配 *。

"ccccd" =~ /.*d/

这个模式可以通过让.*只匹配cccc而不是ccccd来进行匹配,因此这样做了。这是通过回溯来实现的。.*最初匹配ccccd,然后发现d不匹配,因此.*只尝试匹配cccc。这允许匹配d,从而匹配整个模式。
你会发现回溯也在贪婪之外使用。"efg" =~ /^(e|.f)g/匹配是因为当它在使用第一个选择时无法匹配g时,它会尝试第二个选择。
与前面示例中.*避免匹配d的方式相同,您的示例中\s*避免匹配dog之前的LF和制表符。
1.在5.36之前需要use experimental qw( regex_sets );,但从5.18开始可以安全使用,因为它作为一个实验性功能引入后被接受,没有任何变化。
1.不太清楚,因为它使用双重否定。
[^\S\n]
=一个字符,该字符为(not(not(\s)或LF))
=一个字符,该字符为(not(not(\s))且不是(LF))
=一个字符(\s而不是LF)
1.效率较低,而且远不如正则表达式集漂亮。

相关问题