正規表現 - 文字列内から 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