質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

関数

関数(ファンクション・メソッド・サブルーチンとも呼ばれる)は、はプログラムのコードの一部であり、ある特定のタスクを処理するように設計されたものです。

正規表現

正規表現とは特定の文字列によるパターンマッチングを行う際に用いられる宣言型プログラミングです。

Q&A

2回答

4959閲覧

今日までを正規表現でマッチさせる方法

aaaaaaaa

総合スコア501

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

関数

関数(ファンクション・メソッド・サブルーチンとも呼ばれる)は、はプログラムのコードの一部であり、ある特定のタスクを処理するように設計されたものです。

正規表現

正規表現とは特定の文字列によるパターンマッチングを行う際に用いられる宣言型プログラミングです。

0グッド

0クリップ

投稿2016/08/04 11:11

下記のソースは、今日(2016/8/4)まで且つ現実的な西暦と月日に可能な限りマッチさせたい正規表現です。
想定では、「/」「.」「-」で区切られた西暦と月日(2016/1/1や、2016.1.1など)がマッチしたらXXXX年XX月XX日に変換しようとしています。

ただ、下記のソースを見ていただけると解かりますが、$seirekiのような値を代入してもマッチしてしまいます。つまり2550年10月39日のようなありえない西暦と月日もマッチしてしまいます。
2016年8年4日以降と39日のようなありえない日にちにマッチしない正規表現は、作れますか。

また、単語境界をいれないと、2550.10-399と9を一つ多めに足しても「正しい西暦と月日を入力しよう」という画面が出てこなくなります。2555.1000.3999だと単語境界を使わなくても
「正しい西暦と月日を入力しよう」とでてくるのですが、なぜ一桁大目に足しただけだと単語境界が必要になるのでしょうか。

php

1$seireki = "2550.10-39";//変な年、日にちでもマッチしてしまう。 2 3if(preg_match("/\b([1-2]\d\d\d)[\/.\-]([0-1]*[0-9])[\/.\-]([0-3]*[0-9])\b/",$seireki,$data)) { 4print "<p>".$data[1]."年".$data[2]."月".$data[3]."日</p>"; 5print "<p>全体:".$data[0]."</p>"; 6 7} else { 8print "正しい西暦と月日を入力しよう"; 9}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

うるう年などdateの持つ複雑さを正規表現でパターン化するのは難しいのではないかと思います。
正規表現で取り出した「日付と思しき」文字列をifとcheckdateなどで別途判別するのが簡単かと。

###追記(2016.08.10)

2555.1000.3999だと単語境界を使わなくても「正しい西暦と月日を入力しよう」とでてくる

php

1$seireki = "2550.1000-3999";//変な年、日にちでもマッチしてしまう。 2if(preg_match("/([1-2]\d\d\d)[\/.\-]([0-1]*[0-9])[\/.\-]([0-3]*[0-9])/",$seireki,$data)) { 3 echo "<p>".$data[1]."年".$data[2]."月".$data[3]."日</p>"; 4} else { 5 echo "正しい西暦と月日を入力しよう"; 6}

paizaでこのコードを試してみましたが、出力は「<p>2550年1000月39日</p>」でした。2550.10.399でも「<p>2550年10月39日</p>」です。
「*」は直前の文字の0回以上の繰り返しなので、
月のところは00009にも117にもマッチしますが、123にはマッチしません。0か1が複数あった後に0-9のどれかが来る塊って意味ですね
日の所も、0-3のどれかが複数続いた後、0-9のどれかが1文字やってくる塊って意味です。

もしかすると、「/\b([1-2]\d\d\d)/.-/.-\b/」これの方が目的に一致しているかも知れませんね。{0,1}は直前の文字が無いか1つだけ存在するって意味です。
この場合は単語境界を書くことで、12009.01.20や2009.02.299をNGにすることが出来ます。

投稿2016/08/04 11:33

編集2016/08/10 12:19
hirohiro

総合スコア2068

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hirohiro

2016/08/10 12:20

すでにyambejpさんが詳細に記述されているので蛇足とは思いますが、質問後半部分への回答が抜けていたので追記してみました
guest

0

  • まずマッチさせる
  • 日付として整合性をチェック
  • 今日と比べる

PHP

1<?PHP 2$str = "test2016/10/10 2016/08/111 12016/08/111 2550.10-39 2016/1/1 2016.2.2 2016/08/04 2016/08/05 2016.10.23 2016.10.33 2016/13/23test"; 3/* 以下修正前 4$pattern="/(?!=\d)((\d{4})([\.\/])(\d{1,2})(?:\\3)(\d{1,2}))(?!\d)/"; 5*/ 6$pattern="/(?<!\d)((\d{4})([\.\/])(\d{1,2})(?:\\3)(\d{1,2}))(?!\d)/"; 7$replacement=function($a){ 8 if(!checkdate($a[4],$a[5],$a[2])){ return "$a[1](不正な日付)";} 9 if(date("Y-m-d") < date("Y-m-d",strtotime("{$a[2]}/{$a[4]}/{$a[5]}"))){return "$a[1](今日以降)";} 10 return $a[2]."年".$a[4]."月".$a[5]."日"; 11}; 12$str=preg_replace_callback($pattern,$replacement,$str); 13print $str; 14?>

すみません、パターンが記載ミスしていましたので修正します

解説

$pattern="/(?<!\d)((\d{4})([./])(\d{1,2})(?:\3)(\d{1,2}))(?!\d)/";

  • (?<!\d):否定後読み
  • (?!\d):否定先読み

それぞれ日付データの前後に数字がないことを保証します

    1. ((\d{4})([./])(\d{1,2})(?:\3)(\d{1,2}))を分解して
  • 2)(\d{4}):年に当たる箇所は4桁の数字
  • 3)([./]):日付のセパレータはピリオド「.」かスラッシュ「/」
  • 4)(\d{1,2}):月は1桁または2桁の数字
  • 5)(?:\3):前回出てきたのと同じセパレータ(3番めのカッコの中身)
  • 6)(\d{1,2}):日には1桁または2桁の数字

あとは年と月と日のデータの組み合わせです

投稿2016/08/04 12:28

編集2016/08/09 06:56
yambejp

総合スコア114775

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

aaaaaaaa

2016/08/08 10:53

ご回答有難うございます。 私の読解力の問題かもしれませんが、$patternの正規表現は、恐らく否定後読みを使って西暦の前に数字が無く、ドット或いはスラッシュ、数字が一回以上二回以下が二回続き、最後に否定先読みで、まだ読んでない部分に数字が無ければマッチするものだと思っております。 重ね重ね申し訳ありませんが、正規表現内のグループ化括弧にはどういった意味があるのでしょうか。
yambejp

2016/08/09 06:56

解説を追記しておきました パターンが間違っていてすみません
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問