अरे हाँ आप HTML को पार्स करने के लिए Regexes का उपयोग कर सकते हैं !
जिस कार्य के लिए आप प्रयास कर रहे हैं, उसके लिए रेग्जेस पूरी तरह से ठीक हैं!
यह है सच है कि ज्यादातर लोगों को नियमित अभिव्यक्ति के साथ पार्स एचटीएमएल की कठिनाई बहुत मूल्यवान समझना और इसलिए इतनी खराब है।
लेकिन यह कम्प्यूटेशनल सिद्धांत से संबंधित कुछ मूलभूत दोष नहीं है। उस नीचता को इधर-उधर तोता है , लेकिन क्या आप उन पर विश्वास नहीं करते हैं।
तो जबकि यह निश्चित रूप से किया जा सकता है (यह पोस्टिंग इस असंगत तथ्य के अस्तित्व प्रमाण के रूप में कार्य करता है), इसका मतलब यह नहीं है कि यह होना चाहिए।
आपको अपने लिए यह तय करना होगा कि क्या आप यह लिखने के काम पर हैं कि किस राशि को समर्पित, विशेष उद्देश्य वाले HTML पार्सर को regexes से बाहर करना है। ज्यादातर लोग नहीं हैं।
लेकिन मैं हूं। ☻
जनरल रेगेक्स-आधारित 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 फ़ाइलों पर सफलतापूर्वक चलाया है - हर एक जिसे मैं जल्दी और आसानी से अपने हाथों पर प्राप्त कर सकता हूं। उन के अलावा, मैंने इसे विशेष रूप से भोले परासरों को तोड़ने के लिए बनाई गई फाइलों पर भी चलाया है ।
यह कोई भोले तोता नहीं है।
ओह, मुझे यकीन है कि यह सही नहीं है, लेकिन मैं अभी तक इसे तोड़ने में कामयाब नहीं हुआ हूं। मुझे लगता है कि अगर कुछ किया भी, तो प्रोग्राम की स्पष्ट संरचना के कारण फिक्स करना आसान होगा। यहां तक कि रेगेक्स-भारी कार्यक्रमों में भी स्थिरता होनी चाहिए।
अब जब कि रास्ते से बाहर हो गया है, तो मुझे ओपी के प्रश्न को संबोधित करने दें।
ओपी के टास्क को सॉल्व करने के लिए सॉल्व करने का डेमो
html_input_rx
नीचे दिया गया छोटा सा प्रोग्राम मैं निम्न आउटपुट का उत्पादन करता हूं, ताकि आप देख सकें कि regexes के साथ 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: $!";
}
तुम वहाँ जाओ! इसको कुछ नहीं! :)
केवल आप ही अंदाजा लगा सकते हैं कि रिजेक्स के साथ आपका कौशल किसी विशेष पार्सिंग कार्य पर निर्भर है। हर किसी के कौशल का स्तर अलग है, और हर नया कार्य अलग है। उन नौकरियों के लिए जहां आपके पास एक अच्छी तरह से परिभाषित इनपुट सेट है, regexes स्पष्ट रूप से सही विकल्प है, क्योंकि इससे निपटने के लिए आपके पास HTML का प्रतिबंधित सबसेट होने पर कुछ एक साथ रखना तुच्छ है। यहां तक कि रेगेक्स शुरुआती को रेगेक्स के साथ उन नौकरियों को संभालना चाहिए। और कुछ भी overkill है।
हालाँकि , एक बार जब HTML कम से कम बंद होना शुरू हो जाता है, तो एक बार यह उन तरीकों से प्रभावी होने लगता है, जो आप भविष्यवाणी नहीं कर सकते हैं, लेकिन जो पूरी तरह से कानूनी हैं, एक बार जब आपको विभिन्न प्रकार की चीजों या अधिक जटिल निर्भरता के साथ मिलान करना होगा, तो आप अंततः एक बिंदु पर पहुंच जाएंगे। आपको उस समाधान को प्रभावित करने के लिए अधिक परिश्रम करना पड़ता है जो कि रेक्सक्स का उपयोग करता है, जबकि आपको एक पार्सिंग क्लास का उपयोग करना होगा। जहां ब्रेक-इवन पॉइंट फॉल्स फिर से रेगेक्स के साथ अपने आराम के स्तर पर निर्भर करता है।
तो मुझे क्या करना चाहिए?
मैं आपको यह नहीं बताने जा रहा हूं कि आपको क्या करना चाहिए या क्या नहीं करना चाहिए। मुझे लगता है कि यह गलत है। मैं आपको बस कब्जे के साथ पेश करना चाहता हूं, अपनी आंखें थोड़ी खोलें। आपको यह चुनना है कि आप क्या करना चाहते हैं और आप इसे कैसे करना चाहते हैं। कोई निरपेक्षता नहीं हैं - और कोई भी आपकी खुद की स्थिति के साथ-साथ खुद को भी नहीं जानता है। अगर कुछ ऐसा लगता है कि यह बहुत ज्यादा काम है, ठीक है, शायद यह है। प्रोग्रामिंग मजेदार होनी चाहिए , आप जानते हैं। यदि ऐसा नहीं है, तो आप इसे गलत कर सकते हैं।
html_input_rx
कोई भी किसी भी मान्य तरीके से मेरे कार्यक्रम को देख सकता है। ऐसा ही एक है कि आप वास्तव में नियमित अभिव्यक्ति के साथ HTML को पार्स कर सकते हैं । लेकिन एक और यह है कि यह बहुत अधिक है, बहुत कठिन है, लगभग किसी की तुलना में यह कभी भी सोचता है कि यह है। इससे आसानी से निष्कर्ष निकाला जा सकता है कि मेरा कार्यक्रम एक नियम है कि आपको क्या नहीं करना चाहिए , क्योंकि यह वास्तव में बहुत कठिन है।
मैं इससे सहमत नहीं हूँ। निश्चित रूप से अगर मैं अपने कार्यक्रम में जो कुछ करता हूं, वह कुछ अध्ययन के बाद आपके लिए समझ में नहीं आता है, तो आपको इस तरह के कार्य के लिए रेक्सक्स का उपयोग करने का प्रयास नहीं करना चाहिए। विशिष्ट HTML के लिए, रेगेक्स महान हैं, लेकिन जेनेरिक HTML के लिए, वे पागलपन के लिए तांत्रमाउंट कर रहे हैं। मैं हर समय पार्सिंग कक्षाओं का उपयोग करता हूं, खासकर यदि यह HTML है तो मैंने खुद को उत्पन्न नहीं किया है।
छोटे HTML पार्सिंग समस्याओं के लिए इष्टतम , बड़े वाले के लिए pessimal
यहां तक कि अगर मेरे कार्यक्रम को इस बात के रूप में लिया जाता है कि आपको सामान्य HTML को पार्स करने के लिए रीगेक्स का उपयोग क्यों नहीं करना चाहिए - जो कि ठीक है, क्योंकि मैं थोड़े के लिए इसका मतलब था कि ☺ - यह अभी भी एक आंख खोलने वाला होना चाहिए ताकि अधिक लोग बहुत आम तोड़ सकें और बुरा, अपठनीय, असंरचित और अचूक पैटर्न लिखने की गंदी आदत।
पैटर्न को बदसूरत होना नहीं है, और उन्हें कठिन होना नहीं है। यदि आप बदसूरत पैटर्न बनाते हैं, तो यह आप पर प्रतिबिंब है, न कि उन पर।
पूरी तरह से उत्तम रेगेक्स भाषा
मुझे यह बताने के लिए कहा गया है कि आपकी समस्या का मेरे द्वारा किया गया हल पर्ल में लिखा गया है। क्या आप आश्चर्यचकित हैं? आपने गौर नहीं किया? क्या यह रहस्योद्घाटन बमबारी है?
यह सच है कि अन्य सभी उपकरण और प्रोग्रामिंग भाषाएं पेरिफेन के रूप में रीजेक्स के लिए सुविधाजनक, अभिव्यंजक और शक्तिशाली नहीं हैं। वहाँ एक बड़ा स्पेक्ट्रम है, कुछ दूसरों की तुलना में अधिक उपयुक्त है। सामान्य तौर पर, जिन भाषाओं ने लाइब्रेरी के बजाय कोर भाषा के हिस्से के रूप में रीजेक्स व्यक्त किया है, उनके साथ काम करना आसान है। मैंने पीसीआरई के साथ कुछ भी ऐसा नहीं किया है, जिसमें आप ऐसा नहीं कर सकते, हालांकि, आप प्रोग्राम को अलग तरह से तैयार करेंगे, अगर आप सी के साथ थे।
अंततः अन्य भाषाओं को पकड़ लिया जाएगा जहां पर्ल अब रीगेक्स के संदर्भ में है। मैं यह इसलिए कहता हूं क्योंकि जब पर्ल शुरू हुआ था, तब किसी और के पास पर्ल के रेक्सक्स जैसा कुछ नहीं था। आप जैसा चाहें कुछ भी कहें, लेकिन यह वह जगह है जहां पर्ल ने स्पष्ट रूप से जीत हासिल की: हर किसी ने पर्ल के रेगेक्स को उनके विकास के अलग-अलग चरणों में कॉपी किया। पर्ल ने लगभग (न केवल काफी, बल्कि लगभग) सभी चीजों का बीड़ा उठाया है, जो कि आप आज आधुनिक पैटर्न पर भरोसा करने के लिए आए हैं, चाहे आप किसी भी टूल या भाषा का उपयोग करें। तो अंततः दूसरों को पकड़ लेंगे।
लेकिन वे केवल उसी जगह को पकड़ेंगे जहां पर्ल कुछ समय पहले था, जैसा कि अब है। सब कुछ एडवांस। यदि कुछ और नहीं, जहां पर्ल का नेतृत्व होता है, तो अन्य लोग भी इसका अनुसरण करते हैं। जब पर्ल एक बार फिर से सबके साथ हो जाएगा, तो पर्ल अब कहां है? मुझे कोई पता नहीं है, लेकिन मुझे पता है कि हम भी चले गए होंगे। संभवतः हम पर्लिंग शैली के पैटर्न के करीब होंगे ।
यदि आप उस तरह की चीज को पसंद करते हैं, लेकिन इसे पर्ल में इस्तेमाल करना चाहते हैं, तो आप दामियन कॉनवे के अद्भुत रेगेक्सपी :: ग्रामर मॉड्यूल में दिलचस्पी ले सकते हैं । यह पूरी तरह से भयानक है, और जो मैंने अपने कार्यक्रम में यहां किया है वह ऐसा लगता है जैसे कि मेरा आदिम है जैसा कि पैटर्न है कि लोग बिना व्हाट्सएप या अल्फाबेटिक आइडेंटिफ़ायर के एक साथ रटना करते हैं। इसकी जांच - पड़ताल करें!
सिंपल HTML Chunker
यहाँ पार्सर का पूरा स्रोत है जो मैंने इस पोस्टिंग की शुरुआत से केंद्रपीठ को दिखाया था।
मैं यह सुझाव नहीं दे रहा हूं कि आपको इसका परीक्षण एक कठोर परीक्षण वाले पार्सिंग वर्ग पर करना चाहिए। लेकिन मैं लोगों को यह दिखावा करते हुए थक गया हूं कि कोई भी HTML को regexes के साथ पार्स नहीं कर सकता क्योंकि वे नहीं कर सकते। आप स्पष्ट रूप से कर सकते हैं, और यह कार्यक्रम उस दावे का प्रमाण है।
बेशक, यह आसान नहीं है, लेकिन यह है संभव!
और ऐसा करने की कोशिश करना समय की भयावह बर्बादी है, क्योंकि अच्छी पार्सिंग कक्षाएं मौजूद हैं, जिनका उपयोग आपको इस कार्य के लिए करना चाहिए । मनमाने ढंग से HTML पार्स करने की कोशिश कर रहे लोगों का सही जवाब यह नहीं है कि यह असंभव है। यह एक स्पष्ट और विवादास्पद जवाब है। सही और ईमानदार जवाब यह है कि उन्हें इसका प्रयास नहीं करना चाहिए क्योंकि यह खरोंच से पता लगाने के लिए बहुत परेशान है; वे एक पहिया है कि पूरी तरह से अच्छी तरह से काम करता है re backnvent करने के लिए अपनी पीठ तोड़ नहीं करना चाहिए।
दूसरी ओर, HTML जो कि एक प्रेडिक्टेबल सबसेट के भीतर आता है, रेगेक्स के साथ पार्स करना बेहद आसान है। यह कोई आश्चर्य नहीं है कि लोग उन्हें इस्तेमाल करने की कोशिश करते हैं, क्योंकि छोटी समस्याओं के लिए, खिलौना समस्याएं शायद, कुछ भी आसान नहीं हो सकता है। इसीलिए यह दो कामों को अलग करने के लिए महत्वपूर्ण है - विशिष्ट बनाम सामान्य - क्योंकि ये जरूरी नहीं कि समान दृष्टिकोण की मांग करते हैं।
मुझे आशा है कि HTML और रीगेक्स के बारे में सवालों का अधिक निष्पक्ष और ईमानदार उपचार देखने के लिए यहां भविष्य में होगा।
यहाँ मेरा HTML lexer है। यह एक मान्य पार्स करने की कोशिश नहीं करता है; यह सिर्फ शाब्दिक तत्वों की पहचान करता है। आप इसे 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 }