🦔

fgetsだの配列だので困ってたらwhileを理解した話。

2023/12/17に公開

正直ある程度文法理解して単純な2-3行のコードなら書けるつもりでいたんだけど、paizaで結構躓く。
躓いてるのに楽しい。

前回大体の場合

$test = fgets(SDTIN)

echo $test

これ覚えてれば困らないっていったけど、間違い。

どういうことかっていうと

おはようございます
こんにちは
こんばんは

こんな内容だったとき

$test = fgets(SDTIN)

echo $test
出力結果:おはようございます

なんでだよ全部持ってきてくれよ。
そう標準入力されている文字が2列以上あった場合対抗する知識がない。

先人の方たちも困ってる人を見たのか、自分が困ったのか、結構情報がある。
僕の書き方のほうがわかりやすいっていう稀有な人もいるかもしれないから、書いとく。

答えだけ知ってもきっと身に付かないのでfgetsという憎きやつから説明させてほしい
このfgets君だが、実行されるとファイルの一番上の文字列を引っ張ってくる。
なんとそれで終わりなのだ、仕事しろよ。

だから上記のように"おはようございます"しか持ってきてくれないのだ。
ここで分からなくなって検索して、解決の方法はわかったけど何で解決したのかわかんなくなった。while使って配列にしまえばいいよってやつ。

ループ文だから何回も取ってきてくれる、だから解決する。それは分かった。
だけど書式がなんかいまいちピンとこなくて、モヤモヤ。

でもループ分で解決するなら出したい数だけ自分で読んで、格納する変数複数用意すればいいじゃん。
って思って試したのが

$a = fgets(STDIN);
$b = fgets(STDIN);
$c = fgets(STDIN);

echo $a, "\n", $b, "\n", $c;
出力結果
おはようございます
こんにちは
こんばんは

なんだ、いけるじゃん。
何行目の文字列引っ張ってきたかはちゃんと記憶してるらしい。

てことは先人がやってたwhileってのは、何回も呼ぶのがめんどいから条件文でループさせて
何回も違う変数用意したり、そこから取り出すのが面倒だから配列に入れてたんだな。

よしもう1回理解しようとしてみよう。

ここで先人の皆さんがよくfgetsで複数行のデータを配列に格納するのに使うコードを見てほしい。

<?php
$a = array();
while($b = fgets(STDIN)) {
    array_push($c, $b);
}

僕が躓いたのはwhile($b = fgets(STDIN)) この部分。

$bにfgetsで引っ張ってきたものが代入されるのは分かる。

わかるけど、この条件式が正しいかどうかってわかんないからループしないじゃん。って思って躓いた。
1回目fgetsが実行されると引っ張ってくるのは 'おはようございます' trueでもflaseでもない。

つまり2週目のwhileの条件分は 'おはようございます' = fgets である。

意味がわからない、なんでループするんだって躓いた。

でもこれにはカラクリがちゃんとあって、whileってのはループが始まると、条件文の結果がfalseで無い限りループを続けるってこと。

fgetsが働く限りループは終わらない。

しかもfgets君は引っ張れる文字列がなくなったときにfalseを返してくれる。

つまりこの1文でfgetsで取得するべき文字列を全て取得できる、終わったらエラーもはかずにループは終了する。

もうここまで分かれば残りは簡単だ

    array_push($c, $b);

array_pushは1個目で指定された配列の中に2個目で指定したデータを格納する。

今回の場合は変数cに変数bのデータを格納していく

whileでループさせているのでfgetsで取ってきた文字列を変数bに代入にして、変数bに代入されていたデータを変数cという配列の中に次々押し込んでいく。

ループが終われば変数cの中には取ってきたデータが取ってくるべきデータの数だけはいっている。

あとはせっかく配列に格納したのだから、配列を扱える関数 foreachさんにご登場頂き

foreach ($c as $d){
  
  echo $d, "\n";

こうしてやればfgetsに格納されるデータ数が何個であろうが、個数分出力できるようになる。

ああ、楽しかった。

Discussion