php:正規表現を使って、HTMLタグの中以外の改行コードをBRに置き換える処理を教えてください。


今、正規表現を使って、HTML文章中の改行コードを、BRに置き換える処理をPHPにて実装しています。

が、この方法がわかりません。

最初は単純に改行コードをBRタグに置き換えればよいと思っていたのですが、、

この方法だと、HTMLタグ中に入っていた改行コードもBRに置き換えられてしまい。

例えば

<img src="xxx" ¥n alt="xxx">



<img src="xxx" <br> alt="xxx">

になってしまいます。

正規表現を使えば、タグの中の改行コード以外をBRに置き換えるという処理ができそうなのですが、処理方法がわかりません。

割と普通に使われそうな感じだったので、ググッて見たのですが、解りませんでした。


*ここは確認しましたが、これだけでは理解できませんでした。
http://www.din.or.jp/~ohzaki/perl.htm#HTML_Tag


具体的なコードを示していただけるととても助かります。

回答の条件
  • 1人2回まで
  • 登録:2007/03/16 17:46:16
  • 終了:2007/03/17 13:58:49

ベストアンサー

id:nandedarou No.3

nandedarou回答回数230ベストアンサー獲得回数342007/03/17 10:55:16

ポイント30pt
$replaced = preg_replace ('/(((<(["\'][^"\']*["\']|[^>])*>).*?)\n|\n)/', '$2<br>', $html) ;

これでは?

id:ecmas

ありがとうございます。

でもやはり、


$html = '<strong>あいうえお</strong¥n>';


がうまくいきませんでした。



それと先ほどの返信、

$html = '<strong>あいうえお</strong¥n>\n\n\n\n\n';

がうまく行かないのは嘘でした、末端の改行をトリム処理しているのをすっかり忘れておりました。

2007/03/17 11:17:20

その他の回答(2件)

id:GEN111 No.1

GEN111回答回数472ベストアンサー獲得回数582007/03/16 18:41:05

ポイント20pt

これでいけませんかね。

$replaced = preg_replace ('/(<[^>]+?>)|\n/', '$1<br>', $html) ;
id:ecmas

ご回答、ありがとうございます。

さっそく試してみます。

【追記】

試してみました。

大体の場合で大丈夫そうですが。

'や"の中に>が含まれている場合は、やはりダメなようでした。

http://www.din.or.jp/~ohzaki/perl.htm#HTML_Tag

のURLにその判定方法があるみたいなんですが、、

ちょっと考えて見ます。

2007/03/16 19:12:00
id:nandedarou No.2

nandedarou回答回数230ベストアンサー獲得回数342007/03/17 00:12:54

ポイント30pt
$replaced = preg_replace ('/((<("[^"]*"|[^>])*>)?.*?)\n/', '$1<br>', $html) ;

これで、どうでしょうか?

id:ecmas

ありがとうございます。

これで、'や"の中に>が含まれている場合にもきちんと対応できました^^


でも、まだ一つ問題がありました。



「>」が最後に来ている文字列に置き換えを行った場合、そのタグの中の¥nは
になってしまうようです。

例えば、こんな場合にうまく行きません。

$html = '<strong>あいうえお</strong¥n>';


他にも試してみたところ、これもダメでした。

$html = '<strong>あいうえお</strong¥n>\n\n\n\n\n';


でもこれだとOKです。



$html = '<strong>あいうえお</strong¥n>\n\n\n\n\nかきくけこ';


とはいえ、実際に上記のようなケースに当てはまることはけっこうレアな気がします。。

2007/03/17 10:08:40
id:nandedarou No.3

nandedarou回答回数230ベストアンサー獲得回数342007/03/17 10:55:16ここでベストアンサー

ポイント30pt
$replaced = preg_replace ('/(((<(["\'][^"\']*["\']|[^>])*>).*?)\n|\n)/', '$2<br>', $html) ;

これでは?

id:ecmas

ありがとうございます。

でもやはり、


$html = '<strong>あいうえお</strong¥n>';


がうまくいきませんでした。



それと先ほどの返信、

$html = '<strong>あいうえお</strong¥n>\n\n\n\n\n';

がうまく行かないのは嘘でした、末端の改行をトリム処理しているのをすっかり忘れておりました。

2007/03/17 11:17:20
  • id:nandedarou
    すみません。だめですね。
    私最後に改行を入れて検証してしまったようです。
  • id:ecmas
    私こそ、誤情報を載せてしまい、すいません。


    わざわざ確認までしていただいて、ありがとうございます。


    今教えていただいたコードでも、


    一番最後に¥n>とない限りはうまく行くので、運用上は問題ないと思っています。


    とにかく助かりました。ありがとうございます。
  • id:nandedarou
    trim($html)."\n"を正規表現にかければ旨くいきます。
    次に、substrで、最後の<br>をとってできあがりです。
    以下、コードです。
    $replaced = preg_replace ('/(<("[^"]*"|\'[^\']*\'|[^>])*>.*?)\n|\n/', '$1<br>', trim($html)."\n") ;
    $replaced = substr($replaced,0,strlen($replaced)-strlen('<br>'));
    ※正規表現の無駄なカッコを省きました。
    ※タグのなかに、'dfs"jh'や、"dfs'j'h"などがあっても、大丈夫なように改良しました。

    以上、お試しください。

  • id:ecmas
    なるほど!

    ありがとうございます!バッチリ動きました。


    とにっかう一歩前進できました。


    あとは、このコードを読んで意味が理解できるように勉強していきます。


    ありがとうございました。
  • id:ecmas
    その後いろいろ試して見ました。


    が、、思った以上にこのままではダメなようです。



    例えば、


    <strong><i¥n>あいうえお</strong></i>


    のような場合でもうまく動きません。


    どうやら、

    タグがあって、改行する前に次のタグが始まり、そのタグの中で改行されているとうまく動きません。


    うーん、以外に複雑だ。
  • id:nandedarou
    もうちょっと考えて見ます。
    お待ちください。
  • id:ecmas
    大変ありがたいお言葉、ありがとうございます。


    「けっこう要望がありそうな処理だから誰かコードを知ってるだろう」
    と他力本願でしたが、やはり避けては通れない道。。

    今正規表現を勉強中です。


    読んでいると、なんとなくうまく行かなかった理由はわかったつもりになり、いろいろ手を加えて見たのですが、、、

    どんどんひどくなっていきますw


    正規表現、奥が深い。。
  • id:nandedarou
    ちょっとお聞きしますが、この関数は表示の直前におこなうのでしょうか?
    表示の為だけならば、HTMLとして正しければよいので、タグの改行内の改行を取り除いてもよいでしょうか?
  • id:ecmas
    はい。
    表示用だけに使います。

    (復元用に、裏で変換前のデータを保持しています。)


    >表示の為だけならば、HTMLとして正しければよいので、タグの改行内の改行を取り除いてもよいでしょうか?

    もちろん大丈夫です。
    この発想は盲点でした!
  • id:nandedarou
    タグの中の"や'で囲まれた文字列には、改行がないと思っていいでしょうか?
  • id:nandedarou
    >タグの中の"や'で囲まれた文字列には、改行がないと思っていいでしょうか?
    と聞きましたが、どちらでも構わないです。

    以下のコードで、全ての改行を残したまま、タグ外の改行のみに<br>を追加することができます。
    ※$replaced を $htmlに変更しました。確認の際、注意して下さい。

    $html = trim($html);
    while(preg_match('/(<("[^"]*"|\'[^\']*\'|[^>\n])*)\n/', $html)){
    $html = preg_replace ('/(<("[^"]*"|\'[^\']*\'|[^>\n])*)\n/', '$1●改行●',$html ) ;
    }
    $html = preg_replace ('/\n/', '<br>'."\n", $html) ;
    $html = preg_replace ('/●改行●/', "\n", $html) ;

    ※改行文字が要らない場合、は以下のとおり

    $html = trim($html);
    while(preg_match('/(<("[^"]*"|\'[^\']*\'|[^>\n])*)\n/', $html)){
    $html = preg_replace ('/(<("[^"]*"|\'[^\']*\'|[^>\n])*)\n/', '$1',$html ) ;
    }
    $html = preg_replace ('/\n/', '<br>', $html) ;

    以上、どうでしょうか?
  • id:nandedarou
    "" や ''の中に<や>があるとダメ見たいです。
    でも、解決できそうですので、もうちょっとお待ちください。
  • id:nandedarou
    こんどこそ、大丈夫だと思います!

    1.タグ内の¥nを一旦別の文字('[改行]')に変換
    ※" "の中に>があることがあるという条件をクリアするのは、非常に困難でした。コールバック関数というテクニックを使用しました。
    2.残った改行を<br>に変換
    3.タグ内の'[改行]'を¥nに戻す

    define( 'N_IN_TAG', '[改行]' );
    function CutN($matches){
    return str_replace("\n",N_IN_TAG,$matches[0]);
    }
    $html = trim($html);
    $html = preg_replace_callback('/<("[^"]*"|\'[^\']*\'|[^>])*>/', "CutN", $html);
    $html = str_replace("\n",'<br>',$html);
    $html = str_replace(N_IN_TAG,"\n",$html);


    結果を保存する変数名は、$replacedではなく$htmlに変更しましたので、確認時、注意してください。
  • id:nandedarou
    なんどもすみませんさらに修正しました。
    最初の改行は削除せず、
    タグの外の改行の前に<br>を入れるという結果になるように作りました。
    この結果になっているか、ご確認お願いします。

    define( 'N_IN_TAG', '[改行]' );
    function CutN($matches){
    return str_replace("\n",N_IN_TAG,$matches[0]);
    }
    $html = trim($html);
    $html = preg_replace_callback('/<("[^"]*"|\'[^\']*\'|[^>])*>[\n]?/', "CutN", $html);
    $html = str_replace("\n",'<br>'."\n",$html);
    $html = str_replace(N_IN_TAG,"\n",$html);
  • id:ecmas
    >nandedarouさん

    ありがとうございます。
    そしてお返事が遅くなりすいません。

    さっそく試してみたいと思います。
  • id:ecmas
    うまく動きました!

    ありがとうございます。


    *レアケースですが、、タグの中に[改行]と入っている場合、それも改行コードに変わってしまいます。
    例えば、title="[改行]"、、、そもそも、こんな書き方する場合はほとんどなさそうですね。。

    [改行]に置き換えて元に戻す変わりに、いきなり半角スペースに置き換えるのもありかなと思いました。

    function CutN($matches){
    return str_replace("\n"," ",$matches[0]);
    }
    $html = trim($html);
    $html = preg_replace_callback('/<("[^"]*"|\'[^\']*\'|[^>])*>[\n]?/', "CutN", $html);
    $html = str_replace("\n",'<br>'."\n",$html);
  • id:GEN111
    こんなのはどうですか?
    シングルクォートがエスケープされるようなので stripslashes を入れましたが、他の部分のバックスラッシュも削除されるので、場合によっては別の対処が必要です。

    $r = preg_replace('/(<(("[^"]*")|(\'[^\']*\')|[^>])*?>)|\n/e', 'stripslashes("$1"?"$1":"<br>")', $html) ;
  • id:ecmas
    >GEN111さん

    ありがとうございます。これも動きました。

    どうやら、エスケープされるのは、シングルクオートのみのようですね。
    なので、

    preg_replace('/(<(("[^"]*")|(\'[^\']*\')|[^>])*?>)|\n/e', '"$1"?str_replace("\\\'","\'","$1"):"<br />"', $html) ;

    でいけました。
  • id:nandedarou
    >GEN111さん
    なるほど、三項演算子を使えばコールバック関数不要ですね!

    タグを選択する正規表現の後に次の様に\n?をつけた方がいいと思います。
    '/(<("[^"]*"|\'[^\']*\'|[^>])*>\n?)|\n/e'

    そうしないと、タグとタグの間に一つ改行を入れただけで、<br />が入ってしまいます。

  • id:nandedarou
    >GEN111さん
    どちらかというと、三項演算子よりも、eオプションの方がポイントだったんですね。上記のコメント書いてるときには気づきませんでした。

この質問への反応(ブックマークコメント)

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

絞り込み :
はてなココの「ともだち」を表示します。
回答リクエストを送信したユーザーはいません