プロが教えるわが家の防犯対策術!

整数型の配列に整数データを格納するプログラムで、
入力時に文字を入力してしまった場合、
エラーチェックを行いたいのですが、
具体的にどのような方法があるのでしょうか?
文字を入力した場合に、
とてつもないことがDos画面でおこってしまいます。
どうか皆さん、よろしくお願いします。

言語はC言語で、
visualC++6.0を使っています。

A 回答 (11件中1~10件)

fgetsについて


>こちらは'\0'ターミネートが保証されます。
これは、此方の勘違いでした。
#strncpyあたりと勘違いしてたようです。

>strtolは現在主流のANSI-C(C89)では定義されていません。
これについては、JIS X3010-1993の7.10.1.5に定義されてます。
ANSI/ISOについてのソースが手元にないですが、原文ISO9899(C89)にも乗っているはずですが。
    • good
    • 0

strtolは現在主流のANSI-C(C89)では定義されていません。


strtolのサポートはC99からで、Visual C++6.0はC89準拠です。

よって、

> ここまでくるとその処理系が本当にANSI Cかどうか疑いたくなる
「処理系のANSI-C準拠」と「strtolの挙動に対して予防線を張ること」に矛盾は生じません。

> strtolはpを走査の終了位置を示す文字へのポインタにするので、
> p == &str[strlen(str)] つまり *p == '\0'
>となり、p == NULL にはならないです。
前述のようにC89におけるstrtolの挙動は処理系依存である確率が高く、このような断言は危険です。

> fgetsはgetsのように'\n'を'\0'に変換しないのでチェックは必要ですよ?
そうですね。
カン違いでした。

> また、'\0'でターミネートされた値が確実に入るわけではないのでその辺も注意が必要です。
こちらは'\0'ターミネートが保証されます。
(C89,C99ともに)
    • good
    • 0

> 質問の趣旨が「入力文字エラーチェック」ですから不正文字入力を認識するためにstrtol()を使うべきと考えました。


この点に付いてはどちらでも目的を達成できるので異論はないですが、


> pがNULLなら変換不能文字無し(=文字列は全て10進数に変換された)
strtolはpを走査の終了位置を示す文字へのポインタにするので、
p == &str[strlen(str)] つまり *p == '\0'
となり、p == NULL にはならないです。

> fgets()だから'\n'チェックは不要だし、
fgetsはgetsのように'\n'を'\0'に変換しないのでチェックは必要ですよ?
また、'\0'でターミネートされた値が確実に入るわけではないのでその辺も注意が必要です。

> '\0'を変換不能文字だと認識するようなstrtolの実装は見たことが無いのですが、
ここまでくるとその処理系が本当にANSI Cかどうか疑いたくなる。
    • good
    • 0

2度目です。



質問の趣旨が「入力文字エラーチェック」ですから不正文字入力を認識するためにstrtol()を使うべきと考えました。

long strtol(char *, int, char **) ;
文字列を数値に変換

関数値:
変換後の数値。
文字列に変換不能文字があった場合、変換可能文字までを変換する(1zの場合、1)。
文字列が先頭から変換不能文字であった場合0。

第1引数:
変換対象の文字列。

第2引数:
変換の基数。
10→10進変換
16→16進変換
8 →8進変換
0 →C言語の定数記述規約に従い変換(0x=16進。0=8進)

第3引数:
変換不能文字を発見した場合、発見した変換不能文字のアドレスを設定する。
10XYZと文字列であれば'X'のアドレス。
この引数がNULLであった場合変換不能文字の報告を行わない

val[no] = strtol(str, 10, &p) ;
val[no]には変換できたところまでの数値が入ります。
pには変換不能だった文字のアドレスが設定されます。
pがNULLなら変換不能文字無し(=文字列は全て10進数に変換された)
*pが文字なら入力文字列に含まれた変換不能文字そのものへのアドレスです。
*p != '\0' && *p != '\n'の部分は「'\0'や'\n'が変換不能文字として報告されたら困る」という場合のチェックです。
fgets()だから'\n'チェックは不要だし、'\0'を変換不能文字だと認識するようなstrtolの実装は見たことが無いのですが、老婆心で常に入れるクセついてて…
エラールートなので(P!=NULLの時しか通らない)処理効率と関係ないので「nul文字と改行は無関係」という仕様を示す意味もあって入れてたりします。


sscanfやatoiは変換不能文字の報告を行わない(ssacnfでは可能だが、利用法が煩雑)ため、「エラーチェック」を趣旨とするこの質問には合致しないと考えます。
    • good
    • 0

>ret = sscanf(buffer, "%d%[\n]", &data[i], &dummy);


↑の行間違い本当は↓
ret = sscanf(buffer, "%d%1[\n]", &data[i], &dummy);
どう違うかは考えてみてください。
    • good
    • 0

何か問題を難しく考えすぎているようですが、


#1のやり方でgetsをfgetsにすれば一番単純では?
わざわざ一文字づつ調べる必要もないとけど。
とりあえずこんな感じ↓

#include <stdio.h>
#include <stdlib.h>

#define DATA_SIZE 10
#define BUFFER_SIZE 256

int main(void)
{
 int ret, i;
 int data[DATA_SIZE];
 char buffer[BUFFER_SIZE];
 char dummy;

 for(i = 0; i < DATA_SIZE; i++)
 {
  printf("input data[%d] > ", i);
  fgets(buffer, BUFFER_SIZE, stdin);
  ret = sscanf(buffer, "%d%[\n]", &data[i], &dummy);
  if(ret != 2)
  {
   printf("\ndata err!!\n\n");
   --i;
  }
 }

 for(i = 0; i < DATA_SIZE; i++)
  printf("data[%d] = %d\n", i, data[i]);

 return 0;
}
    • good
    • 0

#4です。



>上記のサンプルは文字0から9の入力の場合には、配列に入力文字を格納するものですが、
>これでは126とか-46など、2桁以上の数字や、負の整数には対応できないようです。

先頭文字のチェックをしていませんの符号文字の入力には対応していません。
必要なら先頭文字のときに符号文字をチェックすればいいと思いますが。(ご自分でどうぞ)

2桁以上の数字には対応していませんか? どう読んだのでしょうか?
これは、1つの数値を文字列として格納する部分です。
これを数値データにして、数値配列に格納する部分は含まれていませんが。

尚、これがコンソールのアプリケーションの場合、
BSキーなどによる編集機能に対応するとか、
1文字入力関数もgetchar()でなく画面エコーしない関数を使うとか、
他にも考慮すべき点が多々ありますが、質問とかけ離れますので割愛しています。

要は、1文字ずつ入力して、都度チェックしてバッファにためる数値文字入力専用の関数を
作ればいいのではないでしょうか。

1行入力して、文字列の中身を検査して不適当な文字があれば再入力するようにするのが
一番簡単ですが、そういうものをご希望なんですか。
    • good
    • 0

#3に方のご指摘のように、gets() は使用しないほうがいいですよ。


セキュリティホールの元凶になっている関数です。
文字数が制限できないので、バッファが溢れると弊害が出ます。
VCではどうか知りませんが、gccなどで"dangerous"というWarningが出ます。

ところでDOSアプリケーションでしたら、
getchar()で1文字入力が可能じゃなかったでしょうか。
で、こんな感じではどうでしょう。

 int c, n=0;
 char buf[BLEN+1];
 while((c=getchar()) != '\r') {
  if(('0' <= c) && (c <= '9')) {
    buf[n++]=c;
    if(n > BLEN)
     break;
   }
 }
 buf[n]='\0';

この回答への補足

回答頂きましてありがとうございます。
上記のサンプルは文字0から9の入力の場合には、
配列に入力文字を格納するものですが、
これでは126とか-46など、
2桁以上の数字や、負の整数には対応できないようです。
こちらの質問の方法が不充分だったようで、
申し訳ありませんでした。

補足日時:2002/09/30 03:09
    • good
    • 0

私なら…



int val[MAX], no ;
char str[LEN] ;
char *p ;

for (no = 0 ; no < MAX && fgets(str, LEN, stdin) != NULL ; no++) {
  val[no] = strtol(str, 10, &p) ;
  if (p != NULL && (*p != '\0' && *p != '\n')) {
    //10進数でない入力
    //エラー処理
  }
}

こんな感じにするかと…


gets関数の問題点:
入力文字数を制限できないのでエラー入力が想定できる場合は使用しないほうがよい。

この回答への補足

回答くださいましてありがとうございます。
読ませていただいたところ、
自分の知識不足で読み解けないコーディングがあります。

for (no = 0 ; no < MAX && fgets(str, LEN, stdin) != NULL ; no++) {
  val[no] = strtol(str, 10, &p) ;
  if (p != NULL && (*p != '\0' && *p != '\n')) {
    //10進数でない入力
    //エラー処理

恥ずかしながら、知らない関数があったりして、
論理構造が読み解けません。  
人の好意に甘えて怠けすぎるのもいけないと思うので、
自分でも理解できるようもう少し調べてみようと思います。
ヒントをありがとうございます。

補足日時:2002/09/30 03:10
    • good
    • 0

キー入力を原則的に文字列で入力します。


No.1のkyeongilさんの書かれているgets関数でchar型の配列に入れ、
これをisdigit関数で1文字ずつチェックして0~9までの文字であれば
そのまま正常にatoi関数で整数に変換して戻し値にして、0~9以外の
文字が含まれていたらエラーコードを返す関数を作っておきます。

なお、isdigit関数は#include<ctype.h>
   atoi関数は #include<stdlib.h>
をインクルードいて置きます。

判らないときは、補足でレス下さい、サンプルコードを書きます。。

この回答への補足

回答していただきありがとうございます。
getsやgetcharでは、
一文字ずつしか読み込めず、
複数桁やマイナスの整数には対応できないようです。
その場合はどうすべきか、
自分でももうちょっと考えてみようと思います。
ヒントをくださり、ありがとうございます。

補足日時:2002/09/30 03:19
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!