1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Posted at

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

質問

teratail - C言語においてファイルから一列ずつ文字列を読み取り、特定の箇所を数値化する方法

回答

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

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

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

# 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

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

Man page of SCANF

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

# 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
# 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
# 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

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

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

# 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関数がそれに該当することに気づきます。質問者さんが書いていたソースコードをコピペしてそれらしいコードにしてみましょう。

# 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の空白行を無視するためにはどうすればいいでしょうか?

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

1
1
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
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?