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

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

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

CSV(Comma-Separated Values)はコンマで区切られた明白なテキスト値のリストです。もしくは、そのフォーマットでひとつ以上のリストを含むファイルを指します。

PHP

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

Q&A

解決済

4回答

534閲覧

【PHP】fgetcsvで、時刻表の『今から乗れる直近5件』を表示させたいのですが、終電間際、翌日の処理について教えてください。

arakuman

総合スコア17

CSV

CSV(Comma-Separated Values)はコンマで区切られた明白なテキスト値のリストです。もしくは、そのフォーマットでひとつ以上のリストを含むファイルを指します。

PHP

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

0グッド

0クリップ

投稿2019/07/10 11:02

前提・実現したいこと

お世話になります。
PHP2ヶ月目の初学者です。
CSV(UTF-8 コンマ区切り)で最寄り駅の時刻表を作りました。

5:17,大,
5:39,大,
5:50,宿,急


23:46,大,
23:56,大,
0:02,大,急
0:08,町,
0:16,大,
0:31,町,
0:48,大,

PHPを使用し、現在時刻以降直近の5件の行だけテーブルに出力したいと考えました。
『5件出力』は半日かかって何とか頑張りました。#もっとスマートな方法があれば合わせてお願いします。
終電間際、日付が変わった後の0:02などが当日のタイムスタンプと認識されてしまうので、これを翌日の0:02としたいと思いますがうまくいきません。

該当のソースコード

<?php $myTime = strtotime ("now"); //$myTime = strtotime("23:30:00"); $file = "sample.csv"; if ( ( $handle = fopen ( $file, "r" ) ) !== FALSE ) { echo "<table>\n"; $j = 0; while ( ( $data = fgetcsv ( $handle, 200, ",", '"' ) ) !== FALSE ) { if (strtotime($data[0]) >= $myTime){ $j++; if ($j > 5 ){ //直近の5件のみ表示 break ; } echo "\t<tr>\n"; $count = count( $data ); for ( $i = 0; $i < $count; $i++ ) { echo "\t\t<td>{$data[$i]}</td>\n"; } echo "\t</tr>\n"; } } echo "</table>\n"; fclose ( $handle ); } ?>

試したこと

if (strtotime ( $data[0] ) < strtotime( "3:00:00" ) ) { //始発より前の時間なら(仮に午前3時)
$data[0] = array_replace($data[0] , date("G:i" , strtotime( $data[0] . " +1 day" ) ) );//翌日に変更(??)
}

array_replace とかで配列を書き換え?と色々試しましたが、書き方がよくわかりません。
お忙しいところ恐縮ですが宜しくお願い致します。

#本当は時刻表っぽく
↓こんな感じにしてみたいところです…。急行なら赤とか。
17|39 43 53
18|00 11

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

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

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

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

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

guest

回答4

0

ベストアンサー

方向

strtotime()に与える引数にどうにかして正しい日付の情報を与えてあげれば解決します。

具体的には、次の日と判定された場合、引数に +1 dayが追加されるようにします。

PHP

1//こんな感じにすると、実行している日の次の日の0:01のunixタイムスタンプが取得出来る 2$time = strtotime("0:01 +1 day");

原理

まず

if (strtotime($data[0]) >= $myTime){
が何を意味しているかを理解する必要があります。

strtotimeをPHPマニュアルで確認すると、
strtotime — 英文形式の日付を Unix タイムスタンプに変換する
という関数です。

コード中で渡されている$data[0]は例えば
5:17だったり、0:48だったりして、日付に関する情報が含まれていません。
この場合、省略された日付に関しては本日の日付であると解釈され、
0:48を渡された場合、本日の0:48のUnixタイムスタンプが取得されます。

一方で、$myTime = strtotime ("now");では現在日時のUnixタイムスタンプが取得されます。

結果として23時に実行した場合、
if (本日の0:48のunixタイムスタンプ >= 本日の23:00のUnixタイムスタンプ){
という絶対に成立しない条件が作られてしまうため、日付が変わると表示されなくなります。

正しい日付を得るには?

CSVのフォーマットが質問にある通りに厳守されている

  • 先頭行に始発が記録されている
  • 最終行に終電が記録されている

という事であれば、
始発より早い時刻は全て日付変更後の時刻である
という事がいえるはずです。

であれば、こんな感じで条件を加えてあげて、日付変更後の時間に" +1 day"を付与してあげれば動くはずです。(現在のコードと比較して、どう変わっているか確認してみて下さい。)

PHP

1 2<?php 3 4$myTime = strtotime ("now"); 5//$myTime = strtotime("23:30:00"); 6 7$file = "sample.csv"; 8if ( ( $handle = fopen ( $file, "r" ) ) !== FALSE ) { 9 echo "<table>\n"; 10 11 $firstTime = null; 12 $j = 0; 13 while ( ( $data = fgetcsv ( $handle, 200, ",", '"' ) ) !== FALSE ) { 14 //ここで初回のループの時だけ始発の時間を変数に格納しておく 15 if(is_null($firstTime) === true){ 16 $firstTime = $data[0]; 17 } 18 19 //始発より時間が早いかどうか判定 20 if(strtotime($data[0]) < strtotime($firstTime)){ 21 $time = $data[0]." +1 day"; 22 }else{ 23 $time = $data[0]; 24 } 25 26 if (strtotime($time) >= $myTime){ 27 $j++; 28 if ($j > 5 ){ //直近の5件のみ表示 29 break ; 30 } 31 echo "\t<tr>\n"; 32 $count = count( $data ); 33 for ( $i = 0; $i < $count; $i++ ) { 34 echo "\t\t<td>{$data[$i]}</td>\n"; 35 } 36 echo "\t</tr>\n"; 37 } 38 } 39 echo "</table>\n"; 40 fclose ( $handle ); 41}

投稿2019/07/10 12:11

tanat

総合スコア18713

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

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

arakuman

2019/07/11 01:42

お忙しい中、ご返信ありがとうございます。 実はCSVの1行目に関して気持ち悪いけど放っておいた事があります。 var_dump($firstTime); で見ると"5:17"ときちんと始発の時間を取得しているのですが、 date("Y/m/d G:i",strtotime($firstTime)) を見ると"1970/01/01 9:00"なのです。 $data2 = date("Y/m/d G:i",strtotime($time)); var_dump($date2); では2行目から正しく表示されます。 $myTime = strtotime("04:30:00");とかにして見ると始発が表示されず、次発の"5:39"からだったので、1行目には”上り-平日”みたいな適当な文字列を入れ、2行目に始発を入力で対応していました。(ここでハマりました…) 家族が支度中に見るだけなので、切替の時間を"3:00"としてif文を以下にして、無事解決しました。 if(strtotime($data[0]) < strtotime("3:00:00")){ … } 『なんとなく時間っぽいただの文字列の$data[0]』に対して、$data[0]." +1 day" なんていう乱暴なことをしても大丈夫なんですね…。懐が広いというか…。 この度はありがとうございました。また多分お世話になるかと思いますが、その際は宜しくお願い致します。
arakuman

2019/07/12 00:42

やっぱり気持ち悪いので調べたところ、CSVの1行目の問題は原因がわかりました。 Excelで作成したので先頭にBOMというマークが付いていたんですね。 このまま2行目から読み込む感じにします。
guest

0

毎回ifで判定するからおかしくなるので、このデータなら開始点を見つけたら5回出力する処理に遷移すればいいのでは?

むしろ今が00:03のときに翌日の始発が表示されること、これをクリアしても翌日の始発が5つめに出てこないことのほうがネックになりそうです。

おそらく時刻表をみて作ったデータだと思いますが、質問者様もこう表示したいと仰るようにそれは分かりやすい見た目にした結果であって内部データがそういう形式をとる必然性があるでしょうか?(鉄道会社は一日券の概念があるので必然性があります)

単に0時から始まるデータであれば今のロジックでもよいはずです。
また、土日(祝)があるとは思いますが、金曜日、土曜日、日曜日、他の平日、などを持てば解決します。(これは静的に解決する方法で、動的に作るなら明日の曜日の0時以後と今日の曜日の0時前を連結すれば平易に作れると思います)

投稿2019/07/10 22:43

papinianus

総合スコア12705

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

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

arakuman

2019/07/11 01:41

お忙しい中、ご返信ありがとうございます。 家族が支度中に見るだけなので、『終電が終わったら空白、深夜3時を過ぎたら始発から表示』でもいいかなという感じでいます。 今後は、祝日判定をして平日と土日祝で拾ってくるCSVを変えて、金曜日の深夜、日付が変わった後は土日祝ダイヤを拾わないようにして…と課題は山積していますがチマチマやっていきます。 また多分お世話になるかと思いますが、その際は宜しくお願い致します。
papinianus

2019/07/11 13:19

質問者さまへの直接のお返事ではないです。 私はこの問題をデータ構造とアルゴリズムの問題だと捉えています。 データがソートされているとき、毎行の判定は無駄です(処理を遷移するよう提案した趣旨)。それはソートされていないデータに対してのアルゴリズムです。 逆にソートされていないなら原理的にfilter, sort, sliceの処理が必要であって、所与のコードのようにフィルタ中にsliceするようなことをしては、求まりせん(0時3分の例) なので、アルゴリズムをかえるかデータをソートされた状態にするかです。 アルゴリズムをかえるには、現在時刻との差分をとって、その差でソートして、正の差分の小さいほうから5つをスライスするという処理になります。また日付の概念をもたなければならず修正として複合的でやや複雑です。 わたしは日付情報がない状態でソートされたデータを作ろうと言っています。他のかたは、日付の概念を持ちこもうと言っています。 これは構造の問題であって、用途は関係ないです。
guest

0

いろいろやり方はありそうですが、時間を 300 - 2700 の数字として扱ってしまうのが楽な気がします。

まず CSV の時間表記を上記の数字に書き換えてしまいます。

比較の際は、現在時刻も上記の数字と同じく置き換え、大小を比較します。
条件合致から5件出力すれば完成です。

休日/祝日の切り替えは読み取る CSV そのものを切り替えることで対応するのが良いかなぁ。。。

表示の調整はフローを整理すればコードに落とすことが可能です。
フローの整理には図を書いてみるのが一般的な初学者のやり方です。

投稿2019/07/10 23:24

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

arakuman

2019/07/11 02:04

お忙しい中、ご返信ありがとうございます。 なるほど、そういった考え方もあるんですね。勉強になります。 表示方法はまだまだずっと先になってしまいそうですが、実現させたいと思っています。 (その前にまずCSSの勉強なのですが…) また多分お世話になるかと思いますが、その際は宜しくお願い致します。
guest

0

始発の前なら時間に24を足すってのではどうでしょう

何かで出力させるときには、24時以上は24引いて出力すればいいし。

投稿2019/07/10 12:04

y_waiwai

総合スコア87774

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

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

arakuman

2019/07/11 01:42

お忙しい中、ご返信ありがとうございます。無事に解決しました。 また多分お世話になるかと思いますが、その際は宜しくお願い致します。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問