• ベストアンサー

Perlの正規表現でマッチする範囲を限定

例えば、以下のようなことがしたいのですが可能でしょうか? 変数$bufに複数行の文字列が入っていたとしてこのうち、 タグとエレメント外を対象にしてマッチさせる (例として以下のような文字列にしたい) <a href="abcdef">abcdef</a><br> <b>abc</b>defabcdef<br> abcdef<b>abcdef</b><br> ↓(タグとエレメント外の文字aを<b>a</b>に置き換える) <a href="abcdef">abcdef</a><br> <b>abc</b>def<b>a</b>bcdef<br> <b>a</b>bcdef<b>abcdef</b><br> 実際にはこのときのマッチの対象となる文字と文字数は可変で bになるかもしれないですしabとかになるかもしれません。 ネット上で検索していろいろ試してみたのですが惜しい ところまではいっても完全に正しく動作しませんでした。 (タグやエレメントの中身に反応してしまったり一部分が 置き換えされなかったり<br>タグの後ろが<b></b>のように なったり) 良いアドバイスください。 よろしくお願いします。

質問者が選んだベストアンサー

  • ベストアンサー
noname#37358
noname#37358
回答No.2

No.1のお礼にある http://www.din.or.jp/~ohzaki/regex.htm を読んで、 ある程度の条件付きですが、正規表現だけで上手くいく方法を作ってみました。 以下は質問文の文字列で上手くいく例です。 s/((?:\G|<(?:\/[^>]+)|(?:br)>)[^<]*?)(a)/$1<b>\2<\/b>/gi; 条件は以下の通りです。 ・3層以上の階層構造を持つ文字列は不可 a<x>b<y>c</y>d</x>e のdの位置にはマッチしてしまいます。 ・要素終了タグが省略されている要素はあらかじめ列挙する必要がある。 例えば、 <(?:\/[^>]+)|(?:br)|(?:img [^>]*)> は<br>と<img>を空要素とみなし、その後の文字を要素外と判断します。 また、img要素は属性を持つことも出来ます。 ただやはり、正規表現だけでパターンマッチを行うのは、個人的にはお勧めしません。 正規表現では、マークアップによる階層構造を認識することが出来ないからです。 No.1の方の例のように、文字列を頭から走査し、階層構造上の現在の位置を判断してから、変換を行う方法がベターです。 文字列がXMLデータなら、XMLパーサーモジュールを使用する事もできます。 この場合は、データの解析はモジュールが行ってくれますので、変換部分のみを自分で書くだけで済みます。 HTML(SGML)データの場合は、終了タグの省略が可能なので、XMLパーサーでは扱えません。 いずれにせよ、どんな手法であっても、対象の文字列に対してそれ相応の文法チェックを事前に行っておかないと、必ず失敗は起きます。

pick52
質問者

お礼

ありがとうございます。 これで完璧です。 非常に助かりました。 Tacosanさん、u1roさん本当にありがとうございました。 このアルゴリズムを利用させてもらいます。 その他のご指摘も感謝します。 解決しましたのでこれで質問を閉めます。

その他の回答 (1)

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.1

さすがに「正規表現で一発」というわけにはいかないです.... ぱっと思い付くところだと「配列でスタックを作ってタグをおっかけていき, エレメント外のときには s で置き換える」くらいですかねぇ. こんな感じかなぁ? while (文字列が空でない) { if (最初が開きタグ) { スタックにpushする } elif (最初が閉じタグ) { スタックからpopしてエレメントを作る } elif (スタックが空でない) { 次のタグまでをエレメントに追加する } else { 置き換える } 処理した分を文字列から削除する } 面倒なので詳細はパス.

pick52
質問者

お礼

回答ありがとうございます。 やはり、一発では無理ですか・・・。 下とか参考にしてみたのですが・・・。 http://www.din.or.jp/~ohzaki/regex.htm s/((?:\G|<(b+)>)(<\/\1>)*?)(a+)/$1<b>\3<\/b>/gi; みたいな感じで(少し違ったかも?)惜しいところまで いったんです。 ただ、一部先ほど言ったような症状が出ることがあり 完璧じゃないので・・・。 更にこの$bufもCGIでフォームのデータから取得した 文字列なので可変で何が入力されるかはそのときまで 分かりません。

関連するQ&A

専門家に質問してみよう