RegExは、XHTML自己完結型タグを除くオープンタグと一致します
これらの開始タグをすべて一致させる必要があります。
<p>
<a href="foo">
しかし、これらではありません:
<br />
<hr class="foo" />
私はこれを思いついたので、それが正しいことを確認したかったのです。キャプチャしてa-z
いるだけです。
<([a-z]+) *[^/]*?>
私はそれが言うと信じています:
- 未満を見つけて、
- azを1回以上検索(およびキャプチャ)してから、
- 0個以上のスペースを見つけてから
- を除いて
/
、貪欲な0回以上の文字を検索します。 - 大なり記号を見つける
私はその権利を持っていますか?そしてもっと重要なことに、あなたはどう思いますか?
回答
[X] HTMLを正規表現で解析することはできません。HTMLは正規表現で解析できないためです。正規表現は、HTMLを正しく解析するために使用できるツールではありません。ここでHTMLと正規表現の質問に何度も回答したので、正規表現を使用してもHTMLを使用できません。正規表現は、HTMLで採用されている構成を理解するには十分に洗練されていないツールです。HTMLは正規言語ではないため、正規表現で解析することはできません。正規表現クエリには、HTMLを意味のある部分に分解する機能がありません。何度もですが、私には届きません。Perlで使用されるように拡張された不規則な正規表現でさえ、HTMLを解析するタスクには対応していません。あなたは私をクラックさせることは決してないだろう。HTMLは、正規表現では解析できないほど複雑な言語です。Jon Skeetでさえ、正規表現を使用してHTMLを解析することはできません。正規表現を使用してHTMLを解析しようとするたびに、不潔な子供が処女の血を流し、ロシアのハッカーがWebアプリを起動します。HTMLを正規表現で解析すると、汚染された魂が生きている領域に呼び出されます。HTMLと正規表現は、恋愛、結婚、儀式の子殺しのように一緒になります。<center>は手遅れだとは言えません。同じ概念空間で正規表現とHTMLを一緒に使用すると、水っぽいパテのように心が破壊されます。あなたが彼らに与えている正規表現でHTMLを解析するなら、そして彼らの冒涜的な方法は、基本的な多言語面で名前を表現できない人のために私たち全員を非人道的な苦労に運命づけます。HTML-plus-regexpは、あなたが観察している間、あなたの精神が恐怖の猛攻撃で枯れていく間、知覚者の神経を液化します。Rege̿̔̉xベースのHTMLパーサーは、StackOverflowを殺している癌ですそれは遅すぎる、それは遅すぎる、私たちを保存することはできませんされている子性を保証正規表現は(以前に預言として、それができない、HTMLを除く)全ての生体組織を消費するAの違反缶誰もがこの惨劇を乗り切る方法を愛する主人の助けに私たちを解析するために正規表現を使用しますHTMLは恐怖の拷問やセキュリティホールを永遠に人類を運命づけられたREGEの使用プロセスのHTMLへのツールとしてのxをブレア確立し、この世界とCHと腐敗したエンティティの恐怖レルム(SGML実体のような、しかし、より多くの壊れた)をglimp単なるSEのをREGの世界HTMLのための元のパーサーは、イン意志tantly輸送のAP rogrammerの意識私NTO AW ORL叫ん不断のD、彼が来て、pestilent SL ithy正規表現感染WILあなたHT食い入るリットル用MLパーサ、アプリケーションと存在Visual Basicのように、すべての時間が唯一悪化し、彼は彼のCOM来るES致しませんFiのGHT時間Eくる、HIの不道徳な輝きデすべての悟りをstro҉ying、HTMLタグが漏れるfr̶ǫmYO URの目のようなLIQ UIDのpの正規のAIN、歌 再EXP ssionの解析がextiますMORのnguish声SPからTAL男を、私はそれは美しいトンで、それそれはあなたが見ることができる見ることができ、ここで彼は、F inal snuf
FING O F嘘の男ALL IS LOST AのLL I SL OST番目のeは、彼が来るポニー彼はCOMねES彼は共同私をT sの彼は、ICHまたはpermeat ESアルリットルMY FAC E MY FACEᵒh神のn NO NOO oをO ON Θストップトン彼*̶͑̾̾GL ES ͎a̧͈͖r̽̾̈́͒͑e
N OT実ZA̡͊͠͝LGΌISͮ҉̯͈͕̹̘T O͇̹̺Ɲ̴ȳ̳TH E PO NYH̸̡̪̯ͨ͊̽̅̾Ȩ̶̧̨̬̩̹̭̯̾͛ͪ̈ͧ̾ͬ͘C̷̙̝͖ͭ̏ͥͮ͟Oͮ͏̮̪̝͍M̖͊̒ͪͩͬ̚̚͜Ȇ̴̟̟͙̞ͩ͌͝ S
代わりにXMLパーサーを使用してみましたか?
モデレーターのメモ
この投稿は、コンテンツの不適切な編集を防ぐためにロックされています。投稿は想定どおりに表示されます。内容に問題はありません。注意のためにフラグを立てないでください。
一方で、任意のHTMLのみ正規表現は不可能であると、時には解析するためにそれらを使用するために、適切なだ限られ、知られているHTMLのセットを。
データをスクレイピングしてデータベースに詰め込みたいHTMLページの小さなセットがある場合は、正規表現が正常に機能する可能性があります。たとえば、最近、議会のWebサイトから取得したオーストラリア連邦議会議員の名前、政党、および地区を取得したいと思いました。これは限られた1回限りの仕事でした。
正規表現は私にとっては問題なく機能し、セットアップは非常に高速でした。
ここでの欠点は、HTMLがチョムスキータイプ2文法(文脈自由文法)であり、正規表現がチョムスキータイプ3文法(正規文法)であるということだと思います。タイプ2の文法はタイプ3の文法よりも基本的に複雑であるため(チョムスキー階層を参照)、正規表現を使用してXMLを解析することは数学的に不可能です。
しかし、多くの人が試してみて、成功を主張する人もいますが、他の人が欠点を見つけて完全に混乱するまでは。
これらの人の言うことを聞かないでください。タスクを細かく分割すると、正規表現を使用して文脈自由文法を完全に解析できます。これらのそれぞれを順番に実行するスクリプトを使用して、正しいパターンを生成できます。
- 停止問題を解きます。
- 円を四角にします。
- O(log n)以下で巡回セールスマン問題を解きます。それ以上の場合は、RAMが不足し、エンジンがハングします。
- パターンはかなり大きくなるため、ランダムデータをロスレスで圧縮するアルゴリズムがあることを確認してください。
- ほぼそこにあります-全体をゼロで割るだけです。簡単-簡単。
私は最後の部分を自分で完全に終えていませんが、私は近づいていることを知っています。CthulhuRlyehWgahnaglFhtagnException
なんらかの理由でsをスローし続けるので、VB6に移植して使用しますOn Error Resume Next
。壁に開いたばかりのこの奇妙なドアを調べたら、コードを更新します。うーん。
PSピエール・ド・フェルマーもそれを行う方法を考え出しましたが、彼が書いていたマージンはコードに対して十分な大きさではありませんでした。
免責事項:オプションがある場合はパーサーを使用してください。そうは言っても...
これは、HTMLタグを照合するために使用する正規表現(!)です。
<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+>
完璧ではないかもしれませんが、私はこのコードを多くのHTMLで実行しました。<a name="badgenerator"">
Webに表示されるのような奇妙なものもキャッチすることに注意してください。
自己完結型のタグと一致しないようにするには、Kobiのネガティブな後読みを使用することをお勧めします。
<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+(?<!/\s*)>
または、そうでない場合と組み合わせてください。
反対票を投じる人へ:これは実際の製品からの作業コードです。このページを読んでいる人が、HTMLで正規表現を使用することは社会的に受け入れられるという印象を受けるとは思えません。
警告:この正規表現は、CDATAブロック、コメント、スクリプトおよびスタイル要素が存在する場合でも機能しなくなることに注意してください。良いニュースは、正規表現を使用してそれらを取り除くことができるということです...
地球は丸い(あるいは、奇妙な言葉を使いたければ、地球は扁球である)と言う人がいます。彼らは嘘をついています。
正規表現は再帰的であってはならないと言う人がいます。彼らはあなたを制限しています。彼らはあなたを征服する必要があります、そして彼らはあなたを無知に保つことによってそれをします。
あなたは彼らの現実に生きるか、赤い丸薬を飲むことができます。
マーシャル卿のように(彼はマーシャル.NETクラスの親戚ですか?)、私はUnderverse Stack Based Regex-Verseを見て、想像を絶する力の知識を持って戻ってきました。はい、古いものが1つか2つ保護されていたと思いますが、彼らはテレビでサッカーを見ていましたので、難しくはありませんでした。
XMLの場合は非常に単純だと思います。弱い心が理解しやすいようにbase64でデフレートおよびコード化されたRegEx(.NET構文)は、次のようになります。
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
+r/vD34mUADO1P4/AQAA//8=
設定するオプションはRegexOptions.ExplicitCapture
です。探しているキャプチャグループはELEMENTNAME
です。キャプチャグループERROR
が空でない場合は、解析エラーが発生し、正規表現が停止しました。
人間が読める正規表現に再変換する際に問題が発生した場合は、次のことが役立ちます。
static string FromBase64(string str)
{
byte[] byteArray = Convert.FromBase64String(str);
using (var msIn = new MemoryStream(byteArray))
using (var msOut = new MemoryStream()) {
using (var ds = new DeflateStream(msIn, CompressionMode.Decompress)) {
ds.CopyTo(msOut);
}
return Encoding.UTF8.GetString(msOut.ToArray());
}
}
よくわからない場合は、いいえ、冗談ではありません(しかし、おそらく私は嘘をついています)。それが動作します。私はそれをテストするためにたくさんの単体テストを構築しました、そして私は適合性テスト(の一部)さえ使用しました。これはトークナイザーであり、本格的なパーサーではないため、XMLをコンポーネントトークンに分割するだけです。DTDを解析/統合しません。
ああ...いくつかの補助的な方法で、正規表現のソースコードが必要な場合:
XML、特にHTMLを解析するための適切なツールはパーサーであり、正規表現エンジンではないことに同意します。ただし、他の人が指摘しているように、データ形式を知っていれば、正規表現を使用する方が速くて簡単で、仕事をこなすことがあります。
Microsoftには、実際には.NET Frameworkの正規表現のベストプラクティスのセクションがあり、特に入力ソースの検討について説明しています。
正規表現には制限がありますが、次のことを考慮しましたか?
.NET Frameworkは、正規表現に関しては、Balancing GroupDefinitionsをサポートするという点で独特です。
- バランスの取れた構成と.NET正規表現のマッチングを参照してください。
- .NET正規表現:正規表現とバランスの取れたマッチングを参照してください
- グループ定義のバランシングに関するMicrosoftのドキュメントを参照してください
このため、正規表現を使用してXMLを解析できると思います。ただし、これは有効なXMLである必要があることに注意してください(ブラウザーはHTMLを非常に許容し、HTML内で不適切なXML構文を許可します)。これが可能なのは、「バランシンググループ定義」により、正規表現エンジンがPDAとして機能できるようになるためです。
上記の記事1からの引用:
.NET正規表現エンジン
上記のように、適切にバランスの取れた構成は正規表現では記述できません。ただし、.NET正規表現エンジンは、バランスの取れた構成を認識できるようにするいくつかの構成を提供します。
(?<group>)
-キャプチャされた結果を名前グループとともにキャプチャスタックにプッシュします。(?<-group>)
-キャプチャスタックから名前グループを含む最上位のキャプチャをポップします。(?(group)yes|no)
--groupという名前のグループが存在する場合はyesの部分に一致し、それ以外の場合はnoの部分に一致します。これらの構造により、.NET正規表現は、基本的にスタック操作の単純なバージョン(プッシュ、ポップ、および空)を許可することにより、制限されたPDAをエミュレートできます。単純な操作は、それぞれインクリメント、デクリメント、およびゼロとの比較とほぼ同じです。これにより、.NET正規表現エンジンは、文脈自由言語のサブセット、特に単純なカウンターのみを必要とする言語を認識できます。これにより、従来とは異なる.NET正規表現で、適切にバランスの取れた個々の構造を認識できるようになります。
次の正規表現について考えてみます。
(?=<ul\s+id="matchMe"\s+type="square"\s*>)
(?>
<!-- .*? --> |
<[^>]*/> |
(?<opentag><(?!/)[^>]*[^/]>) |
(?<-opentag></[^>]*[^/]>) |
[^<>]*
)*
(?(opentag)(?!))
フラグを使用します。
- 単線
- IgnorePatternWhitespace(正規表現を折りたたんですべての空白を削除する場合は必要ありません)
- IgnoreCase(不要)
正規表現の説明(インライン)
(?=<ul\s+id="matchMe"\s+type="square"\s*>) # match start with <ul id="matchMe"...
(?> # atomic group / don't backtrack (faster)
<!-- .*? --> | # match xml / html comment
<[^>]*/> | # self closing tag
(?<opentag><(?!/)[^>]*[^/]>) | # push opening xml tag
(?<-opentag></[^>]*[^/]>) | # pop closing xml tag
[^<>]* # something between tags
)* # match as many xml tags as possible
(?(opentag)(?!)) # ensure no 'opentag' groups are on stack
これは、A Better.NET正規表現テスターで試すことができます。
私は次のサンプルソースを使用しました:
<html>
<body>
<div>
<br />
<ul id="matchMe" type="square">
<li>stuff...</li>
<li>more stuff</li>
<li>
<div>
<span>still more</span>
<ul>
<li>Another >ul<, oh my!</li>
<li>...</li>
</ul>
</div>
</li>
</ul>
</div>
</body>
</html>
これにより一致が見つかりました:
<ul id="matchMe" type="square">
<li>stuff...</li>
<li>more stuff</li>
<li>
<div>
<span>still more</span>
<ul>
<li>Another >ul<, oh my!</li>
<li>...</li>
</ul>
</div>
</li>
</ul>
それは実際にはこのように出てきましたが:
<ul id="matchMe" type="square"> <li>stuff...</li> <li>more stuff</li> <li> <div> <span>still more</span> <ul> <li>Another >ul<, oh my!</li> <li>...</li> </ul> </div> </li> </ul>
最後に、JeffAtwoodの記事「ParsingHtmlTheCthulhuWay」を本当に楽しんだ 。おかしなことに、現在4k以上の票を獲得しているこの質問への回答を引用しています。
正規表現を使用してHTMLを解析できないという答えは正しいですが、ここでは当てはまりません。OPは、正規表現を使用して1つのHTMLタグを解析したいだけです。これは、正規表現を使用して実行できるものです。
ただし、提案された正規表現は間違っています。
<([a-z]+) *[^/]*?>
正規表現に何かを追加すると、バックトラックすることで<a >>
、のようなばかげたものと一致させることができますが、[^/]
許容範囲が広すぎます。また、<space>*[^/]*
は[^/]*
スペースと一致する可能性があるため、冗長であることに注意してください。
私の提案は
<([a-z]+)[^>]*(?<!/)>
(?<! ... )
(Perl正規表現では)ネガティブな後読みはどこにありますか。「a <、次に単語、次に>以外のもの、最後は/ではない可能性があり、その後に>」と表示されます。
これにより、<a/ >
(元の正規表現と同じように)のようなことが可能になるため、より制限的なものが必要な場合は、スペースで区切られた属性ペアに一致する正規表現を作成する必要があります。
試してみてください:
<([^\s]+)(\s[^>]*?)?(?<!/)>
これはあなたのものと似ていますが、最後>
はスラッシュの後であってはならず、また受け入れますh1
。
古代中国の戦略家、将軍、哲学者である孫子は次のように述べています。
敵を知り、自分を知っていれば、1回も負けずに100回の戦闘に勝つことができると言われています。自分だけを知っていて、対戦相手を知らない場合は、勝つか負ける可能性があります。あなたがあなた自身もあなたの敵も知らなければ、あなたはいつもあなた自身を危険にさらすでしょう。
この場合、敵はHTMLであり、あなたは自分自身または正規表現のいずれかです。あなたは不規則な正規表現を持つPerlでさえあるかもしれません。HTMLを知っている。汝自身を知れ。
HTMLの性質を説明する俳句を作成しました。
HTML has
complexity exceeding
regular language.
また、Perlの正規表現の性質を説明する俳句も作成しました。
The regex you seek
is defined within the phrase
<([a-zA-Z]+)(?:[^>]*[^/]*)?>
<?php
$selfClosing = explode(',', 'area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed');
$html = '
<p><a href="#">foo</a></p>
<hr/>
<br/>
<div>name</div>';
$dom = new DOMDocument();
$dom->loadHTML($html);
$els = $dom->getElementsByTagName('*');
foreach ( $els as $el ) {
$nodeName = strtolower($el->nodeName);
if ( !in_array( $nodeName, $selfClosing ) ) {
var_dump( $nodeName );
}
}
出力:
string(4) "html"
string(4) "body"
string(1) "p"
string(1) "a"
string(3) "div"
基本的には、自己閉鎖する要素ノード名を定義し、html文字列全体をDOMライブラリにロードし、すべての要素を取得し、自己閉鎖しない要素をループして除外し、それらを操作します。
この目的で正規表現を使用するべきではないことは、すでにご存知だと思います。
これが正確に必要かどうかはわかりませんが、.NETも使用している場合は、Html Agility Packを使用できませんか?
抜粋:
これは、「Web外」のHTMLファイルを解析できるようにする.NETコードライブラリです。パーサーは、「実世界」の不正な形式のHTMLに対して非常に耐性があります。
最初の>
前に/
。が付いていないようにします。見て、ここでそれを行う方法の詳細については。これは、ネガティブルックビハインドと呼ばれます。
ただし、そのナイーブな実装は<bar/></foo>
、このサンプルドキュメントで一致することになります
<foo><bar/></foo>
解決しようとしている問題についてもう少し情報を提供できますか?プログラムでタグを繰り返し処理していますか?
W3Cは、疑似正規表現形式で解析を説明しています
。W3Cリンク
以下のためのVaRのリンクをたどりQName
、S
とAttribute
鮮明な画像を取得します。
これに基づいて、タグの削除などを処理するための非常に優れた正規表現を作成できます。
PHPでこれが必要な場合:
PHPのDOM 関数は、それが適切にXMLにフォーマットされていない限り、正常に動作しません。それらの使用が他の人類にとってどれほど優れていても。
simplehtmldomは良いですが、少しバグがあり、メモリがかなり重いです[大きなページでクラッシュします。]
私はquerypathを使ったことがないので、その有用性についてコメントすることはできません。
もう1つ試してみるのは、リソースが非常に少ないDOMParserです。これは、しばらくの間、楽しく使用しています。習得が簡単で強力です。
PythonとJavaについては、同様のリンクが投稿されました。
反対派の方へ-XMLパーサーが実際の使用に耐えられないことが判明した場合にのみ、クラスを作成しました。宗教的な反対票は、有用な回答が投稿されないようにするだけです。質問の範囲内に物事を置いてください。
解決策は次のとおりです。
<?php
// here's the pattern:
$pattern = '/<(\w+)(\s+(\w+)\s*\=\s*(\'|")(.*?)\\4\s*)*\s*(\/>|>)/';
// a string to parse:
$string = 'Hello, try clicking <a href="#paragraph">here</a>
<br/>and check out.<hr />
<h2>title</h2>
<a name ="paragraph" rel= "I\'m an anchor"></a>
Fine, <span title=\'highlight the "punch"\'>thanks<span>.
<div class = "clear"></div>
<br>';
// let's get the occurrences:
preg_match_all($pattern, $string, $matches, PREG_PATTERN_ORDER);
// print the result:
print_r($matches[0]);
?>
深くテストするために、次のような文字列の自動終了タグを入力しました。
- <hr />
- <br/>
- <br>
また、次のタグを入力しました。
- 1つの属性
- 複数の属性
- 値が一重引用符または二重引用符にバインドされている属性
- 区切り文字が二重引用符である場合、およびその逆の場合に一重引用符を含む属性
- 「unpretty」属性は、「=」記号の前、後、および前後の両方にスペースがあります。
上記の概念実証で機能しないものを見つけた場合は、コードを分析してスキルを向上させることができます。
<編集> ユーザーからの質問は、自動終了タグの解析を回避することであったことを忘れました。この場合、パターンはより単純で、次のようになります。
$pattern = '/<(\w+)(\s+(\w+)\s*\=\s*(\'|")(.*?)\\4\s*)*\s*>/';
ユーザー@ridgerunnerは、パターンが引用符で囲まれていない属性または値のない属性を許可しないことに気づきました。この場合、微調整により次のパターンが得られます。
$pattern = '/<(\w+)(\s+(\w+)(\s*\=\s*(\'|"|)(.*?)\\5\s*)?)*\s*>/';
</編集>
パターンを理解する
誰かがパターンについてもっと知りたいと思ったら、私はいくつかの行を提供します:
- 最初の部分式(\ w +)はタグ名と一致します
- 2番目の部分式には、属性のパターンが含まれています。それはによって構成されています:
- 1つ以上の空白\ s +
- 属性の名前(\ w +)
- 0個以上の空白\ s *(可能かどうか、ここに空白を残します)
- 「=」記号
- 繰り返しますが、0個以上の空白
- 属性値の区切り文字である一重引用符または二重引用符( '| ")。パターンでは、一重引用符はPHP文字列区切り文字と一致するためエスケープされます。この部分式は括弧で囲まれているため、参照できます。再び属性のクロージャを解析するために、それが非常に重要である理由です。
- ほとんどすべてのものと一致する属性の値:(。*?); この特定の構文では、欲張り一致(アスタリスクの後の疑問符)を使用して、RegExpエンジンにより、この部分式に続くもの以外に一致する「先読み」のような演算子が有効になります。
- ここに楽しみがあります。\ 4の部分は後方参照演算子であり、パターン内で以前に定義された部分式を参照します。この場合、最初に見つかった属性区切り文字である4番目の部分式を参照しています。
- 0個以上の空白\ s *
- 属性の部分式はここで終了し、アスタリスクで指定された0個以上の可能なオカレンスを指定します。
- 次に、タグが「>」記号の前に空白で終わる可能性があるため、0個以上の空白が\ s *サブパターンと一致します。
- 一致するタグは、単純な ">"記号、またはその前のスラッシュを使用する可能性のあるXHTMLクロージャで終わる場合があります:(/> |>)。もちろん、スラッシュは正規表現の区切り文字と一致するため、エスケープされます。
小さなヒント:このコードをより適切に分析するには、エスケープするHTML特殊文字を提供しなかったため、生成されたソースコードを確認する必要があります。
HTMLドキュメントから何かをすばやく抽出する必要があるときはいつでも、Tidyを使用してそれをXMLに変換し、XPathまたはXSLTを使用して必要なものを取得します。あなたの場合、次のようなものです。
//p/a[@href='foo']
以前はHTMLParserというオープンソースツールを使用していました。HTMLをさまざまな方法で解析するように設計されており、目的を十分に果たします。HTMLを別のツリーノードとして解析でき、APIを使用してノードから属性を簡単に取得できます。それをチェックして、これがあなたを助けることができるかどうか見てください。
HTMLを正規表現で解析するのが好きです。故意に壊れたばかHTMLを解析しようとはしません。このコードは私のメインパーサーです(Perl版):
$_ = join "",<STDIN>; tr/\n\r \t/ /s; s/</\n</g; s/>/>\n/g; s/\n ?\n/\n/g;
s/^ ?\n//s; s/ $//s; print
これはhtmlsplitと呼ばれ、HTMLを行に分割し、各行に1つのタグまたはテキストのチャンクを配置します。その後、grep、sed、Perlなどの他のテキストツールやスクリプトを使用して、行をさらに処理できます。冗談ではありません:)お楽しみください。
巨大なWebページを処理したい場合は、slurp-everything-firstPerlスクリプトを素晴らしいストリーミングに再調整するのは簡単です。しかし、それは本当に必要ではありません。
いくつかのより良い正規表現:
/(<.*?>|[^<]+)\s*/g # Get tags and text
/(\w+)="(.*?)"/g # Get attibutes
XML / XHTMLに適しています。
マイナーなバリエーションで、厄介なHTMLに対処できます...または最初にHTML-> XHTMLを変換します。
正規表現を作成する最良の方法は、不透明なワンライナーやコメント付きの複数行の怪物ではなく、Lex / Yaccスタイルを使用することです。私はまだここではそれをしませんでした。これらのものはほとんどそれを必要としません。
これは、いくつかの不敬虔な正規表現を使用してHTMLを解析するPHPベースのパーサーです。このプロジェクトの作成者として、正規表現を使用してHTMLを解析することは可能ですが、効率的ではないと言えます。サーバー側のソリューションが必要な場合(wp-Typography WordPressプラグインで行ったように)、これは機能します。
HTMLをBBCodeに置き換えるためのいくつかの素晴らしい正規表現がここにあります。否定的な発言をするすべての人にとって、彼はHTMLを完全に解析しようとしているのではなく、単にそれをサニタイズしようとしていることに注意してください。彼はおそらく、彼の単純な「パーサー」が理解できないタグを削除する余裕があります。
例えば:
$store =~ s/http:/http:\/\//gi;
$store =~ s/https:/https:\/\//gi;
$baseurl = $store;
if (!$query->param("ascii")) {
$html =~ s/\s\s+/\n/gi;
$html =~ s/<pre(.*?)>(.*?)<\/pre>/\[code]$2\[\/code]/sgmi;
}
$html =~ s/\n//gi;
$html =~ s/\r\r//gi;
$html =~ s/$baseurl//gi;
$html =~ s/<h[1-7](.*?)>(.*?)<\/h[1-7]>/\n\[b]$2\[\/b]\n/sgmi;
$html =~ s/<p>/\n\n/gi;
$html =~ s/<br(.*?)>/\n/gi;
$html =~ s/<textarea(.*?)>(.*?)<\/textarea>/\[code]$2\[\/code]/sgmi;
$html =~ s/<b>(.*?)<\/b>/\[b]$1\[\/b]/gi;
$html =~ s/<i>(.*?)<\/i>/\[i]$1\[\/i]/gi;
$html =~ s/<u>(.*?)<\/u>/\[u]$1\[\/u]/gi;
$html =~ s/<em>(.*?)<\/em>/\[i]$1\[\/i]/gi;
$html =~ s/<strong>(.*?)<\/strong>/\[b]$1\[\/b]/gi;
$html =~ s/<cite>(.*?)<\/cite>/\[i]$1\[\/i]/gi;
$html =~ s/<font color="(.*?)">(.*?)<\/font>/\[color=$1]$2\[\/color]/sgmi;
$html =~ s/<font color=(.*?)>(.*?)<\/font>/\[color=$1]$2\[\/color]/sgmi;
$html =~ s/<link(.*?)>//gi;
$html =~ s/<li(.*?)>(.*?)<\/li>/\[\*]$2/gi;
$html =~ s/<ul(.*?)>/\[list]/gi;
$html =~ s/<\/ul>/\[\/list]/gi;
$html =~ s/<div>/\n/gi;
$html =~ s/<\/div>/\n/gi;
$html =~ s/<td(.*?)>/ /gi;
$html =~ s/<tr(.*?)>/\n/gi;
$html =~ s/<img(.*?)src="(.*?)"(.*?)>/\[img]$baseurl\/$2\[\/img]/gi;
$html =~ s/<a(.*?)href="(.*?)"(.*?)>(.*?)<\/a>/\[url=$baseurl\/$2]$4\[\/url]/gi;
$html =~ s/\[url=$baseurl\/http:\/\/(.*?)](.*?)\[\/url]/\[url=http:\/\/$1]$2\[\/url]/gi;
$html =~ s/\[img]$baseurl\/http:\/\/(.*?)\[\/img]/\[img]http:\/\/$1\[\/img]/gi;
$html =~ s/<head>(.*?)<\/head>//sgmi;
$html =~ s/<object>(.*?)<\/object>//sgmi;
$html =~ s/<script(.*?)>(.*?)<\/script>//sgmi;
$html =~ s/<style(.*?)>(.*?)<\/style>//sgmi;
$html =~ s/<title>(.*?)<\/title>//sgmi;
$html =~ s/<!--(.*?)-->/\n/sgmi;
$html =~ s/\/\//\//gi;
$html =~ s/http:\//http:\/\//gi;
$html =~ s/https:\//https:\/\//gi;
$html =~ s/<(?:[^>'"]*|(['"]).*?\1)*>//gsi;
$html =~ s/\r\r//gi;
$html =~ s/\[img]\//\[img]/gi;
$html =~ s/\[url=\//\[url=/gi;
(x)HTMLを解析するための正規表現メソッドの質問について、いくつかの制限について話したすべての人への答えは次のとおりです。ここでは誰も再帰について話していないため、この強力な武器の力を支配するのに十分な訓練を受けていません。。
正規表現にとらわれない同僚が私にこの議論を通知しましたが、これは確かにこの古くてホットなトピックについてWeb上で最初ではありません。
いくつかの投稿を読んだ後、私が最初にしたことは、このスレッドで「?R」文字列を探すことでした。2つ目は、「再帰」について検索することでした。
いいえ、聖なる牛、一致するものは見つかりませんでした。パーサーが組み込まれている主要なメカニズムについて誰も言及していなかったので、私はすぐに誰もその要点を理解していないことに気づきました。
(x)HTMLパーサーが再帰を必要とする場合、再帰のない正規表現パーサーは目的には十分ではありません。単純な構成です。
正規表現のブラックアートを習得するのは難しいので、ウェブ全体を片手でキャプチャするための個人的なソリューションを試し、テストしているときに、さらに可能性を省いた可能性があります...まあ、私はそれについて確信しています:)
これが魔法のパターンです:
$pattern = "/<([\w]+)([^>]*?)(([\s]*\/>)|(>((([^<]*?|<\!\-\-.*?\-\->)|(?R))*)<\/\\1[\s]*>))/s";
やってみなよ。これはPHP文字列として記述されているため、「s」修飾子を使用すると、クラスに改行が含まれます。
ここだPHPマニュアルのサンプルノート1月に書いた私は:リファレンス
(注意してください。そのメモでは、「m」修飾子を誤って使用しました。正規表現エンジンによって破棄されますが、アンカーが使用されていない^
か、$
アンカーが使用されていないため、消去する必要があります)。
さて、より多くの情報に基づいた観点から、この方法の限界について話すことができます。
- 正規表現エンジンの特定の実装によると、再帰には、解析されるネストされたパターンの数に制限がある場合がありますが、使用される言語によって異なります。
- 破損していますが、(x)HTMLは重大なエラーを引き起こしません。消毒されていません。
とにかく、それは正規表現パターンにすぎませんが、多くの強力な実装の開発の可能性を明らかにしています。
このパターンは、フレームワークで構築したテンプレートエンジンの再帰下降パーサーを強化するために作成しました。実行時間とメモリ使用量の両方で、パフォーマンスは非常に優れています(同じ構文を使用する他のテンプレートエンジンとは関係ありません)。
多くの人がすでに指摘しているように、HTMLは正規言語ではないため、解析が非常に困難になる可能性があります。これに対する私の解決策は、整頓されたプログラムを使用してそれを正規言語に変換し、次にXMLパーサーを使用して結果を消費することです。これには多くの良いオプションがあります。私のプログラムは、Javaとjtidyライブラリを使用してHTMLをXMLに変換し、次にJaxenをxpathして結果に変換するように記述されています。
<\s*(\w+)[^/>]*>
説明された部分:
<
:開始文字
\s*
:タグ名の前に空白がある場合があります(醜いですが、可能です)。
(\w+)
:タグには文字と数字を含めることができます(h1)。まあ、\w
「_」にも一致しますが、害はないと思います。興味がある場合は、代わりに([a-zA-Z0-9] +)を使用してください。
[^/>]*
:以外のもの>
と/
クロージングまで>
>
: 閉鎖 >
無関係
そして、正規表現を過小評価している仲間に、彼らは正規言語と同じくらい強力であると言っています。
規則的ではなく、文脈自由でもないa n ba n ba nは、^(a+)b\1b\1$
FTWの逆参照!
(解析の野心なしに)これらのタグを単に見つけようとしている場合は、次の正規表現を試してください。
/<[^/]*?>/g
私はそれを30秒で書き、ここでテストしました:http: //gskinner.com/RegExr/
無視したいと言ったタイプを無視しながら、あなたが言及したタグのタイプと一致します。
末尾に「/」が付いていないタグを照合しようとしているようです。これを試して:
<([a-zA-Z][a-zA-Z0-9]*)[^>]*(?<!/)>
プログラミングの場合、特に精度が最優先される場合(たとえば、処理にセキュリティ上の影響がある場合)、HTMLを処理するときは、正規表現ではなく専用のパーサーとAPIを使用するのが通常は最善です。ただし、XMLスタイルのマークアップを正規表現で処理してはならないという独断的な見方には賛成しません。テキストエディタで1回限りの編集を行う場合、壊れたXMLファイルを修正する場合、またはXMLのように見えても完全ではないファイル形式を処理する場合など、正規表現がジョブに最適なツールである場合があります。知っておくべきいくつかの問題がありますが、それらは克服できないものではなく、必ずしも関連性があるわけでもありません。
<([^>"']|"[^"]*"|'[^']*')*>
私が今述べたような場合には、通常、のような単純な正規表現で十分です。これは、すべてを考慮した単純なソリューションですが、>
属性値にエンコードされていないシンボルを正しく許可します。たとえばtable
タグを探している場合は、としてそれを適応させることができます</?table\b([^>"']|"[^"]*"|'[^']*')*>
。
より「高度な」HTML正規表現がどのように見えるかを理解するために、以下は実際のブラウザーの動作とHTML5解析アルゴリズムをエミュレートするかなり立派な仕事をします。
</?([A-Za-z][^\s>/]*)(?:=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)|[^>])*(?:>|$)
以下は、XMLタグのかなり厳密な定義に一致します(ただし、XML名で許可されているUnicode文字の完全なセットは考慮されていません)。
<(?:([_:A-Z][-.:\w]*)(?:\s+[_:A-Z][-.:\w]*\s*=\s*(?:"[^"]*"|'[^']*'))*\s*/?|/([_:A-Z][-.:\w]*)\s*)>
確かに、これらは周囲のコンテキストといくつかのエッジケースを考慮していませんが、本当に必要な場合(たとえば、別の正規表現の一致を検索することによって)、そのようなことでも対処できます。
一日の終わりには、そのツールが正規表現である場合でも、仕事に最も適切なツールを使用してください。
その目的で正規表現を使用することは適切で効果的ではありませんが、正規表現が単純な一致の問題の迅速な解決策を提供することがあり、私の見解では、些細な作業に正規表現を使用することはそれほど恐ろしいことではありません。
StevenLevithanによって書かれた最も内側のHTML要素のマッチングに関する決定的なブログ投稿があります。