• ベストアンサー

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>のように なったり) 良いアドバイスください。 よろしくお願いします。

  • Perl
  • 回答数2
  • ありがとう数2

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

  • ベストアンサー
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

  • 正規表現で文字列の抜き出し

    PHP4で、文字列の中からマッチしたものを抜き出したいのですが、なかなか上手いこといきません。 $str = "<a href=http://www.abc.com/><b>ABC</b>company</a>"; preg_match("/<a href=(.*)>(.*)<\/a>/i",$str,$str_reg); http://www.abc.com/ と <b>ABC</b>company の2つを抜き出したいのですが、 <b>タグが邪魔をして抜き出せません。 どうすれば上記2つを抜き出せるのでしょうか?

    • ベストアンサー
    • PHP
  • 正規表現で括弧にマッチさせたいのですが・

    Javaで正規表現を使おうとしてるのですがうまく行きません。 次のようにマッチさせたいです。 マッチさせたい文字列 : (abc)and(efg) マッチする文字列 : abcとefg マッチさせたい文字列 : (abc(efg)) マッチする文字列 : abc(efg) もしくは abc(efg)とefg とのようにまっちさせたいです。上の方はできたのですが、下の方がabc(efgとマッチしてまうのです。 いい方法を教えてください><お願いします。

  • 「マッチしない」正規表現の書き方

    正規表現について、おたずねします。 文字列 abc,def,ghi のいずれかにマッチする正規表現は (abc|def|ghi) ですよね。 それでは、「abc,def,ghi のいずれにもマッチしない」正規表現は、どう書けばいいのでしょうか? あちこち調べましたが、どうしてもわかりません。 ただし、if $a =~ /(abc|def|ghi)/ などで、=~ を !~ に直す、というのはナシです。あくまでも右辺の式の中で表現したいのですが…

  • 正規表現について

    タグで囲まれた文字列以外に含まれるアルファベットに一致させたいのですがうまくいかず困ってます。 例えば以下のようなHTMLの文字列があったとします。 ---------------------------------------------------------------------------- PHP: Hypertext Preprocessorとは、動的にHTMLデータを生成することによって、動的なウェブページを実現することを主な目的としたプログラミング言語、およびその言語処理系である。<br> 詳しくは以下のURLを参照されたし。<br> <a href="http://ja.wikipedia.org/wiki/PHP:_Hypertext_Preprocessor">リンク</a> ---------------------------------------------------------------------------- "PHP: Hypertext Preprocessor"と"URL"と"HTML"の部分だけに一致させて <br>や<a>などタグで囲まれた部分に含まれるアルファベットや記号には一致しないようにしたいのです。 いろいろ試してみましたが、どうにもうまくいきません。 どなたかお助けください><

    • ベストアンサー
    • PHP
  • c#で正規表現を使用してのmatchがうまくいきません。

    c#で正規表現を使用してのmatchがうまくいきません。 例えば以下のようなタグがあったとします この6行をmatchの対象の文字列としたとします。(タグは適当です) ---------------------------------------- <select class="iiy_name_list"> <option value="1347">abc</option> </select></td> <select class="sug_name_list"> <option value="761">def</option> </select></td> ---------------------------------------- 2行目の"abc"を含む列が欲しかったので、以下のように正規表現にてキャプチャ範囲を指定 @"<option\s+value=""1347"">(.*)</select></td>", しかし、”(.*)”以降に指定した"</select></td>"というタグは match対象の全体分には2箇所全く同じ物があるため、2回目に出現した"</select></td>"まで読み込み、不必要なdefの行までも取得してしまいます。 "</select></td>"以外に正規表現で指定できる文字はありません、 ”(.*)”以降に指定した"</select></td>"が最初に出現した場所までをキャプチャし、2回目に出現してもキャプチャの範囲対象外にするには どのようにすればよいのでしょうか? よろしくお願いします。

  • PHP5の正規表現でうまくマッチできない

    PHP5の正規表現で文字列中の独自タグをパースしようとしていて、 うまくいかず困り果てています。 正規表現パターンは、/<tag>.*<\/tag>/ 対象文字列は、<tag>こんにちは</tag><tag>こんばんは</tag> preg_match_all('/<tag>.*<\/tag>/', '<tag>こんにちは</tag><tag>こんばんは</tag>', $result) でマッチした文字列は、 $result[0]:<tag>こんにちは</tag> $result[1]:<tag>こんばんは</tag> となることを期待しているのですが、 実際は、 $result[0]:<tag>こんにちは</tag><tag>こんばんは</tag> となりました。 <tag>タグの間に入る文字列長、文字列フォーマットは不定です。 どうすれば期待通りの振る舞いに出来るでしょうか?

    • ベストアンサー
    • PHP
  • HTMLタグを取得する正規表現

    HTMLタグを取得する正規表現が出来ず、困っています。 <b><a href="http://aaa.ccc.co.jp">あいうえお</a></b> このような文字列であった時、 「<b><a href="http://aaa.ccc.co.jp">」を取得したいのです。 プログラムで制御する事も含め、以下のような動作を望んでいます。 1.「<b><a href="http://aaa.ccc.co.jp">」を取得する 2.対象文字列を「あいうえお</a></b>」にする 3.「あいうえお」を取得する 4.対象文字列を「</a></b>」にする 5.「</a></b>」を取得する やりたい事は「実際の表示上の文字数で文字数制限を行い、制限分の文字列を出力する」です。 なので、もし上記のような事をやる必要ないというのなら、その手段もご教示頂ければと思います。

    • 締切済み
    • PHP
  • [15]にマッチする正規表現

    [15]にマッチする正規表現 [15] にマッチする正規表現を書きたいのですが、うまくいきません。。。。 簡単だ、と思ったのですが。。。 expr "$DATA1" : .*\[15\].* と書くと、中の数字が15じゃなくてもマッチしてしまいます。 expr "$DATA1" : .*15.* であれば"15"にマッチしますが、対象の文字列の都合上、カッコでも識別する必要があり 困っております。[と]をエスケープする目的で\をつける、と思っているのですが、何か 思い違いをしているのでしょうか。

  • (秀丸)正規表現で、特定文字列含む<~>範囲を選択

    お世話になります。 (秀丸の)正規表現について教えてください。 たとえば、テキストファイルに、1行、 ”<abc>文字列1です><abc>文字列2です><abc>文字列3です>(ここで改行)” という文字列が存在しています。 それを対象として、 (秀丸の)正規表現を利用し、 ”<abc>文字列2です>”だけをマッチさせたいのですが、 試しに、(秀丸の)正規表現で、 <abc.*文字列2.*?>  ←(なお、実際の記号は半角(.*?等)で、それぞれ入力しています・・・) で検索実行したところ、 ”<abc>文字列1です><abc>文字列2です>” として、 前の方に、マッチして欲しくない、 ”<abc>文字列1です>” までがマッチしてしまっている状況です。 そこでご質問なのですが、 上記におきまして、(秀丸の)正規表現を利用して、 ”<abc>文字列2です>”だけをマッチさせるには、 どのようにすれば、よろしかったでしょうか? (ちなみに秀丸は、2013年12月10日(火)現在の  最新バージョンVer8.32を利用しています) 以上になります。 宜しくお願いいたします。

  • Perlで長音

    無謀なのかもしれませんが、Perl3日生(勉強しはじめて3日目)の分際で、フリーのスクリプトを改造しています。 そのスクリプトは検索エンジンなのですが、タグが無効になっていたため、いくつかのサイトを参考にして、指定したタグだけを許可するように改造しました。 しかし、何故か登録する文字列(html)に、長音"ー"が入った時だけエラーとなってしまいます。 Perlでは長音というのは何か特別な働きをするものなのでしょうか? ソースは以下のページを参考にしてますので、以下のページに書かれているものとほぼ同じです。 http://cuo.press.ne.jp/cgi/b04_1.html フォームから送信?する文字列が、 <a href=xxx>yahoo</a> であれば問題ないのですが、 <a href=xxx>ヤフー</a> とするとエラーになります。 また、不思議なことに、文字列でも <b>ヤフー</b> <img src=xxxx>ヤフー<br> といった、他のタグでは問題がありません。 何故でしょうか? どなたかお詳しい方、ヒントだけでも結構ですので、 何か思い当たることがございましたら、お教え下さい。 よろしくお願い致します。m(_ _)m

    • ベストアンサー
    • Perl

専門家に質問してみよう