오 예 당신은 정규 표현식을 사용하여 HTML을 파싱 할 수 있습니다 !
시도중인 작업에 대해 정규 표현식은 완벽하게 좋습니다 !
이다 대부분의 사람들이 정규 표현식 구문 분석 HTML의 어려움을 과소 평가하기 때문에 너무 가난 할 것이 사실.
그러나 이것은 계산 이론과 관련된 근본적인 결함이 아닙니다. 그 어리 석음이 여기 주변에 많이 파묻혀 있지만 믿지 마십시오.
그것은 확실하게 할 수있는 반면 그래서 (본 게시물이 논쟁의 여지가 사실의 존재 증명의 역할), 즉 의미하지 않는다 해야 합니다.
정규 표현식에서 전용의 특수 목적 HTML 파서에 얼마를 쓰는지 결정해야합니다. 대부분의 사람들은 그렇지 않습니다.
그러나 나는 생각합니다. ☻
일반 정규식 기반 HTML 파싱 솔루션
먼저 임의의 HTML을 정규식 으로 구문 분석하는 것이 얼마나 쉬운 지 보여 드리겠습니다 . 전체 프로그램은이 게시물의 끝에 있지만 파서의 핵심은 다음과 같습니다.
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);
}
}
}
얼마나 쉬운 지보십시오 보십니까?
작성된대로 각 HTML을 식별하고 해당 부분을 찾은 위치를 알려줍니다. 주어진 유형의 조각으로 또는 다른 것보다 더 특정한 유형으로 원하는 다른 작업을 수행하도록 쉽게 수정할 수 있습니다.
나는 실패한 테스트 사례가 없다 (왼쪽 :) : 100,000 개가 넘는 HTML 파일에서이 코드를 성공적으로 실행했다. 그 외에도, 특별히 구성된 파일에서 실행했습니다. 순진한 파서를 깨뜨리기 위해 .
이것은 아닙니다 순진한 파서 .
오, 나는 그것이 완벽하지 않다고 확신하지만, 아직 그것을 깨뜨리지 못했습니다. 나는 무언가가 있었더라도 프로그램의 명확한 구조 때문에 수정이 쉽게 적용 될 수 있다고 생각합니다. 정규 표현식이 많은 프로그램조차도 구조가 있어야합니다.
이제 그 길을 벗어 났으므로 OP의 질문에 대해 설명하겠습니다.
정규식을 사용하여 OP 작업 해결 데모
html_input_rx
아래에 포함 된 작은 프로그램은 다음과 같은 출력을 생성하므로 정규 표현식으로 HTML을 구문 분석하면 원하는 작업에 적합하다는 것을 알 수 있습니다.
% 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"
입력 태그 구문 분석, 악의없는 입력 참조
위의 출력을 생성 한 프로그램의 소스는 다음과 같습니다.
#!/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: $!";
}
당신은 간다! 아무것도 아니야! :)
오직 당신 정규 표현식에와 당신의 기술이 특정 구문 분석 작업까지인지를 판단 할 수 있습니다. 모든 사람의 기술 수준은 다르며 모든 새로운 작업은 다릅니다. 잘 정의 된 입력 세트가있는 작업의 경우, 정규식이 올바른 선택입니다. 처리 할 HTML의 제한된 서브 세트가있을 때 일부를 조합하는 것이 쉽지 않기 때문입니다. 정규식 초보자조차도 정규식을 사용하여 이러한 작업을 처리해야합니다. 다른 것은 과잉입니다.
그러나 HTML이 잘 못 박 히기 시작하면 예측할 수 없지만 완벽하게 합법적 인 방식으로 시작하기 시작하면 더 많은 종류의 것들이나 더 복잡한 종속성을 일치시켜야하면 결국에는 구문 분석 클래스를 사용하는 것보다 정규 표현식을 사용하는 솔루션을 적용하려면 더 열심히 노력해야합니다. 그 손익 분기점이 떨어지는 곳은 다시 정규 표현식을 사용하는 편안함 수준에 달려 있습니다.
그래서 내가 무엇을해야하니?
당신이 해야 할 일이나 할 수없는 일 을 말하지 않겠습니다 . 나는 그것이 틀렸다고 생각한다. 나는 단지 당신에게 가능성을 제시하고 싶습니다, 당신의 눈을 조금여십시오. 당신은 당신이하고 싶은 일과 원하는 방법을 선택할 수 있습니다. 절대적인 것은 없으며, 자신과 마찬가지로 자신의 상황을 아는 사람은 없습니다. 무언가가 너무 많은 것처럼 보인다면 아마도 그럴 것입니다. 프로그래밍은 재미 있어야합니다 . 그렇지 않으면 잘못했을 수 있습니다.
하나 html_input_rx
의 유효한 방법으로 내 프로그램을 볼 수 있습니다 . 그중 하나는 실제로 정규 표현식으로 HTML을 구문 분석 할 수 있다는 것 입니다. 그러나 다른 하나는 그것이 거의 모든 사람이 생각하는 것보다 훨씬, 훨씬, 훨씬 어렵다는 것입니다. 이것은 쉽게 내 프로그램은 당신이해야하는지에 대한 증거라는 결론으로 이어질 수 없는 정말 너무 어렵 기 때문에, 할.
나는 그것에 동의하지 않습니다. 확실히 내 프로그램에서 내가하는 모든 일이 약간의 연구 후에 이해가되지 않는다면, 이런 종류의 작업에 정규식을 사용하려고 시도해서는 안됩니다. 특정 HTML의 경우 정규 표현식은 훌륭하지만 일반적인 HTML의 경우 광기와 관련이 있습니다. 구문 분석 클래스를 항상 사용합니다. 특히 HTML 인 경우 직접 생성하지 않습니다.
에 대한 정규 표현식에 최적 작은 HTML 구문 분석 문제, 큰 사람을위한 pessimal
내 프로그램이 일반적인 HTML을 파싱하기 위해 정규 표현식을 사용 하지 않아야 하는 이유를 설명하는 경우에도 마찬가지입니다. 읽을 수없고, 구조화되지 않았으며, 유지할 수없는 패턴을 작성하는 불쾌하고 불쾌한 습관.
패턴은 추악 할 필요가 없으며 단단하지 않아도됩니다. 못생긴 패턴을 만들면 패턴이 아니라 자신을 반영합니다.
놀랍도록 절묘한 정규식 언어
귀하의 문제에 대한 나의 바람직한 해결책이 Perl로 작성되었음을 알려달라고 요청 받았습니다. 너 놀랐 니? 눈치 채지 못했습니까? 이 계시는 폭탄입니까?
다른 툴과 프로그래밍 언어가 Perl처럼 정규 표현식에있어 편리하고 표현력이 뛰어나고 강력하지는 않습니다. 큰 스펙트럼이 있으며 일부는 다른 것보다 더 적합합니다. 일반적으로 라이브러리 대신 핵심 언어의 일부로 정규 표현식을 표현한 언어는 작업하기가 더 쉽습니다. C를 사용하는 경우 프로그램을 다르게 구성하더라도 PCRE와 같이 할 수없는 정규 표현식으로 아무것도하지 않았습니다.
결국 Perl이 이제 정규식이라는 관점에서 다른 언어를 따라 잡을 수 있습니다. 나는 이것이 Perl이 시작되었을 때 펄의 정규식과 같은 것을 가진 사람이 없기 때문에 이것을 말한다. 당신이 좋아하는 것을 말하지만, 이것은 Perl이 분명히 얻은 곳입니다. 모든 사람들은 Perl의 정규식을 개발 단계마다 다르지만 복사했습니다. Perl은 사용하는 도구 나 언어에 상관없이 오늘날 현대 패턴에 의존하는 거의 모든 것 (거의 전부는 아니지만 거의)을 개척했습니다. 결국 다른 사람들 은 따라 잡을 것입니다.
그러나 그들은 지금처럼 과거에 Perl이 있었던 곳까지만 따라 잡을 것입니다. 모든 것이 발전합니다. Perl이 이끄는 다른 것이 없다면 정규 표현식에서 다른 사람들이 따릅니다. 펄은 이제 다른 사람들이 펄이있는 곳을 따라 잡을 수 있을까요? 나도 몰라, 그러나 우리도 움직일 것입니다. 아마도 우리는 Perl₆의 제작 패턴 스타일에 더 가까울 것 입니다.
그런 종류의 것을 좋아하지만 Perl₅에서 사용하고 싶다면 Damian Conway의 멋진 Regexp :: Grammars 모듈에 관심이있을 것 입니다. 완전히 훌륭하고 내 프로그램에서 내가 한 일이 사람들이 공백이나 알파벳 식별자없이 함께 모은 패턴을 만드는 것처럼 원시적 인 것처럼 보입니다. 확인 해봐!
간단한 HTML Chunker
다음은이 게시물의 시작 부분에서 중심을 보여준 파서의 완전한 소스입니다.
나는 엄격히 테스트 된 파싱 클래스보다 이것을 사용해야한다고 제안 하지 않습니다 . 하지만 난 그냥 때문에 정규 표현식에와 그 누구도 할 수있는 구문 분석 HTML 척없는 사람들의 피곤 그들이 할 수 없습니다입니다. 당신은 분명히 할 수 있으며,이 프로그램은 그 주장의 증거입니다.
물론, 그것은 쉬운 일이 아닙니다, 그러나 그것은 이다 가능!
좋은 구문 분석 클래스는 어떤 존재하기 때문에 그렇게하려고 노력하는 것은 시간의 끔찍한 낭비입니다 해야 이 작업을 위해 사용합니다. 임의의 HTML 을 구문 분석하려는 사람들에게 정답 은 불가능하다는 것이 아닙니다 . 그것은 쉽고 해가없는 대답입니다. 정직하고 정직한 대답은 처음부터 파악하기에는 너무 귀찮기 때문에 시도하지 않아야한다는 것입니다. 그들은 완벽하게 잘 작동하는 바퀴를 되살리려 고 애 쓰지 말아야합니다.
반면, 예측 가능한 하위 집합에 속하는 HTML 은 정규 표현식으로 구문 분석하기가 매우 쉽습니다. 작은 문제, 장난감 문제, 아마도 쉬운 일이 없기 때문에 사람들이 사용하려고하는 것은 놀라운 일이 아닙니다. 그렇기 때문에 두 가지 작업 (특정 대 일반)을 구별하는 것이 매우 중요한 이유는 동일한 접근 방식을 요구하지 않기 때문입니다.
앞으로 HTML 및 정규 표현식에 대한 질문에 대한 공정하고 정직한 처리가 이루어지기를 바랍니다.
여기 내 HTML 어휘 분석기가있다. 유효성 검사 구문 분석을 시도하지 않습니다. 어휘 요소 만 식별합니다. HTML 파서보다 HTML 청커 로 생각할 수도 있습니다 . 깨진 HTML을 용서하지는 않지만 그 방향으로 약간의 여유를줍니다.
전체 HTML을 직접 구문 분석하지 않아도 (그리고 왜해야합니까? 해결 된 문제입니다!),이 프로그램에는 많은 사람들이 많이 배울 수 있다고 생각하는 멋진 정규식 비트가 많이 있습니다. 즐겨!
#!/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 }