正規表現 - 文字列内から HTML タグを正確に判別!
Updated:
使っているプログラミング言語に関わらず、文字列内から HTML タグの部分を抽出したり削除したりするケースがあると思います。
その際、正規表現を使用すると思いますが、場合によっては確実に HTML タグを判別できない場合があります。
結局、広く知れ渡っている正規表現パターンが正確に判別できますが、他のパターンも含めて数種類のパターンを Ruby で検証してみました。(正規表現の説明付きで)
0. 準備
今回のテストでは、以下のような文字列を考えてみることにする。
文字列をダブルクォーテーション "
で囲んだり、シングルクォーテーション '
で囲んだり、タグの属性値内にさらにタグを埋め込んだりしている。
この画像<img src="../images/example.png" alt='example.png' title='<img>タグ例' />は<span>テスト画像</span>です。
そして、テストはこの文字列から HTML タグを判別して除去することにする。
以下のような文字になれば成功ということ。
この画像はテスト画像です。
1. テストパターン1(不正確なパターン)
<.*?>
.
は、改行を除く任意の1文字。*
は、直前の表現の0回以上の繰り返しだが、*?
とすることでさらに最短一致。
よって、 <
と最初の >
で囲まれた任意の0文字以上の文字列のこと。
2. テストパターン2(不正確なパターン)
<\/?[^>]*>
\/
は、/
文字そのもの。/
だけだと別の意味なるのでエスケープしている。?
は、 直前の正規表現の 0 または 1 回の繰り返し。[ ]
は、文字クラス指定。[ ]
内に列挙したいずれかの1文字。^
は、[ ]
内の先頭にあれば、[ ]
内に列挙されている指定文字以外の1文字。*
は、直前の表現の0回以上の繰り返し。
よって、<
と >
で囲まれ、 <
の次に /
が0個か1個で、残りが >
以外の文字列のこと。
言い換えれば、「テストパターン1」の <.*?>
と同じになる。
3. テストパターン3(正確なパターン)
<("[^"]*"|'[^']*'|[^'">])*>
( )
は、正規表現のグループ化。|
は、選択(OR)。"[^"]*"
は、"
と次の"
で囲まれた部分という意味。
これは、この部分を1つのかたまりとみなすため。'[^']*'
は、'
と次の'
で囲まれた部分という意味。
これは、この部分を1つのかたまりとみなすため。[^'">]
は、"
,'
,>
以外の1文字。
"
と"
で囲まれた部分や'
と'
で囲まれた部分を1つの固まりとみなしているのに、さらに"
や'
が存在した場合は HTML タグではない。
また、>
を除去するのは最短一致させるため。
4. テストパターン4(正確なパターン)
<(".*?"|'.*?'|[^'"])*?>
これは「テストパターン3」の正規表現を書き換えただけのものなので、これも正確に HTML タグを判別できる。
5. 検証用 Ruby スクリプト
上記の「テストパターン1」〜「テストパターン4」をテストする Ruby スクリプトは以下の通り。
File: test_regexp.rb
1
2
3
4
5
6
7
8
9
# -*- coding: utf-8 -*-
str = "この画像<img src=\"../images/example.png\" alt='example.png' title='<img>タグ例' />は<span>テスト画像</span>です。"
puts "[変換前] #{str}"
puts "[パターン1(×)] #{str.gsub(/<.*?>/, "")}"
puts "[パターン2(×)] #{str.gsub(/<\/?[^>]*>/, "")}"
puts "[パターン3(○)] #{str.gsub(/<("[^"]*"|'[^']*'|[^'">])*>/, "")}"
puts "[パターン4(○)] #{str.gsub(/<(".*?"|'.*?'|[^'"])*?>/, "")}"
文字列は "
と "
で囲むので、文字列内の "
は エスケープしている。
6. 検証実施
作成した検証用 Ruby スクリプトを実行してみる。
$ ruby test_regexp.rb
[変換前] この画像<img src="../images/example.png" alt='example.png' title='<img>タグ例' />は<span>テスト画像</span>です。
[パターン1(×)] この画像タグ例' />はテスト画像です。
[パターン2(×)] この画像タグ例' />はテスト画像です。
[パターン3(○)] この画像はテスト画像です。
[パターン4(○)] この画像はテスト画像です。
意図した結果となった。
他にも対応不可能なパターンがあるかも知れませんが、おそらく上記の「テストパターン3」、「テストパターン4」でほぼ確実に HTML タグを判定可能です。
また、正規表現の部分は Ruby に限らず他の言語でも応用可能です。
意外と詳しい説明(なぜそんな正規表現パターンなのかという説明)が少ないのでまとめてみた次第です。
参考になれば幸いです。
以上。
Comments