printfデバッグを解説する記事？

teratailの質問に回答しようとしたものがブログ記事並みに長大になったのでQiitaのブログ記事にしましたｽﾞｺｰ

# 質問

[teratail - C言語においてファイルから一列ずつ文字列を読み取り、特定の箇所を数値化する方法](https://teratail.com/questions/271563)

# 回答

printfデバッグの手法を学んでください。あと、C言語の標準関数の使い方をすぐ探す方法についてもですね。これからそれを実演します。

まず、この文字列`2014/1/1,0:00,128`について、`128`を数字で取り出す方法ですね。

まず一番最初に思いつくのはscanf関数。scanf関数はユーザーの入力を受け付けます。そのバリエーションとしてfscanfとsscanfがあるのをご存知でしょうか。sscanfは文字列を入力とします。sscanfで強引に文字列をリード（パース、解釈）してみます。

```C
#include <stdio.h>

int main() {
    int a, b, c, d, e, f, g;
    sscanf("2014/1/1,0:00,128",
           "%d/%d/%d,%d,%d:%d,%d",
           &a, &b, &c, &d, &e, &f, &g);
    printf("a = %d\n", a);
    printf("b = %d\n", b);
    printf("c = %d\n", c);
    printf("d = %d\n", d);
    printf("e = %d\n", e);
    printf("f = %d\n", f);
    printf("g = %d\n", g);
    return 0;
}
```

実行結果

```
a = 2014
b = 1
c = 1
d = 0
e = 21911
f = -1907481120
g = 32767
```

最初の４つはうまくいっているようですが、後ろの方はうまく数字が拾えていないようです。どうしてでしょうか。scanfのマニュアルページをながめてもヒントになりそうなものはありませんでした。

[Man page of SCANF](https://linuxjm.osdn.jp/html/LDP_man-pages/man3/scanf.3.html)

`/`か`,`か`:`が悪さをしているのでしょうか。３つのバージョンを試してみます。ちなみにscanf(scanf系の関数)の返り値も重要らしいのでこれもprintfしておきます。

```C
#include <stdio.h>

int main() {
    int a, b, c, d, e, f, g;
    int retval;
    retval = sscanf("2014/1/1/0/00/128",
           "%d/%d/%d/%d/%d/%d/%d",
           &a, &b, &c, &d, &e, &f, &g);
    printf("a = %d\n", a);
    printf("b = %d\n", b);
    printf("c = %d\n", c);
    printf("d = %d\n", d);
    printf("e = %d\n", e);
    printf("f = %d\n", f);
    printf("g = %d\n", g);
    printf("retval = %d\n", retval);
    return 0;
}
```

実行結果

```
a = 2014
b = 1
c = 1
d = 0
e = 0
f = 128
g = -2079744000
retval = 6
```

```C
#include <stdio.h>

int main() {
    int a, b, c, d, e, f, g;
    int retval;
    retval = sscanf("2014,1,1,0,00,128",
           "%d,%d,%d,%d,%d,%d,%d",
           &a, &b, &c, &d, &e, &f, &g);
    printf("a = %d\n", a);
    printf("b = %d\n", b);
    printf("c = %d\n", c);
    printf("d = %d\n", d);
    printf("e = %d\n", e);
    printf("f = %d\n", f);
    printf("g = %d\n", g);
    printf("retval = %d\n", retval);
    return 0;
}
```

実行結果

```
a = 2014
b = 1
c = 1
d = 0
e = 0
f = 128
g = 934701248
retval = 6
```

```C
#include <stdio.h>

int main() {
    int a, b, c, d, e, f, g;
    int retval;
    retval = sscanf("2014:1:1:0:00:128",
           "%d:%d:%d:%d:%d:%d:%d",
           &a, &b, &c, &d, &e, &f, &g);
    printf("a = %d\n", a);
    printf("b = %d\n", b);
    printf("c = %d\n", c);
    printf("d = %d\n", d);
    printf("e = %d\n", e);
    printf("f = %d\n", f);
    printf("g = %d\n", g);
    printf("retval = %d\n", retval);
    return 0;
}
```

実行結果

```
a = 2014
b = 1
c = 1
d = 0
e = 0
f = 128
g = -1325543248
retval = 6
```

おっと？ちょっとわざとくさくもありますが７個数字を拾うつもりが６個数字を拾っていたようですね。さっきのプログラムにそれを反映させます。

よく見てみると対象の文字列に合わせて書いてたつもりのフォーマットも微妙に間違ってたようなので直します。

```C
#include <stdio.h>

int main() {
    int a, b, c, d, e, f;
    int retval;
    retval = sscanf("2014/1/1,0:00,128",
           "%d/%d/%d,%d:%d,%d",
           &a, &b, &c, &d, &e, &f);
    printf("a = %d\n", a);
    printf("b = %d\n", b);
    printf("c = %d\n", c);
    printf("d = %d\n", d);
    printf("e = %d\n", e);
    printf("f = %d\n", f);
    printf("retval = %d\n", retval);
    return 0;
}
```

実行結果

```
a = 2014
b = 1
c = 1
d = 0
e = 0
f = 128
retval = 6
```

変数fに目的の数字が拾えてるようですね。これをCSVファイルに応用できないでしょうか？例えばscanfをしまくる方法です。それでもいいのですが、一行拾ってsscanfに流していったほうが安全な気がします。Cで一行読み込む方法を検索するために「C言語　一行読み込む」で検索します。すると質問者さんが使っていたfgets関数がそれに該当することに気づきます。質問者さんが書いていたソースコードをコピペしてそれらしいコードにしてみましょう。

```C
#include    <stdio.h>
#include     <string.h>
#include <stdlib.h>
#define MAX 100

int    main(void)
{
    char s[MAX];
    int a, b, c, d, e, f, retval;

    FILE *fp=fopen("year-2014.csv","r");

        while(fgets(s,MAX, fp)!=NULL){
            retval = sscanf(s, "%d/%d/%d,%d:%d,%d", &a, &b, &c, &d, &e, &f);
            if (retval == 6) {
                printf("f = %d, f+3 = %d\n", f, f + 3);
            } else {
                printf("この行は正しく読めませんでした。\n");
            }
        }
    fclose(fp);

    return    0;
}
```

実行結果

```
f = 128, f+3 = 131
f = 131, f+3 = 134
f = 255, f+3 = 258
f = 245, f+3 = 248
f = 239, f+3 = 242
f = 239, f+3 = 242
f = 247, f+3 = 250
f = 260, f+3 = 263
f = 271, f+3 = 274
この行は正しく読めませんでした。
```

このプログラムは概ね正しく動いているようです。

### 研究課題

さて、先ほどのプログラムでは一行だけ解釈できない行がありました。私がCSVの最後で改行したためです。そこで問題です。CSVの空白行を無視するためにはどうすればいいでしょうか？

[ヒント]　改行しただけならその行の長さは０になるはずです。文字列の長さを取得するCの標準関数はなんですか？