Об использовании пустого регулярного выражения в Perl ( m// )
На днях делал отсечение элементов списка не подходящих под регулярку введённую пользователем:
...
my $re = get_text_in_filter();
@list = grep { /$re/i } @list;
...
$re
был пустой строкой и в @list
должны были остаться все элементы.
Так и происходило при первом проходе, а при втором регулярка не пропускала ни одного элемента списка.
Я предположил, что $re
или @list
изменились и распечатал их датапринтером:
p my $x=[$re, qr/$re/, $list[0], $list[0] =~ qr/$re/? "ok":"no")];
# 1-й проход:
[
[0] "",
[1] (modifiers: u),
[2] "Application",
[3] "ok"
]
# 2-й проход:
[
[0] "",
[1] (modifiers: u),
[2] "Application",
[3] "no"
]
С точки зрения датапринтера они не изменились. «В таком случае, возможно, в переменных какой-то флаг был установлен», продолжал рассуждать я. И сдампил $x дэвэл-пиком:
pop @$x;
use Devel::Peek;
Dump($x);
# 1-й проход:
SV = IV(0x56228695d7f8) at 0x56228695d808
REFCNT = 1
FLAGS = (ROK)
RV = 0x56228708a930
SV = PVAV(0x56228712b618) at 0x56228708a930
REFCNT = 1
FLAGS = ()
ARRAY = 0x562287095590
FILL = 2
MAX = 3
FLAGS = (REAL)
Elt No. 0
SV = PV(0x56228708c200) at 0x562286bfdbb0
REFCNT = 1
FLAGS = (POK,pPOK)
PV = 0x562286fd9930 ""\0
CUR = 0
LEN = 10
Elt No. 1
SV = IV(0x5622870ac428) at 0x5622870ac438
REFCNT = 1
FLAGS = (ROK)
RV = 0x562286c0ef68
SV = REGEXP(0x56228710d4a0) at 0x562286c0ef68
REFCNT = 1
FLAGS = (OBJECT,POK,FAKE,pPOK)
PV = 0x5622870c04d0 "(?^u:)"
CUR = 6
LEN = 0
STASH = 0x562285ffd230 "Regexp"
COMPFLAGS = 0x100 ()
EXTFLAGS = 0x80000100 (NULL)
ENGINE = 0x7f817615d380 (STANDARD)
INTFLAGS = 0x0 ()
NPARENS = 0
LASTPAREN = 0
LASTCLOSEPAREN = 0
MINLEN = 0
MINLENRET = 0
GOFS = 0
PRE_PREFIX = 5
SUBLEN = 0
SUBOFFSET = 0
SUBCOFFSET = 0
SUBBEG = 0x0
MOTHER_RE = 0x562287123810
SV = REGEXP(0x56228710d3e0) at 0x562287123810
REFCNT = 2
FLAGS = (POK,pPOK)
PV = 0x5622870c04d0 "(?^u:)"
CUR = 6
LEN = 10
COMPFLAGS = 0x100 ()
EXTFLAGS = 0x80000100 (NULL)
ENGINE = 0x7f817615d380 (STANDARD)
INTFLAGS = 0x0 ()
NPARENS = 0
LASTPAREN = 0
LASTCLOSEPAREN = 0
MINLEN = 0
MINLENRET = 0
GOFS = 0
PRE_PREFIX = 5
SUBLEN = 0
SUBOFFSET = 0
SUBCOFFSET = 0
SUBBEG = 0x0
MOTHER_RE = 0x0
PAREN_NAMES = 0x0
SUBSTRS = 0x562287042a00
PPRIVATE = 0x562287130bb0
OFFS = 0x5622870153f0
QR_ANONCV = 0x0
SAVED_COPY = 0x0
PAREN_NAMES = 0x0
SUBSTRS = 0x562287042970
PPRIVATE = 0x562287130bb0
OFFS = 0x562287095090
QR_ANONCV = 0x0
SAVED_COPY = 0x0
Elt No. 2
SV = PVNV(0x5622870402a0) at 0x562286bfdd00
REFCNT = 1
FLAGS = (POK,IsCOW,pPOK)
IV = 0
NV = 0
PV = 0x562287130eb0 "Application"\0
CUR = 11
LEN = 13
COW_REFCNT = 3
# 2-й проход:
SV = IV(0x56228695d7f8) at 0x56228695d808
REFCNT = 1
FLAGS = (ROK)
RV = 0x562287058898
SV = PVAV(0x562287139a90) at 0x562287058898
REFCNT = 1
FLAGS = ()
ARRAY = 0x5622871cb700
FILL = 2
MAX = 3
FLAGS = (REAL)
Elt No. 0
SV = PV(0x56228713b690) at 0x56228712e378
REFCNT = 1
FLAGS = (POK,pPOK)
PV = 0x562287113620 ""\0
CUR = 0
LEN = 10
Elt No. 1
SV = IV(0x5622871c7be0) at 0x5622871c7bf0
REFCNT = 1
FLAGS = (ROK)
RV = 0x5622871419b8
SV = REGEXP(0x56228710d860) at 0x5622871419b8
REFCNT = 1
FLAGS = (OBJECT,POK,FAKE,pPOK)
PV = 0x5622870c04d0 "(?^u:)"
CUR = 6
LEN = 0
STASH = 0x562285ffd230 "Regexp"
COMPFLAGS = 0x100 ()
EXTFLAGS = 0x80000100 (NULL)
ENGINE = 0x7f817615d380 (STANDARD)
INTFLAGS = 0x0 ()
NPARENS = 0
LASTPAREN = 0
LASTCLOSEPAREN = 0
MINLEN = 0
MINLENRET = 0
GOFS = 0
PRE_PREFIX = 5
SUBLEN = 0
SUBOFFSET = 0
SUBCOFFSET = 0
SUBBEG = 0x0
MOTHER_RE = 0x562287123810
SV = REGEXP(0x56228710d3e0) at 0x562287123810
REFCNT = 2
FLAGS = (POK,pPOK)
PV = 0x5622870c04d0 "(?^u:)"
CUR = 6
LEN = 10
COMPFLAGS = 0x100 ()
EXTFLAGS = 0x80000100 (NULL)
ENGINE = 0x7f817615d380 (STANDARD)
INTFLAGS = 0x0 ()
NPARENS = 0
LASTPAREN = 0
LASTCLOSEPAREN = 0
MINLEN = 0
MINLENRET = 0
GOFS = 0
PRE_PREFIX = 5
SUBLEN = 0
SUBOFFSET = 0
SUBCOFFSET = 0
SUBBEG = 0x0
MOTHER_RE = 0x0
PAREN_NAMES = 0x0
SUBSTRS = 0x562287042a00
PPRIVATE = 0x562287130bb0
OFFS = 0x5622870153f0
QR_ANONCV = 0x0
SAVED_COPY = 0x0
PAREN_NAMES = 0x0
SUBSTRS = 0x5622871b0af0
PPRIVATE = 0x562287130bb0
OFFS = 0x562287093f70
QR_ANONCV = 0x0
SAVED_COPY = 0x0
Elt No. 2
SV = PVNV(0x562287040580) at 0x5622871cdc18
REFCNT = 1
FLAGS = (POK,IsCOW,pPOK)
IV = 0
NV = 0
PV = 0x562286fe39d0 "Application"\0
CUR = 11
LEN = 13
COW_REFCNT = 3
meld показал что различия только в указателях на память, флаги у значений оказались одинаковыми:
«В таком случае дело в какой-то внутренней переменной perl-а, которая влияет на поведение пустой регулярки», решил я.
Я уж думал поднять исходники perl-а, но для начала загуглил. Оказалось, что данное поведение регулярок описано в perlop. А именно у оператора матчинга (=~
): оказывается, при использовании регулярка запоминается perl-ом, а пустая регулярка матчит как предыдущая. Что видно на следующем примере:
$x = "abc";
$y = "xby";
$x =~ /b/; print $`; # -> a
$y =~ //; print $`; # -> x
Причём предыдущая регулярка может быть использована где угодно:
sub f1 { $_[0] =~ /b/; print $`; }
sub f2 { f1(@_) }
$x = "abc";
$y = "xby";
f2($x); # -> a
$y =~ //; print $`; # -> x
То есть между двумя проходами отбора элементов списка где-то была использована регулярка, которая запомнилась и была использована пустой регуляркой. Проблема в том, что пустая это регулярка или нет perl определяет не на стадии компилляции. То есть он вначале интерполирует регулярку, а потом уже судит:
$x = "abc";
$y = "xby";
$x =~ /b/; print $`; # -> a
my $re = ""; $y =~ /$re/; print $`; # -> x
Но если переменная $re
не пустая:
$x = "abc";
$y = "xby";
$x =~ /b/; print $`; # -> a
my $re = "y"; $y =~ /$re/; print $`; # -> xb
В perlop предлагается ставить ()
при использовании с переменной которая может быть пустой:
$x = "abc";
$_ = "xby";
$x =~ /b/;
my $re = ""; $y =~ /()$re/; print $`; # ->
print $'; # -> xby
Из особенностей пустой регулярки ещё нужно сказать о том, что она использует предыдущую регулярку не только в матчинге, но и при замене и не работает так в split
(а просто бъёт строку на символы):
$x = "abc";
$_ = "xby";
$x =~ /b/;
s//*/;
print; # -> x*y
$, = ", "; print split //, "abc"; # -> a, b, c
И ещё, что с эскейпингом /\Q$re\E/
она тоже считается пустой:
$x = "abc";
$y = "xby";
$x =~ /b/; print $`; # -> a
my $re = ""; $y =~ /\Q$re\E/; print $`; # -> x
perl таит в себе множество тайн и когда уже думаешь, что знаешь о нём всё, оказывается, что ты лишь в начале долгого и увлекательного путешествия в мир кратких выражений.
Список использованной литературы
Пустой паттерн / https://perldoc.perl.org/perlop#The-empty-pattern-//
Проблема интерполяции паттерна / https://raku.org/archive/rfc/144.html
Повторяющиеся шаблоны поиска подстроки нулевой длины / https://metacpan.org/dist/POD2-RU/view/lib/POD2/RU/perlre.pod#Повторяющиеся-шаблоны-поиска-подстроки-нулевой-длины