LoginSignup
4
3

More than 5 years have passed since last update.

【PHP】fscanfを繰り返し実行するときの謎動作

Last updated at Posted at 2016-10-21

前回のおさらい

PHPのfscanfの戻り値

  1. パラメータが2つだけ
    fscanf(STDIN,"%d %d");
    戻り値は配列

  2. パラメータにオプション指定有り
    fscanf(STDIN,"%d %d",$a,$b);
    戻り値は代入された値の数

パターンマッチしないケースも含めて表にまとめるとこうなる

入力 パターン 正常性 オプ無 オプ有
HKT 48 "%d %d" :x: array(null,null) 0
48 HKT "%d %d" :x: array(48,null) 1
HKT HKT "%d %d" :x: array(null,null) 0
48 48 "%d %d" :o: array(48,48) 2
(空行) "%d %d" :x: null -1

パラメータにオプションを指定したときの謎動作

たとえば、標準入力を以下の通りとする。

HKT 48
48 HKT
HKT HKT
48 48

これをlist()を使って、$a,$bに値を格納して、$a$bの状態を確認する。

<?php
list($a,$b)=fscanf(STDIN,"%d %d");
echo "a=",$a,",b=",$b,PHP_EOL;
list($a,$b)=fscanf(STDIN,"%d %d");
echo "a=",$a,",b=",$b,PHP_EOL;
list($a,$b)=fscanf(STDIN,"%d %d");
echo "a=",$a,",b=",$b,PHP_EOL;
list($a,$b)=fscanf(STDIN,"%d %d");
echo "a=",$a,",b=",$b,PHP_EOL;
list($a,$b)=fscanf(STDIN,"%d %d");
echo "a=",$a,",b=",$b,PHP_EOL;
a=,b=
a=48,b=
a=,b=
a=48,b=48
a=,b=

fscanfの戻り値を踏まえると、期待通りの動作といえる。

さて、いよいよ謎動作。fscanfのオプションを活用して、$a$bの状態を確認すると実に気持ち悪い

<?php
fscanf(STDIN,"%d %d",$a,$b);
echo "a=",$a,",b=",$b,PHP_EOL;
fscanf(STDIN,"%d %d",$a,$b);
echo "a=",$a,",b=",$b,PHP_EOL;
fscanf(STDIN,"%d %d",$a,$b);
echo "a=",$a,",b=",$b,PHP_EOL;
fscanf(STDIN,"%d %d",$a,$b);
echo "a=",$a,",b=",$b,PHP_EOL;
fscanf(STDIN,"%d %d",$a,$b);
echo "a=",$a,",b=",$b,PHP_EOL;
a=,b=
a=48,b=
a=48,b=     ← a=,b=  じゃないの?
a=48,b=48
a=48,b=48   ← a=,b=  じゃないの?

上書きしないで値を使いまわしてる!

これによって困ること

while(!feof(STDIN)){
    fscanf(STDIN,"%d %d",$a,$b);
    if($a>0 and $b>0){
        echo "Yes",PHP_EOL;
    }else {
        echo "No",PHP_EOL;
    }
}
No
No
No
Yes
Yes <= Noだろが!!

回避方法

1. 初期化してあげる

<?php
while(!feof(STDIN)){
    fscanf(STDIN,"%d %d",$a,$b);
    if($a>0 and $b>0){
        echo "Yes",PHP_EOL;
    }else {
        echo "No",PHP_EOL;
    }
    $a=$b=null;
}

2. リストを使う

<?php
while(!feof(STDIN)){
    list($a,$b)=fscanf(STDIN,"%d %d");
    if($a>0 and $b>0){
        echo "Yes",PHP_EOL;
    }else {
        echo "No",PHP_EOL;
    }
}

3. 空行なら処理終了

<?php
while(list($a,$b)=fscanf(STDIN,"%d %d")){
    if($a>0 and $b>0){
        echo "Yes",PHP_EOL;
    }else {
        echo "No",PHP_EOL;
    }
}

or

<?php
while(fscanf(STDIN,"%d %d",$a,$b)>-1){
    if($a>0 and $b>0){
        echo "Yes",PHP_EOL;
    }else {
        echo "No",PHP_EOL;
    }
}

and so on...

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3