• 締切済み

正規表現パターンの記入方法について

とあるサイトで、 「URL からドメイン名を得る」という項目があったのでマネして やってみたらうまくいきました。しかし正規表現パターンの意味は 理解できませんでしたので理解できなかった部分だけをのせたスクリプトを以下にまとめました。 <?php // まずUPLからホスト名を得る preg_match('@^(?:http://)?([^/]+)@i', "http://www.nantoka.com/index.html", $matches); $zenbu=$matches[0]; $host = $matches[1]; $saigo=$matches[2]; /*必要なのはホスト名だけですが、$matches[0]や$matches[2]には どんな文字列が格納されるか気になって出力することにしました*/ print($zenbu); print("<br/>"); print($host); print("<br/>"); print($saigo); print("<br/>"); ?> これを実行した結果は、 $zenbuが「http://www.nantoka.com」で、 $hostが「www.nantoka.com」で、 $saigoがなにもなしでした。 このスクリプトでわからない部分は、'@^(?:http://)?([^/]+)@i'の部分と、$host = $matches[1];の部分です。 まず正規表現パターンの最初のアットマークの後ろの^は「次の文字列からはじまる」と解釈しました。 最後のアットマークの後ろのiは、「大文字と小文字を区別しない」という意味だと解釈しました。 カッコで囲まれている(?:http://)と、([^/]+)は、サブマッチパターン だと思いました。 $matches[0]には、"http://www.nantoka.com/index.html"の 中で'@^(?:http://)?([^/]+)@i'に当てはまるもの全体が格納され、 $matches[1]には、(?:http://)に当てはまるもの、 $matches[2]には、([^/]+)に当てはまるものが格納されると考えました。 [^/]+は、「スラッシュを含まない文字が1文字以上」と解釈しました。 ただ、その他の事については考えましたがよくわからず、 特になんで(?:http://)にあたるものが「www.nantoka.com」 になるのかさっぱりわかりません。 (?:http://)の中にあるhttp://の前の?:が一体何なのか、 (?:http://)と([^/]+)の間にある?は何なのか、 両端のアットマークは何なのか(マッチ演算子かと思って スラッシュに置き換えて実行してみたらエラーになりました。) うまく説明できませんがとにかくその辺のことがよくわかりませんでした。どなたか教えていただけませんか。

  • ossu
  • お礼率75% (36/48)
  • PHP
  • 回答数3
  • ありがとう数3

みんなの回答

  • eitetsu
  • ベストアンサー率64% (22/34)
回答No.3

と、その他の部分も補足します。 まず、@ですが、正規表現のマッチングの場合、マッチング対象を囲みます。 通常は、「/」を使うのですが、今回のマッチング文字列の中に「/」が含まれているため、代替として「@」を使っているのだと思います。 マッチ演算子として、「/」に戻した場合、内部の「http://」の「/」を¥マークでエスケープしないとエラーになってしまうと思います。 つづいて、(?:http://)の後の?ですが、これは「この?の前にあるブロックが、0~1回出てくること」という条件になります。 なので、http://http://aaaという文字列を入れた場合、http://を1回分だけよりわける、ということが出来るようになります。

ossu
質問者

お礼

エラーの詳細まで教えていただき感謝しています。 http://の後の?についても確かめてみました、 (なんのためにhttp://が2つもあったりhttp://がなかったりするのか はよくわかりませんが、検索したら実際にそういうものがありますね。)

  • sakusaker7
  • ベストアンサー率62% (800/1280)
回答No.2

@はここでは、正規表現の始まりと終わりを示すものとして使われています。 通常この目的には '/' が使われることが多いのですが、今回マッチングに 使用するパターンに '/' が複数含まれているのでエスケープのバックスラッシュを 多用することによって見づらくなるのを避けるために'@' を使用したのでしょう。 $matches[0], $matches[1], $matches[2], ... ですが 0 → パターンにマッチした全体 1 → 1番目のサブパターンに捕獲された部分文字列 2 → 2番目のサブパターンに捕獲された部分文字列 となります。ここで、(?: ) と ( ) とは違うものだということに注意してください。 (?: ) によりグループ化された場合、捕獲はなされません。つまり、 > $matches[1]には、(?:?http://)?に当てはまるもの、 > $matches[2]には、([^/]+)に当てはまるものが格納されると考えました。 これがひとつずれます。$matches[1] には ([^/]+)によって捕獲されたものであり、 このパターンには捕獲を行うグループ化はひとつしかありませんので、 $matches[2]は常に空になります。 順番が前後しましたが、'(?:' の三文字で捕獲をしないグループ化を表す メタ文字になってます。ですので '?:' それ自体に意味はありません。

ossu
質問者

お礼

「捕獲をしないグループ化」というところが非常にわかりやすくて頭の中にしっかり残りました。ありがとうございます。

  • eitetsu
  • ベストアンサー率64% (22/34)
回答No.1

まさにossuさんがわからない、といっていた「?:」がこの部分の肝となります。 (?:~~~)とすると、この括弧の部分を検索時のマッチ対象から外すことが出来るようになります。 このため、$matches[1]に本来、$matches[2]に入る内容が入ってくることになります。 詳しくは、PHPマニュアルをご覧下さい。

参考URL:
http://search.net-newbie.com/php/reference.pcre.pattern.syntax.html
ossu
質問者

お礼

ありがとうございます。まさかそんなことが出来るとは思いませんでした。

関連するQ&A

  • 改行や、タブを含む正規表現の方法

    HTMLからある部分抜き出す時、 たとえば、あるソースから <title>なんたら</title> という言葉を抽出する場合、 $htmlに全ソースが格納されていると仮定して 単純な正規表現で preg_match('/<title>なんたら</title>/', $html, $matches); といえれると、"<title>なんたら</title>"という文字が出せます。 ところが <title>   なんたら </title> のような改行やらタブが含まれていると抜き出すことができませんでした。 いろいろ調べますと、文字エスケープシーケンスというものがあり \nは改行 \tタブと書いてあるサイトがありましたので preg_match('/<title>\n\tなんたら\n</title>/', $html, $matches); という風に記述してみました。 しかし、それでも改行とタブの含まれた<title>から 文字を抜き出すことはできませんでした。 改行とタブの含まれた<title>から 文字を抜き出すには、どのような記述をすれば良いのか ご指導お願いいたします。

    • ベストアンサー
    • PHP
  • 特定文字列の取り出し方 複雑な正規表現

    お世話中になります。 特定文字列の取り出し方が分かりません。 たとえば、下記のような文字列があります。 == $contents ======================================== <img alt="Image813.jpg" class="pict" height="320" src="http://xyzxyz.com/20111101_123.jpg" width="240" /><br /> もう後2ヶ月です!!<br /> 早いですね。<br /> <br /> 写真はココちゃんです!<br /> <img alt="DSC_0052.JPG" class="pict" height="320" src="http://xyzxyz.com/20111101_456.jpg" width="240" /><br /> 今年の夏から発売し、たくさんのお客様に<br /> 食べていただいているソフトクリーム。<br /> <br /> 今のところ人気ランキングはこんな感じです。<br /> ちなみにソフトクリームは真冬の最高気温が5度の日でも<br /> 販売し続ける予定です!<br /> ================================================== この$contents という文字列から 「http://xyzxyz.com/20111101_123.jpg」 「http://xyzxyz.com/20111102_456.jpg」 を取り出し、ぞれぞれ配列に入れる場合の 正規表現はどのように記述すればいいですか? 下記の文字列を $contents として、 正規表現を $pattern 結果を $matches とします。 お忙しいところ 申し訳ありませんが、 よろしくお願いします。

    • ベストアンサー
    • PHP
  • 正規表現(~を含まないものにマッチ)

    Perlの正規表現について質問です。 画像<img src="http://hogehoge.com/img/gokuu.jpg" border=0><a href="http://hogehoge.com/">hogehoge</a><br> 画像<img src="http://hogehoge.com/img/pikkoro.jpg" border=0><a href="http://hogehoge.com/">hogehoge</a><br> 画像<img src="http://hogehoge.com/img/bejiita.jpg" border=0><a href="http://hogehoge.com/">hogehoge</a><br> 画像<img src="http://hogehoge.com/img/gohan.jpg" border=0><a href="http://hogehoge.com/">hogehoge</a><br> 画像<img src="http://hogehoge.com/img/buruma.jpg" border=0><a href="http://hogehoge.com/">hogehoge</a><br> ↓ 処理後 ↓ 画像<img src="gazo_new1.jpg" border=0><a href="http://hogehoge.com/">hogehoge</a><br> 画像<img src="gazo_new2.jpg" border=0><a href="http://hogehoge.com/">hogehoge</a><br> 画像<img src="gazo_new3.jpg" border=0><a href="http://hogehoge.com/">hogehoge</a><br> 画像<img src="gazo_new4.jpg" border=0><a href="http://hogehoge.com/">hogehoge</a><br> 画像<img src="gazo_new5.jpg" border=0><a href="http://hogehoge.com/">hogehoge</a><br> (変更した画像:gokuu.jpg,pikkoro.jpg,bejiita.jpg,gohan.jpg,buruma.jpg,) 以上のように 画像URL部分のみを gazo_new(連番).jpg に変更させ、 変更前の画像URL一覧を表示させるスクリプトを書いてみました。 $htmldata にHTMLの全データが格納されているとします。 $i=1; while($htmldata=~/(<\s*img.*?src.*?=["\s]*)([.\/]*?\/)?((?:(?!gazo_new)[^"\s>])*)?["\s]?.*?>/i){ $imgurlparts .="$3,"; $htmldata=~ s/(<\s*img.*?src.*?=["\s]*)((?:(?!gazo_new)[^"\s>])*)?(["\s]?.*?>)/$1gazo_new$i.jpg$3/i; $i++; } $htmldata .="(変更した画像:$imgurlparts)"; どうにもうまく動きません。 何卒ご教示をお願い致します。

    • ベストアンサー
    • Perl
  • 正規表現<置き換え>が実行してくれません

    次のようなスクリプトで、改行マーク\nがたくさんある1行データを<BR>に置換しようとしましたが、表示は\nのままです。原因が分かりません。たぶんこの肝心の箇所のスクリプト・エラーだと思いますが、自分で発見できません。どなたかお教え下さい。 print "Content-type: text/html; charset=sjis-jp\n\n"; print "\n"; print "<HTML>\n"; print "<HEAD>\n"; print "<TITLE></TITLE>\n"; print "</HEAD>\n"; print "<BODY>\n"; # 辞書を読む #ファイルの各行を配列に一括して読み込む。 open(IN, "GermJ.txt"); @list = <IN>; close(IN); $max = $#list; for ($i=0; $i<$max; $i++) {$data = @list[$i]; if (index($data, $tango.' /// ')==0) {$data=~ s/ \n/<BR>/g; print "$data\n"; last; } } print "</BODY>\n"; print "</HTML>\n"; __END__

    • ベストアンサー
    • Perl
  • mod_rewrite的なことをphpで実現させたいが空白ページが表示されてしまう

    www.example.com/ほげほげ にアクセスされたら、 example.com/ほげほげ へ urlをかきかえ、 さらに、 $_SERVER["REQUEST_URI"] がスラッシュで終わる(2個以上のスラッシュも考慮)ときは、これらのスラッシュをとりのぞいたurlへかきかえ、 そして、このurlへ301ステータスコードでリダイレクトさせる(ただし、get methodの場合に限る) というコードをphpで書いたのですが、 なぜか、urlは書き換わらず、空白のページが表示されてしまいます。 どこがいけないのでしょうか? お助けくださいませ。         if ($_SERVER["REQUEST_METHOD"] == "GET"){             #HTTP_HOST (exclude www.)             preg_match_all("/^(www\.)(.*)$/", $_SERVER["HTTP_HOST"],$matches_HTTP_HOST);             if ($matches_HTTP_HOST[1] != ''){                 $bWillBeRedirected = true;                 $strAfterRedirect_HTTP_HOST = $matches_HTTP_HOST[2];             }             else{                 $strAfterRedirect_HTTP_HOST = $_SERVER["HTTP_HOST"]             }             #REQUEST_URI (,which includes query string)             preg_match_all("/^(\/)(.*)(\/+)$/", $_SERVER["REQUEST_URI"],$matches_REQUEST_URI);             if ($matches_REQUEST_URI[3] != ''){                 $bWillBeRedirected = true;                 $strAfterRedirect_Request_URI = $matches_REQUEST_URI[1] . $matches_REQUEST_URI[2];             }             else{                 $strAfterRedirect_Request_URI = $_SERVER["REQUEST_URI"];             }             #redirect with 301 status code if needed             if($bWillBeRedirected){                 #Protocol                 if ($_SERVER["HTTPS"] == "on"){                     $strProtocol = "https";                 }                 else{                     $strProtocol = "http";                 }                 $url = $strProtocol . "://" . $strAfterRedirect_HTTP_HOST . $strAfterRedirect_Request_URI;                                                   header("HTTP/1.1 301 Moved Permanently");                 hearder(”Location: “.$url);                 header(”Connection: close”);             }         }

    • ベストアンサー
    • PHP
  • 正規表現を用いてHTML内の文字列を抜き出したいのですが・・・

    PHPの正規表現を用いて外部Webページのソース内にある文字列を抜き出してきたいのですがうまくいきません。 例えば、 <td class="nml">食べてきれいにやせる! 伊達式脂肪燃焼ダイエット / 伊達友美<br></td> のようなタグに挟まれた文字列を抜き出します。自分で書いたプログラムは以下の通りです。 <?php //外部URL $fp = fopen("特定のURL", "r"); while(!feof($fp)){ //HTMLソースを全文取得 $line = fgets($fp, 1024); //各行を配列に格納 $line_array[] = $line; } //配列を一つの文字列に変換 $line_text = implode("", $line_array); if(preg_match_all('ここの部分が思いつきません', $line_text, $match)){ print $match; }else{ print 'マッチしません'; } ?> 色々と調べて試行錯誤しましたが、結局うまくいきませんでした。 正規表現となる部分をどなたかご教授頂けないでしょうか。宜しくお願い致します。

    • ベストアンサー
    • PHP
  • スクリプトの中から使用されている変数と関数をリストする

    phpで書かれたスクリプトファイルの中から使用されている関数と変数をリストアップさせるようなことをしたいのですが、preg_matchを使うのが妥当でしょうか?また、正規表記はどんな感じがベストでしょうか?自分で試してみてもまったくへたくそでして... トホホ ぜひ皆さんの技量を拝見させてください ^^。 <?php $data = file_get_contents('~.php'); $functions = '/\s*\(*/i'; preg_match($functions, $data, $matches); print_r($matches); echo "<br>"; $variables = '/^\$[.+]/i'; preg_match($variables, $data, $matches2); print_r($matches2); echo "<br><br>"; echo $data; ?>

    • ベストアンサー
    • PHP
  • 正規表現・lexによる字句解析器

    lexソースの中に次のようなコードが出てるんですが、左側の正規表現の意味がわからなくて困っています。文字列の解釈のコードだと思います。 \はバックスラッシュだと思ってください。 \"[^"]*\" { yytext[yyleng - 1] = '\0'; yylval.str = strdup(&yytext[ 1]); return(STRING); } (yaccのソースの中に&token <str> STRINGという記述が入っている) 文字列はダブルクォーテーション「"」で囲み、間に改行が入っても構わないそうです。 yytextは次の入力を処理するときに破壊されるので、ヒープ上に確保した領域に文字列を格納し、そのアドレスをyylval.strに渡す。初めと終わりの「"」は取り除く・・ のだそうです。おそらくstrdupの説明だと思うのですが、C言語のマニュアルには載ってなかったですね・・・。

  • postgreSQLの正規表現

    いつもお世話になってますurizakaです。 さて、現在postgreSQL7.1(環境はLinux、言語はJava)を使ってツールを作って いるのですが、その中でIPアドレスとそのドメインを入力したテーブルm_hostが あるのですが(以下のような内容のデータが入っています) ip domein 128.155.35.42 http://www.sample.ne.jp 128.166.34.69 http://www.sando.com/index.html 199.231.12.36 http://paoo.co.jp/search?q=axieosenoe これらのテーブルから、domeinの「http://」の後で、次の「/」までの部分で、 最後の文字部分がjpのものが何件あるか?といったデータを集計したいの ですが、これはどのようにすればよいのでしょうか?JavaやVBだったら、 「/」で区切った後に、「.」で区切って最後の配列の要素を…といったプログラム を組むのですが、なるべくDB側で処理してしまいたいのです。 postgreSQLには正規表現の機能があるそうなので、それを使って結果を集計 できればと思うのですが… すみませんが、宜しくお願いします。

  • 正規表現のタイプがわからない(初心者)

    正規表現で置換ができることを知り、こういうサイトを見つけました。 @IT:Windows TIPS -- Tips:複数ファイルの文字列置換をワンクリックで行なう http://www.atmarkit.co.jp/fwin2k/win2ktips/308strrpl/strrpl.html そこでreplace.wsfを作りましたが、正規表現がうまくいきません。 replace.wsfは正規表現を使う場合は "/ /g" で囲うらしいのですが urlのようなスラッシュ / が多いものを円記号 \ でエスケープしましたがうまくいきません。 さらに http://msdn.microsoft.com/ja-jp/library/aa293063(VS.71).aspx というサイトで任意の文字をドット . で置換したいんですができません。 以下書いたものを載せます 文字はアンダーバー _ で書いています strRepl="/http:[\/][\/]______.____.jp[\/]____/____[\/][0-9][0-9][0-9][0-9][\/][0-9][0-9][\/][0-9][0-9]/g"; /* 置換対象文字列 */ [\/]はかっこなしでも試しましたができませんでした [0-9]の部分は年月日が入ります。 replace.wsfで正規表現を実現させることができません。 正しい方法を教えてください!!