PHPの正規表現についてです。

ちょっと説明が難しいのですが・・・拙いですがすみません。
たとえば以下のようなメールなど入力フォームのあるHTMLがあるとします。
<body>
<form>
入力1:<input type="text" name="○○○">
入力2:<select name="△△△">
    <option>xxxx</option>
    <option>xxxx</option>
    </select>
入力3:<input name="□□□" type="radio" value="xxx">xxx
    <input name="□□□" type="radio" value="xxx">xxx
    <input name="□□□" type="radio" value="xxx">xxx
<input type="submit" value="送信">
</form>
</body>

それぞれのタグ(inputやselect)を各々のname属性(○○○、△△△、□□□)に置換したいです。

<body>
入力1:○○○
入力2:△△△
入力3:□□□
</body>

以下のようなものを要素ごとに繰り返してみたんですが、
preg_replace('/(.*)<input[\s]+[^>]*?name=["\'](.*?)["\'].+[>$]?(.*)/i','$1$2$3',$html);
<select>なんかは<option>や</select>は置換されず、ラジオボタンは個数分全て出てきてしまいます。
ちなみに具体的な用途は、それぞれのname属性を$_POST['○○○']として自動的に受け取って、入力確認として表示したいというものです。
よろしくお願いします。

回答の条件
  • 1人50回まで
  • 登録:2007/11/08 20:29:29
  • 終了:2007/11/09 18:00:08

回答(1件)

id:tezcello No.1

tezcello回答回数457ベストアンサー獲得回数682007/11/08 22:06:23

ポイント100pt

$html = <<<EOL
<body>
<form>
入力1:<input type="text" name="○○○">
入力2:<select name="△△△">
    <option>xxxx</option>
    <option>xxxx</option>
    </select>
入力3:<input name="□□□" type="radio" value="xxx">xxx
    <input name="□□□" type="radio" value="xxx">xxx
    <input name="□□□" type="radio" value="xxx">xxx
入力4:<select name="●●●">
    <option>xxxx</option>
    <option>xxxx</option>
    </select>
<input type="submit" value="送信">
</form>
</body>

EOL;


// form タグとそれ以外の分離
preg_match('/(.*?)<form[^>]*?>(.*?)<\/form>\s?(.*)/s', $html, $elms);

// input タグ、select タグを抜き出す
preg_match_all('/\s(.*?)<(input|select).*?name="([^"]+)"/',$elms[2], $res, PREG_SET_ORDER);

foreach ($res as $r){
	if (isset($tags[$r[3]])) continue;	// 同じ名前で登録をしない<=ラジオボタンを1回だけ登録する為
	// 抜き出したタグのname属性で配列を作る
	$tags[$r[3]] = $r[1];
}

// これ以降はオマケです。質問文の希望出力結果に合わせてみました。
$result = $elms[1];
foreach ($tags as $name => $preword){
	$result .= $preword.$name."\n";
}
$result .= $elms[3];

print_r($result);

結果が

<body>
入力1:○○○
入力2:△△△
入力3:□□□
入力4:●●●
</body>

複数のformがある場合は考慮していません。

id:indiana

ありがとうございます。試させていただきました。

name部分は抽出できるのですが、「置換」がうまくいかないようです。

(質問文がわかりにくいんですけどね・・・汗)

以下のHTMLで試してみました。

------------------------------------------

<body>

<form>

<table>

<tr>

<th>項目1</th>

<td><input type="text" name="itemA" value=""></td>

</tr>

<tr>

<th>項目2</th>

<td><textarea name="itemB"></textarea></td>

</tr>

<tr>

<th>項目3</th>

<td>

<select name="itemC">

<option value="z">z

<option value="y">y

<option value="x">x

</select>

</td>

</tr>

<tr>

<th項目4</th>

<td>

<input type="radio" name="itemD" value="w">w

<input type="radio" name="itemD" value="v">v

<input type="radio" name="itemD" value="u">u

</td>

</tr>

<tr>

<th項目5</th>

<td>

<input type="checkbox" name="itemE" value="t">t

<input type="checkbox" name="itemE" value="s">s

<input type="checkbox" name="itemE" value="r">r

</td>

</tr>

<tr>

<td colspan="2"><input type="submit" value="送信"></td>

</tr>

</table>

</form>

</body>

------------------------------------------

結果は

------------------------------------------

<body>

<td>itemA

<td>itemB

itemC

itemD

itemE

</body>

------------------------------------------

となりました。

理想としては

------------------------------------------

<body>

<form>

<table>

<tr>

<th>項目1</th>

<td>itemA</td>

</tr>

<tr>

<th>項目2</th>

<td>itemB</td>

</tr>

<tr>

<th>項目3</th>

<td>itemC</td>

</tr>

<tr>

<th項目4</th>

<td>itemD</td>

</tr>

<tr>

<th項目5</th>

<td>itemE</td>

</tr>

<tr>

<td colspan="2"></td>

</tr>

</table>

</form>

</body>

------------------------------------------

としたいです。

長いソースばかりで読みにくいですが・・・

というか質問の例題も説明不足ですね・・・すみません。

2007/11/09 00:26:14
  • id:tezcello
    やりたい事が質問文とは少しずれてきている部分もあるので、確認したいです。

    ・name属性を取り出したいタグは、input, select, textarea(<-追加)
    ・上記タグは削除し、name属性の値のみ残す
    ・上記以外のタグは全て残す(formタグ及び例に挙がっていないタグも)

    また、
    ・submitボタンにname属性が付いている場合は?
    ・hiddenタイプの場合は?

    更に
    ・HTMLソースは変更できませんか?
      checkbox, radiobutton に続く文字列の終りを判断できない為

    これらにについてお答え下さい。


    以下は単なる興味でお尋ねしたいのですが、
    > ちなみに具体的な用途は、それぞれのname属性を$_POST['○○○']として自動的に受け取って、
    > 入力確認として表示したいというものです。
    (改行を挿入)
    radiobuttom, checkbox は非選択の場合何も送られないのでソースを解析して...は必要な場合もあるかと思いますが、入力漏れの確認でしたら、name属性の取り出しのみでは不十分でしょうか?
    一つも選ばれていない状態はNGであるならデフォルトを用意(checked属性で指示する)したり、プログラム的に未選択時のデフォルトを設定したりの方がスマートな気がします。
    但し、そうすると汎用性が無くなり、いろいろなHTMLソースを一つのスクリプトで対応は出来ないでしょうけど。
    (HTMLソースの中にコメントや、独自タグでそれらを記述しておく事もできますが、受取る側のスクリプトは結構複雑になるでしょうね。仕様をキッチリつくる必要もありそうです。)
  • id:indiana
    いろいろとお気遣いありがとうございます。
    基本的にはfile_get_contentsにてリファラのHTMLを取得し、そのなかで<form>~</form>内にあるフォーム要素(input,checkbox,select,radio(radiobutton),hidden,[submit,reset])すべてに対応し、テーブルなどの構成(デザイン)はそのままに、フォーム要素のタグだけを$_POST['○○○']に変換したいというものです。
    (<form>~</form>は1つだけという前提です)
    ですので、
    <td><input type="text" name="t" value="○○○"></td> →→ <td>$_POST['t']</td>
    <td><select name="s"><option></select></td> →→ <td>$_POST['s']</td>
    といった形にして、name属性がどんなものでも対応できればと思っています。


    >・HTMLソースは変更できませんか?
    >  checkbox, radiobutton に続く文字列の終りを判断できない為

    おっしゃる通りですので、こちらは続く文字列はそのままでお願いします。
    <td><input type="radio" name="r" value="○○○">○○○</td> →→ <td>$_POST['r']○○○</td>


    >radiobuttom, checkbox は非選択の場合何も送られないのでソースを解析して...は必要な場合もあるかと思いますが、入力漏れの確認でしたら、name属性の取り出しのみでは不十分でしょうか?
    >一つも選ばれていない状態はNGであるならデフォルトを用意(checked属性で指示する)したり、プログラム的に未選択時のデフォルトを設定したりの方がスマートな気がします。
    >但し、そうすると汎用性が無くなり、いろいろなHTMLソースを一つのスクリプトで対応は出来ないでしょうけど。
    >(HTMLソースの中にコメントや、独自タグでそれらを記述しておく事もできますが、受取る側のスクリプトは結構複雑になるでしょうね。仕様をキッチリつくる必要もありそうです。)

    入力漏れはJavaScriptで対応します。
    今回の質問では「以下が入力された内容ですので確認して下さい」といった用途のページを考えています。
  • id:tezcello
    > >・HTMLソースは変更できませんか?
    > >  checkbox, radiobutton に続く文字列の終りを判断できない為
    > おっしゃる通りですので、こちらは続く文字列はそのままでお願いします。
    > <td><input type="radio" name="r" value="○○○">○○○</td> →→ <td>$_POST['r']○○○</td>

    説明不足でした。
    ○○○がタグを含まない文字列だけ
    常に改行で区切られている
    常にtdタグに囲まれている
    など、決まっていれば問題無いのですが、
    <td><input .....><b>○○</b>△△</td> とか
    <p><input ......>□□<i>△△</i></p> とかの場合どこまでが削除したい範囲かがわからないのです。
    もし元のHTMLソースも自サイトのものなら、
    <input type="radio" name="r" value="○○○">○○○
    の部分を
    <label><input type="radio" name="r" value="○○○">○○○</label>
    のように記述できればinputタグとそれに続く文字列の範囲を限定できると思ったわけです。
    (labelタグは、むしろ付けておく事が推奨されていたと記憶しています)

    それと、余計なお世話かも知れませんが
    > 基本的にはfile_get_contentsにてリファラのHTMLを取得し、そのなかで<form>~</form>内にあ
    > るフォーム要素(input,checkbox,select,radio(radiobutton),hidden,[submit,reset])すべてに
    > 対応し、テーブルなどの構成(デザイン)はそのままに、フォーム要素のタグだけを$_POST['○○○']
    > に変換したいというもの
    (改行を挿入)
    referrer を送ってこないブラウザもあるようですが、大丈夫ですか?(hiddenフィールドに書いておくか、クエリに付けておくなどして、スクリプトに知らせてやればいいのですけど)
    hidden を表示してしまっていいのでしょうか?そういう仕様なら別に構いませんが。
  • id:tezcello
    例えば、下のように常に改行で区切られていれば
    <input type="checkbox" name="itemE" value="t">t
    <input type="checkbox" name="itemE" value="s">s
    <input type="checkbox" name="itemE" value="r">r

    // input の処理
    $html = preg_replace('/(<input.*?name="([^"]+)"[^>]*>).*/', '$2' ,$html);
    // radio, checkbox の時、複数行出てくるのを削除する
    $html = preg_replace('/(.*\s)\1*/', '$1', $html);
    // 名前の無いボタンの処理
    $html = preg_replace('/<input.*?type="(submit|reset|button)"[^>]*?>/', '', $html);
    // select, textarea の処理
    $html = preg_replace('/<(select|textarea).*?name="([^"]+)"[^>]*?>.*?<\/\1>/s', '$2', $html);
    こんな感じで良いように思います。
  • id:indiana
    わざわざコメントですみません。
    ある程度は希望に沿うものができそうです。
    ただやはりradiobuttonとcheckboxの重複処理だけが厳しいですね・・・

    ためしに以下のように書いてみたところ、とりあえず重複処理もできそうな感じでした。

    $fp = fopen($_SERVER['HTTP_REFERER'],"r");
    $b="";
    $html="";
    while(!(feof($fp))){
    $buf=fgets($fp,1024);
    $b1=preg_replace('/(.*)<input.*?name=[("|\')]?(.*?)[("\s|\'\s|\s)][^>]*?>(.*)/i','$2',$buf);
    if($b!=$b1){
    $buf=preg_replace('/(.*)<input.*?name=[("|\')]?(.*?)[("\s|\'\s|\s)][^>]*?>(.*)/i','$1$2$3',$buf);
    $buf= preg_replace('/<input.*?type="(submit|reset|button)"[^>]*?>/', '',$buf);
    $html.=$buf;
    }
    $b=$b1;
    }
    fclose($fp);

    $html = preg_replace('/(.*?)<select.*?name=[("|\')]?(.*?)[("\s|\'\s|\s)].*?[^(<\/select>)]*<\/select>(.*?)/ms','$1$2$3', $html);
    $html = preg_replace('/(.*)<textarea.*?name=[("|\')]?(.*?)[("\s|\'\s|\s)][^(<\/)]*<\/textarea>(.*)/i','$1$2$3', $html);

    もっとスマートにできる気はするのですが、正規表現が苦手なもので・・・
    というか当初の質問から要求が増した感じで申し訳ないです。


    ご指摘のようにリファラのない場合には別の処理を考えてみます。
    ちなみに動作させるのは同じサーバー内のみですので、ソースはいじれます。
    たしかに<label>等のフラグがあった方がいいかもしれませんね。
  • id:tezcello
    一度に全部読み込んでしまった方が良くないですか?
    HTMLソースが長かった時に、置換えのキーワードが機能しないなんて事になりそうですが。

    <label>タグが無くても、常に改行で区切られているように注意すれば先のコメントの置換で重複は除けると思います。
  • id:indiana
    たしかに1行ずつだと遅くなったり無駄な処理が多いですね・・・
    2007-11-09 15:08:58にいただいたコメントのもので対処しようと思います。
    tezcelloさん長々と本当にありがとうございました。
  • id:tezcello
    高ポイントありがとうございます。

    質問の主旨からは外れますが、僕なら form 以下を別ファイルにしておいて入力部分を独自ダグにしておいて、用途に応じてそのタグを置き換えるかも知れません。
    例えば、
    <form>
    <table>
    <tr><th>項目1</th><td><tags type="text" name="itemA" default=""></td></tr>
    <tr><th>項目2</th><td><tags type="textarea" name="itemB" default=""></td></tr>
    <tr><th>項目3</th><td><tags type="select" name="itemC" default="z" option="z+y+x">
    </td></tr>
    <tr><th>項目4</th><td><tags type="radio" name="itemD" default="" option="w+v+u"></td></tr>
    <tr><th>項目5</th></th><td><tags type="check" name="itemE" default="" option="t+s+r"></td></tr><td>
    <tr><td colspan="2"><input type="submit" value="送信"></td></tr>
    </table>
    </form>
    なんてのを用意しておいて、入力用にする時は、
    <tags type="text" name="itemA" default="">

    <input type="text" name="itemA" value="">
    に置き換える(他も同様に置き換える)
    確認用には
    <tags type="text" name="itemA" default="">

    $_GET['itemA'] (または$_POST['itemA'])
    の内容に置き換える

    という感じで。様はブログのテンプレートみたいなモンでしょうか。
    でも、書きながら php(Web)を通さないとレイアウトの確認もできないし、手間ばっかりかかるような気もして来ました。
  • id:indiana
    そうですね・・・実はそういうことも考えてみたんです。
    それで、PEARのHTML_Saxを使ってリファラのファイルをパースし、POSTに代入しようとしていたのですが、
    フォームのあるページ(リファラのページ)のデザインをそのまま引き継ぐには少々手間が多いと感じたので質問に至りました。

    でも今回のものでpreg_replace()の返り値をどうやってPOSTにいれようかというところでまた詰まってしまったので(「preg_replace('/xxx(.*)xxx/',$_POST['$1'],$html)」というのができないので)、やはりHTML_Saxでパースしようか・・・とも思ってます。
    今回の質問がやや無駄になってしまいますが・・・汗

  • id:tezcello
    PHPマニュアルのこのページは参考になると思います。
    http://jp2.php.net/manual/ja/function.preg-replace.php#id3491101

    他にもやり方はあるとは思いますが。

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

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

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

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