Oh Evet HTML Ayrıştırma Regexes kullanabilirsiniz!
Yapmaya çalıştığınız görev için normal ifadeler gayet iyi!
İse çoğu kişi normal ifadeler ile ayrıştırma HTML zorluğunu hafife ve bu nedenle çok kötü yapmak doğrudur.
Ancak bu, hesaplama teorisiyle ilgili bazı temel kusur değildir. Bu saçmalık burada çok papağan , ama onlara inanmıyorsun.
Bu yüzden kesinlikle yapılabilir olsa da (bu ilan bu tartışılmaz gerçeğin bir varoluş kanıtıdır), bu olması gerektiği anlamına gelmez .
Normal ifadelerden özel, özel amaçlı bir HTML ayrıştırıcısının ne anlama geldiğini yazma görevine bağlı olup olmadığınıza kendiniz karar vermelisiniz. Çoğu insan değildir.
Ama ben değilim. ☻
Genel Normal İfade Tabanlı HTML Ayrıştırma Çözümleri
Önce rastgele HTML ile rastgele HTML ayrıştırmanın ne kadar kolay olduğunu göstereceğim . Programın tamamı bu yayının sonunda, ancak ayrıştırıcının kalbi:
for (;;) {
given ($html) {
last when (pos || 0) >= length;
printf "\@%d=", (pos || 0);
print "doctype " when / \G (?&doctype) $RX_SUBS /xgc;
print "cdata " when / \G (?&cdata) $RX_SUBS /xgc;
print "xml " when / \G (?&xml) $RX_SUBS /xgc;
print "xhook " when / \G (?&xhook) $RX_SUBS /xgc;
print "script " when / \G (?&script) $RX_SUBS /xgc;
print "style " when / \G (?&style) $RX_SUBS /xgc;
print "comment " when / \G (?&comment) $RX_SUBS /xgc;
print "tag " when / \G (?&tag) $RX_SUBS /xgc;
print "untag " when / \G (?&untag) $RX_SUBS /xgc;
print "nasty " when / \G (?&nasty) $RX_SUBS /xgc;
print "text " when / \G (?&nontag) $RX_SUBS /xgc;
default {
die "UNCLASSIFIED: " .
substr($_, pos || 0, (length > 65) ? 65 : length);
}
}
}
Okumanın ne kadar kolay olduğunu görüyor musunuz?
Yazıldığı gibi, her HTML parçasını tanımlar ve parçayı nerede bulduğunu söyler. Herhangi bir parça türüyle veya bunlardan daha belirli türler için istediğinizi yapmak için kolayca değiştirebilirsiniz.
Başarısız test durumlarım yok (sol :): Bu kodu 100.000'den fazla HTML dosyasında başarıyla çalıştırdım - hızlı ve kolay bir şekilde ellerimi alabildiğim her biri. Bunların ötesinde, saf ayrıştırıcıları kırmak için özel olarak oluşturulmuş dosyalar üzerinde de çalıştırıyorum .
Bu saf bir ayrıştırıcı değil .
Oh, eminim mükemmel değil, ama henüz kırmayı başaramadım. Bir şey yapsa bile, programın net yapısı nedeniyle düzeltmenin kolayca sığacağını düşünüyorum. Regex-heavy programlarında bile yapı bulunmalıdır.
Şimdi bu yoldan çekildiğine göre, OP'nin sorusunu ele alalım.
Regexes Kullanarak OP'nin Görevini Çözme Demosu
html_input_rx
Aşağıda eklediğim küçük program aşağıdaki çıktıyı üretir, böylece HTML'yi regexes ile ayrıştırma yapmak istediğiniz şey için iyi çalışır:
% html_input_rx Amazon.com-_Online_Shopping_for_Electronics,_Apparel,_Computers,_Books,_DVDs_\&_more.htm
input tag #1 at character 9955:
class => "searchSelect"
id => "twotabsearchtextbox"
name => "field-keywords"
size => "50"
style => "width:100%; background-color: #FFF;"
title => "Search for"
type => "text"
value => ""
input tag #2 at character 10335:
alt => "Go"
src => "http://g-ecx.images-amazon.com/images/G/01/x-locale/common/transparent-pixel._V192234675_.gif"
type => "image"
Giriş Etiketlerini Ayrıştırın, Bkz. Kötü Giriş Yok
Yukarıdaki çıktıyı üreten programın kaynağı.
#!/usr/bin/env perl
#
# html_input_rx - pull out all <input> tags from (X)HTML src
# via simple regex processing
#
# Tom Christiansen <tchrist@perl.com>
# Sat Nov 20 10:17:31 MST 2010
#
################################################################
use 5.012;
use strict;
use autodie;
use warnings FATAL => "all";
use subs qw{
see_no_evil
parse_input_tags
input descape dequote
load_patterns
};
use open ":std",
IN => ":bytes",
OUT => ":utf8";
use Encode qw< encode decode >;
###########################################################
parse_input_tags
see_no_evil
input
###########################################################
until eof(); sub parse_input_tags {
my $_ = shift();
our($Input_Tag_Rx, $Pull_Attr_Rx);
my $count = 0;
while (/$Input_Tag_Rx/pig) {
my $input_tag = $+{TAG};
my $place = pos() - length ${^MATCH};
printf "input tag #%d at character %d:\n", ++$count, $place;
my %attr = ();
while ($input_tag =~ /$Pull_Attr_Rx/g) {
my ($name, $value) = @+{ qw< NAME VALUE > };
$value = dequote($value);
if (exists $attr{$name}) {
printf "Discarding dup attr value '%s' on %s attr\n",
$attr{$name} // "<undef>", $name;
}
$attr{$name} = $value;
}
for my $name (sort keys %attr) {
printf " %10s => ", $name;
my $value = descape $attr{$name};
my @Q; given ($value) {
@Q = qw[ " " ] when !/'/ && !/"/;
@Q = qw[ " " ] when /'/ && !/"/;
@Q = qw[ ' ' ] when !/'/ && /"/;
@Q = qw[ q( ) ] when /'/ && /"/;
default { die "NOTREACHED" }
}
say $Q[0], $value, $Q[1];
}
print "\n";
}
}
sub dequote {
my $_ = $_[0];
s{
(?<quote> ["'] )
(?<BODY>
(?s: (?! \k<quote> ) . ) *
)
\k<quote>
}{$+{BODY}}six;
return $_;
}
sub descape {
my $string = $_[0];
for my $_ ($string) {
s{
(?<! % )
% ( \p{Hex_Digit} {2} )
}{
chr hex $1;
}gsex;
s{
& \043
( [0-9]+ )
(?: ;
| (?= [^0-9] )
)
}{
chr $1;
}gsex;
s{
& \043 x
( \p{ASCII_HexDigit} + )
(?: ;
| (?= \P{ASCII_HexDigit} )
)
}{
chr hex $1;
}gsex;
}
return $string;
}
sub input {
our ($RX_SUBS, $Meta_Tag_Rx);
my $_ = do { local $/; <> };
my $encoding = "iso-8859-1"; # web default; wish we had the HTTP headers :(
while (/$Meta_Tag_Rx/gi) {
my $meta = $+{META};
next unless $meta =~ m{ $RX_SUBS
(?= http-equiv )
(?&name)
(?&equals)
(?= (?"e)? content-type )
(?&value)
}six;
next unless $meta =~ m{ $RX_SUBS
(?= content ) (?&name)
(?&equals)
(?<CONTENT> (?&value) )
}six;
next unless $+{CONTENT} =~ m{ $RX_SUBS
(?= charset ) (?&name)
(?&equals)
(?<CHARSET> (?&value) )
}six;
if (lc $encoding ne lc $+{CHARSET}) {
say "[RESETTING ENCODING $encoding => $+{CHARSET}]";
$encoding = $+{CHARSET};
}
}
return decode($encoding, $_);
}
sub see_no_evil {
my $_ = shift();
s{ <! DOCTYPE .*? > }{}sx;
s{ <! \[ CDATA \[ .*? \]\] > }{}gsx;
s{ <script> .*? </script> }{}gsix;
s{ <!-- .*? --> }{}gsx;
return $_;
}
sub load_patterns {
our $RX_SUBS = qr{ (?(DEFINE)
(?<nv_pair> (?&name) (?&equals) (?&value) )
(?<name> \b (?= \pL ) [\w\-] + (?<= \pL ) \b )
(?<equals> (?&might_white) = (?&might_white) )
(?<value> (?"ed_value) | (?&unquoted_value) )
(?<unwhite_chunk> (?: (?! > ) \S ) + )
(?<unquoted_value> [\w\-] * )
(?<might_white> \s * )
(?<quoted_value>
(?<quote> ["'] )
(?: (?! \k<quote> ) . ) *
\k<quote>
)
(?<start_tag> < (?&might_white) )
(?<end_tag>
(?&might_white)
(?: (?&html_end_tag)
| (?&xhtml_end_tag)
)
)
(?<html_end_tag> > )
(?<xhtml_end_tag> / > )
) }six;
our $Meta_Tag_Rx = qr{ $RX_SUBS
(?<META>
(?&start_tag) meta \b
(?:
(?&might_white) (?&nv_pair)
) +
(?&end_tag)
)
}six;
our $Pull_Attr_Rx = qr{ $RX_SUBS
(?<NAME> (?&name) )
(?&equals)
(?<VALUE> (?&value) )
}six;
our $Input_Tag_Rx = qr{ $RX_SUBS
(?<TAG> (?&input_tag) )
(?(DEFINE)
(?<input_tag>
(?&start_tag)
input
(?&might_white)
(?&attributes)
(?&might_white)
(?&end_tag)
)
(?<attributes>
(?:
(?&might_white)
(?&one_attribute)
) *
)
(?<one_attribute>
\b
(?&legal_attribute)
(?&might_white) = (?&might_white)
(?:
(?"ed_value)
| (?&unquoted_value)
)
)
(?<legal_attribute>
(?: (?&optional_attribute)
| (?&standard_attribute)
| (?&event_attribute)
# for LEGAL parse only, comment out next line
| (?&illegal_attribute)
)
)
(?<illegal_attribute> (?&name) )
(?<required_attribute> (?#no required attributes) )
(?<optional_attribute>
(?&permitted_attribute)
| (?&deprecated_attribute)
)
# NB: The white space in string literals
# below DOES NOT COUNT! It's just
# there for legibility.
(?<permitted_attribute>
accept
| alt
| bottom
| check box
| checked
| disabled
| file
| hidden
| image
| max length
| middle
| name
| password
| radio
| read only
| reset
| right
| size
| src
| submit
| text
| top
| type
| value
)
(?<deprecated_attribute>
align
)
(?<standard_attribute>
access key
| class
| dir
| ltr
| id
| lang
| style
| tab index
| title
| xml:lang
)
(?<event_attribute>
on blur
| on change
| on click
| on dbl click
| on focus
| on mouse down
| on mouse move
| on mouse out
| on mouse over
| on mouse up
| on key down
| on key press
| on key up
| on select
)
)
}six;
}
UNITCHECK {
load_patterns();
}
END {
close(STDOUT)
|| die "can't close stdout: $!";
}
İşte böyle! Hiçbir şey! :)
Regexes ile olan yeteneğinizin belirli bir ayrıştırma görevine bağlı olup olmadığını sadece siz değerlendirebilirsiniz. Herkesin beceri düzeyi farklıdır ve her yeni görev farklıdır. İyi tanımlanmış bir giriş kümesine sahip olduğunuz işler için, normal ifadeler açık bir şekilde doğru seçimdir, çünkü başa çıkmak için kısıtlanmış bir HTML alt kümeniz olduğunda bazılarını bir araya getirmek önemsizdir. Regex yeni başlayanlar bile bu işleri regexes ile ele almalıdır. Başka her şey aşırı.
Bununla birlikte , HTML daha az çivilenmeye başladığında, tahmin edemeyeceğiniz, ancak tamamen yasal olan yollarla çarpmaya başladıktan sonra, daha farklı şeyler veya daha karmaşık bağımlılıklar ile eşleşmeniz gerektiğinde, sonunda bir noktaya ulaşacaksınız. regexes kullanan bir çözümü uygulamak için ayrıştırıcı sınıf kullanmak zorunda olduğunuzdan daha fazla çalışmanız gerekir. Başabaş noktasının nereye düştüğü, regexes ile kendi konfor seviyenize tekrar bağlıdır.
Peki ne yapmalıyım?
Sana ne diyeceğim gitmiyorum gerekir yapmak ya ne edemez yapmak. Bence bu yanlış. Size sadece olasılıklar sunmak, gözlerinizi biraz açmak istiyorum. Ne yapmak istediğinizi ve nasıl yapmak istediğinizi seçebilirsiniz. Mutlaklık yoktur - ve hiç kimse kendi durumunuzu kendiniz kadar iyi bilmez. Bir şey çok fazla iş gibi görünüyorsa, belki de öyle. Programlama eğlenceli olmalı . Değilse, yanlış yapıyor olabilirsiniz.
html_input_rx
Programıma herhangi bir sayıda geçerli şekilde bakılabilir . Bir şekilde sen gerçekten de can düzenli ifadeler ile ayrıştırma HTML. Ama bir diğeri, neredeyse herkesin sandığından çok, çok, çok daha zor olmasıdır. Bu kolayca benim program gereken bir kanıtıdır olduğu sonucuna yol açabilir değil gerçekten çok zor, çünkü yok.
Buna katılmayacağım. Kesinlikle programımda yaptığım her şey bir çalışmadan sonra sizin için bir anlam ifade etmiyorsa, bu tür bir görev için normal ifadeleri kullanmaya çalışmamalısınız. Belirli HTML için normal ifadeler mükemmeldir, ancak genel HTML için delilikle eşdeğerdir. Her zaman ayrıştırma sınıfları kullanıyorum, özellikle HTML ise kendimi oluşturmadım.
Küçük HTML ayrıştırma sorunları için en uygun regexes, büyük olanlar için soluk
Benim program neden açıklayıcı olarak alınmıştır bile değil ☺ Ben biraz geliyordu çünkü o olmak için, Tamam - - Genel HTML ayrıştırma için Regexes kullanmak daha fazla insan korkunç ortak kırmak yüzden yine de bir göz açıcı olmalıdır ve okunamayan, yapılandırılmamış ve sürdürülemez kalıplar yazma gibi kötü ve kötü alışkanlıklar
Desenler çirkin olmak zorunda değildir ve zor olmak zorunda değildir. Eğer çirkin desenler yaratırsanız, bu onlar üzerinde değil, size bir yansımadır.
Olağanüstü Mükemmel Regex Dili
Soruna yönelik güçlü çözümümün Perl'de yazıldığını belirtmem istendi. Şaşırdın mı? Fark etmedin mi? Bu vahiy bomba mı?
Diğer tüm araçların ve programlama dillerinin, Perl gibi regexes söz konusu olduğunda oldukça kullanışlı, etkileyici ve güçlü olmadığı doğrudur. Orada büyük bir spektrum var, bazıları diğerlerinden daha uygun. Genel olarak, kütüphane yerine temel dilin bir parçası olarak normal ifadeleri ifade eden dillerle çalışmak daha kolaydır. PCRE'de yapamayacağınız regexes ile hiçbir şey yapmadım, ancak C'yi kullanıyorsanız programı farklı yapılandıracaksınız.
Sonunda diğer diller, Perl'in şu anki regexes olduğu yere yetişecek. Bunu söylüyorum çünkü Perl başladığında Perl'in ifadelerine benzeyen başka kimse yoktu. Beğendiğiniz bir şey söyleyin, ancak Perl açıkça kazandı: herkes Perl'in regex'lerini gelişimlerinin çeşitli aşamalarında da kopyaladı. Perl, hangi aracı veya dili kullanırsanız kullanın, bugün modern desenlere güvendiğiniz neredeyse her şeye (neredeyse hepsi değil, neredeyse) her şeye öncülük etti. Yani sonuçta başkaları olacaktır yakalamak.
Ancak Perl'in geçmişte olduğu gibi, şimdi olduğu gibi yetişecekler. Her şey ilerler. Regexes'te Perl'in yol açtığı başka bir şey yoksa diğerleri izler. Herkes sonunda Perl'in nerede olduğunu yakaladığında Perl nerede olacak? Hiçbir fikrim yok, ama biz de taşınacağımızı biliyorum. Muhtemelen Perl₆'nun işçiliği modellerine daha yakın olacağız .
Bu tür bir şeyden hoşlanıyorsanız, ancak Perl₅'de kullanmak istiyorsanız, Damian Conway'in harika Regexp :: Grammars modülüyle ilgilenebilirsiniz . Tamamen harika ve programımda burada yaptığım şey, benimki gibi insanların beyaz boşluk veya alfabetik tanımlayıcılar olmadan bir araya getirdikleri desenleri yaptığı gibi ilkel görünüyor. Bunu kontrol et!
Basit HTML Parçalayıcı
İşte bu yazının başlangıcından itibaren merkez parçasını gösterdiğim ayrıştırıcıya tam kaynak.
Bunu , titizlikle test edilmiş bir ayrıştırma sınıfında kullanmanız gerektiğini söylemiyorum . Ama sırf regexes o kimse can ayrıştırma HTML büründüğü yorgun am onlar olamaz. Açıkça yapabilirsiniz ve bu program bu iddianın kanıtıdır.
Tabii, bu kolay değil, ama o olduğunu mümkün!
Ve bunu yapmaya çalışmak korkunç bir zaman kaybıdır, çünkü bu görev için kullanmanız gereken iyi ayrıştırma sınıfları vardır . Ayrıştırma çalışıyorum insanlara doğru cevap keyfi HTML değil imkansız olduğunu. Bu kolay ve gösterişsiz bir cevap. Doğru ve dürüst cevap, bunu denememeleri gerektiğidir, çünkü sıfırdan anlamak çok zahmetlidir; mükemmel çalışan bir tekerleği yeniden icat etmek için çaba sarf etmemeliler.
Öte yandan, öngörülebilir bir alt kümeye giren HTML'nin normal ifadelerle ayrıştırılması son derece kolaydır. İnsanların bunları kullanmaya çalışması şaşırtıcı değil, çünkü küçük problemler için oyuncak problemleri belki de hiçbir şey daha kolay olamazdı. Bu nedenle, aynı yaklaşımı zorunlu kılmadıkları için iki görevi - spesifik ve genel - birbirinden ayırmak çok önemlidir.
Gelecekte HTML ve normal ifadelerle ilgili soruların daha adil ve dürüst bir şekilde ele alınmasını umuyorum.
İşte benim HTML lexer'ım. Doğrulayıcı bir ayrıştırma yapmaya çalışmaz; sadece sözcüksel unsurları tanımlar. Bir HTML ayrıştırıcısından daha çok bir HTML parçası olarak düşünebilirsiniz . Bu yönde çok küçük izinler almasına rağmen, bozuk HTML'yi affetmez.
Tam HTML'yi kendiniz ayrıştırmasanız bile (ve neden çözmelisiniz? Bu çözülmüş bir sorundur!), Bu programın birçok insanın çok şey öğrenebileceğine inanıyorum. Zevk almak!
#!/usr/bin/env perl
#
# chunk_HTML - a regex-based HTML chunker
#
# Tom Christiansen <tchrist@perl.com
# Sun Nov 21 19:16:02 MST 2010
########################################
use 5.012;
use strict;
use autodie;
use warnings qw< FATAL all >;
use open qw< IN :bytes OUT :utf8 :std >;
MAIN: {
$| = 1;
lex_html(my $page = slurpy());
exit();
}
########################################################################
sub lex_html {
our $RX_SUBS; ###############
my $html = shift(); # Am I... #
for (;;) { # forgiven? :)#
given ($html) { ###############
last when (pos || 0) >= length;
printf "\@%d=", (pos || 0);
print "doctype " when / \G (?&doctype) $RX_SUBS /xgc;
print "cdata " when / \G (?&cdata) $RX_SUBS /xgc;
print "xml " when / \G (?&xml) $RX_SUBS /xgc;
print "xhook " when / \G (?&xhook) $RX_SUBS /xgc;
print "script " when / \G (?&script) $RX_SUBS /xgc;
print "style " when / \G (?&style) $RX_SUBS /xgc;
print "comment " when / \G (?&comment) $RX_SUBS /xgc;
print "tag " when / \G (?&tag) $RX_SUBS /xgc;
print "untag " when / \G (?&untag) $RX_SUBS /xgc;
print "nasty " when / \G (?&nasty) $RX_SUBS /xgc;
print "text " when / \G (?&nontag) $RX_SUBS /xgc;
default {
die "UNCLASSIFIED: " .
substr($_, pos || 0, (length > 65) ? 65 : length);
}
}
}
say ".";
}
#####################
# Return correctly decoded contents of next complete
# file slurped in from the <ARGV> stream.
#
sub slurpy {
our ($RX_SUBS, $Meta_Tag_Rx);
my $_ = do { local $/; <ARGV> }; # read all input
return unless length;
use Encode qw< decode >;
my $bom = "";
given ($_) {
$bom = "UTF-32LE" when / ^ \xFf \xFe \0 \0 /x; # LE
$bom = "UTF-32BE" when / ^ \0 \0 \xFe \xFf /x; # BE
$bom = "UTF-16LE" when / ^ \xFf \xFe /x; # le
$bom = "UTF-16BE" when / ^ \xFe \xFf /x; # be
$bom = "UTF-8" when / ^ \xEF \xBB \xBF /x; # st00pid
}
if ($bom) {
say "[BOM $bom]";
s/^...// if $bom eq "UTF-8"; # st00pid
# Must use UTF-(16|32) w/o -[BL]E to strip BOM.
$bom =~ s/-[LB]E//;
return decode($bom, $_);
# if BOM found, don't fall through to look
# for embedded encoding spec
}
# Latin1 is web default if not otherwise specified.
# No way to do this correctly if it was overridden
# in the HTTP header, since we assume stream contains
# HTML only, not also the HTTP header.
my $encoding = "iso-8859-1";
while (/ (?&xml) $RX_SUBS /pgx) {
my $xml = ${^MATCH};
next unless $xml =~ m{ $RX_SUBS
(?= encoding ) (?&name)
(?&equals)
(?"e) ?
(?<ENCODING> (?&value) )
}sx;
if (lc $encoding ne lc $+{ENCODING}) {
say "[XML ENCODING $encoding => $+{ENCODING}]";
$encoding = $+{ENCODING};
}
}
while (/$Meta_Tag_Rx/gi) {
my $meta = $+{META};
next unless $meta =~ m{ $RX_SUBS
(?= http-equiv ) (?&name)
(?&equals)
(?= (?"e)? content-type )
(?&value)
}six;
next unless $meta =~ m{ $RX_SUBS
(?= content ) (?&name)
(?&equals)
(?<CONTENT> (?&value) )
}six;
next unless $+{CONTENT} =~ m{ $RX_SUBS
(?= charset ) (?&name)
(?&equals)
(?<CHARSET> (?&value) )
}six;
if (lc $encoding ne lc $+{CHARSET}) {
say "[HTTP-EQUIV ENCODING $encoding => $+{CHARSET}]";
$encoding = $+{CHARSET};
}
}
return decode($encoding, $_);
}
########################################################################
# Make sure to this function is called
# as soon as source unit has been compiled.
UNITCHECK { load_rxsubs() }
# useful regex subroutines for HTML parsing
sub load_rxsubs {
our $RX_SUBS = qr{
(?(DEFINE)
(?<WS> \s * )
(?<any_nv_pair> (?&name) (?&equals) (?&value) )
(?<name> \b (?= \pL ) [\w:\-] + \b )
(?<equals> (?&WS) = (?&WS) )
(?<value> (?"ed_value) | (?&unquoted_value) )
(?<unwhite_chunk> (?: (?! > ) \S ) + )
(?<unquoted_value> [\w:\-] * )
(?<any_quote> ["'] )
(?<quoted_value>
(?<quote> (?&any_quote) )
(?: (?! \k<quote> ) . ) *
\k<quote>
)
(?<start_tag> < (?&WS) )
(?<html_end_tag> > )
(?<xhtml_end_tag> / > )
(?<end_tag>
(?&WS)
(?: (?&html_end_tag)
| (?&xhtml_end_tag) )
)
(?<tag>
(?&start_tag)
(?&name)
(?:
(?&WS)
(?&any_nv_pair)
) *
(?&end_tag)
)
(?<untag> </ (?&name) > )
# starts like a tag, but has screwed up quotes inside it
(?<nasty>
(?&start_tag)
(?&name)
.*?
(?&end_tag)
)
(?<nontag> [^<] + )
(?<string> (?"ed_value) )
(?<word> (?&name) )
(?<doctype>
<!DOCTYPE
# please don't feed me nonHTML
### (?&WS) HTML
[^>]* >
)
(?<cdata> <!\[CDATA\[ .*? \]\] > )
(?<script> (?= <script ) (?&tag) .*? </script> )
(?<style> (?= <style ) (?&tag) .*? </style> )
(?<comment> <!-- .*? --> )
(?<xml>
< \? xml
(?:
(?&WS)
(?&any_nv_pair)
) *
(?&WS)
\? >
)
(?<xhook> < \? .*? \? > )
)
}six;
our $Meta_Tag_Rx = qr{ $RX_SUBS
(?<META>
(?&start_tag) meta \b
(?:
(?&WS) (?&any_nv_pair)
) +
(?&end_tag)
)
}six;
}
# nobody *ever* remembers to do this!
END { close STDOUT }