scanfの使い方を教えてください

[戻る]
HAL 2008/04/12(土) 23:27:38
お世話になります、下記のようにコンソール画面からキー入力をscanf()で受け
ファイルに書き出したいのですが、int型のデータが期待した動作となりません
(1234と入力しても1234と書き出しできません)
プロジェクトのプロパティーから文字セットはマルチバイト文字を選択しています

また、scanf("%d%*c", &shurui);
ここでの抑制文字 *の意味なのですが、int型以外の文字を受け付けないように
するためという解釈で使っているのですが、この認識は正しいのでしょうか
自信がありません、間違っていたらご指摘願います。

以上よろしくお願いいたします。

VS2005 VC++ Express

#include <stdio.h>

void touroku(int shurui);

int main(void)

{
  int shurui;
  char kakunin;

  printf("\n           <<< 商品マスタ更新 >>>\n\n");
  printf("           新規登録=1、追加登録=2    ");
  scanf("%d%*c", &shurui);
  do {
    touroku(shurui);
    printf("             続けますか?(y/n)        : ");
    scanf("%c%*c", &kakunin);
    shurui = 2;
  } while(kakunin == 'y' || kakunin == 'Y');

  return 0;
}

void touroku(int shurui)
{
  int temp, temp1;
  typedef struct {
    int code;
    char    hinmei[21];
    int     tanka;
  } SHOHIN_M;

  char kakunin;
  FILE* fp;
  SHOHIN_M srec;

  printf("\n           1  商品コード(半角数字4桁)    : ");
  scanf("%d%*c", &srec.code);    // ここの文字がおかしくなる
  temp = &srec.code;

  printf("           2  商  品  名(全角10文字)    : ");
  scanf("%s%*c", srec.hinmei);

  printf("           3  単      価(半角数字8桁)    : ");
  scanf("%d%*c", &srec.tanka);    // ここの文字がおかしくなる
  temp1 = &srec.tanka;

  printf("           登録しますか?(y/n)         : ");
  scanf("%c%*c", &kakunin);

  if(kakunin == 'y' || kakunin == 'Y') {
    if(shurui == 1) {
        fp = fopen("shohin_m.txt", "w");
    } else {
        fp = fopen("shohin_m.txt", "a");
    }
    fwrite(&srec, sizeof(SHOHIN_M), 1, fp);
    fclose(fp);
  }
}

tetrapod 2008/04/13(日) 07:36:26
実用に耐える強度で(操作性とかセキュリティホールとか) scanf を使うのは
上級者でも細心の注意が必要だったりするので、ここはあえて断言しておこう

scanf は使うな!
fgets + sscanf/strtol/strtod にしとけ!

char userinput[MAXINPUT];
fgets(userinput, MAXINPUT, stdin);
sscanf(userinput, "%d", &shurui);

scanf は改行までを読み込む関数ではないので、期待通りに使うのは難しいのさ
fgets で改行までを読み込み sscanf や strtol で分解するのがいいぞ

それでもあえて scanf を使いたいのであれば解説しないわけでもないが・・・

HAL 2008/04/13(日) 09:42:14
tetrapodさんお世話になります
>>scanf は使うな!
私も調べているうちにscanf関数って奥が深いなと感じたしだいです
デバッガーでキーボードから入力した値を追いかけると変数への代入は正しく行えていま
したが書き込みの部分でおかしくなっているもようです

下記ように書式付で書き込んでやると正しく書き込むことができたのですが
fprintf(fp, "%d%s%d", srec.code, srec.hinmei, srec.tanka);

fp = fopen("shohin_m.txt", "wb");
fwrite(&srec, sizeof(SHOHIN_M), 1, fp);
上記のようにバイナリ形式にシリアライズして書き込むにはどのようにしたらいいのでし
ょうか?

wclrp ( 'o') 2008/04/14(月) 00:13:39
>fwrite(&srec, sizeof(SHOHIN_M), 1, fp);
>上記のようにバイナリ形式にシリアライズして書き込むにはどのようにしたらいいのでし
>ょうか?

たぶん書き込みはできていると思う。
何か問題?

テキストエディタでは読めないよ。
たとえばhinmeiに5バイト分の文字が入っていても
この方法では21バイト分書き込みをするからね。

freadで読み込めば読み込めるよ。

俺的には構造体にパディングがあるかないかで動作変わるから気になるけど。

wclrp ( 'o') 2008/04/14(月) 00:26:04
>fprintf(fp, "%d%s%d", srec.code, srec.hinmei, srec.tanka);

srec.hinmeiが数字で始まる名前だったり数字で終わるとしたら困らないないのぅ?
こういうのよくないよ。
まあ、例として書いているんだろうけど

当然scanfでの読み込みはどこまでがcodeでどこまでがhinmeiかわからない
ってか人間が見ても区別つかないし。

余談
俺的に考えて、テキスト形式にシリアライズするなら

商品名には,が含まれないというルールで
fprintf(fp, "%d,%s,%d\n", srec.code, srec.hinmei, srec.tanka);

商品名に,が含まれる場合は
fprintf(fp, "%d\t%s\t%d\n", srec.code, srec.hinmei, srec.tanka);

かな。

Haru 2008/04/14(月) 10:16:42
>> temp = &srec.code;
tempはint型だけどcodeのポインタいれるの??

>>fwrite(&srec, sizeof(SHOHIN_M), 1, fp);
構造体のパディングを無しというか1バイトにしておかないと、
読み込みの時に困るかも知れない。

>>scanfについて
scanfを使うなといわれるのは、書式に%dなど数値を取得した場合に
オーバーフロー(数値の表現範囲を超え桁あふれする場合)が防げないからです。
#多くの人はバッファオーバーラン(書き込みしてはいけない領域を上書きする場合)が
#防げないと思っている人が多いですが、そんなことはありませんので。

>>fgets(userinput, MAXINPUT, stdin);
>>sscanf(userinput, "%d", &shurui);
sscanfとscanfに書式指定の仕様に差異は無かったはずなので、
これは無意味と思います。
上に書いたようなオーバーフローは防げないし、
セキュリティホールを克服したことにはなら無いかと。

tetrapod 2008/04/14(月) 10:53:06
scanf("%d", &a); に対して a と入力しちゃうと無限ループしちゃいがちに対して
fgets+sscanf だと無限ループしませんな
# 既に書いたとおり scanf は改行までを読む関数ではない

integer overflow については御意 strto* を使うべきですな
buffer overflow については scanf のフォーマット文字の工夫で防げるけど
つい忘れがち=細心の注意が必要なので、やっぱり scanf は非推奨

PATIO 2008/04/14(月) 11:11:38
scanfって関数はそれこそ入力する側は絶対に間違えないとか
入力チェックはいいからちょっとしたロジックテストがしたいだけ
とかそういう時に間に合わせに使うようなものと言うイメージが
あるので入力周りをきちんと作りたい時は使いませんねぇ。

その辺はすでにtetrapodさんが書かれている通り。
真面目に入力周りも作るのであれば、tetrapodさんが書かれている
ような方法を使ったほうが良いと思いますね。

古いスレッドには追加発言できません。