数字以外の文字をはじきたい。

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
しき
記事: 34
登録日時: 6年前

数字以外の文字をはじきたい。

#1

投稿記事 by しき » 6年前

質問です。
現在スタックに値を入れたり出したりする課題をやっているのですが、数値をスタックに入れるときのエラー耐性で苦戦しています。
具体的にはpush関数(数値をスタックに入れる関数)に数値以外の文字が入力された時にやり直させるというものです。

コード:

#incude<stdio.h>
#include<ctype.h>
#define stack_max 100
int stack[stack_max];
int sp=-1;

int push(int data)
{
  if(isdigit(data)){
  if(data<10){
  printf("---------\n");
  printf("push:%2d\n",data);
  printf("---------\n");

  if(sp < stack_max){
    stack[sp] =data;
    sp++;
    return stack[sp];
  }
  return 0;
  }
else
  printf("---------------------------\n");
  printf("一桁の数値を入力してください\n");
  printf("---------------------------\n");
  }
else
  printf("---------------------------\n");
  printf("数値を入力してください\n");
  printf("---------------------------\n");
}

int main(void)
{
     char t;

     printf("格納する一桁の数字を入力してください:");
     scanf("%s",&t);
     push(t);

}
実行結果は以下の通りです。

コード:

//実行結果1
格納する一桁の数字を入力してください:3
---------------------------
一桁の数値を入力してください
---------------------------
数値を入力してください
---------------------------

コード:

//実行結果2
格納する一桁の数字を入力してください:k
---------------------------
数値を入力してください
---------------------------

コード:

//実行結果3
格納する一桁の数字を入力してください:!
---------------------------
数値を入力してください
---------------------------
実行結果を見るとちゃんと数字以外ははじいてくれているようですが肝心の数字まではじいてしまします。実行結果1を見るに判定どちらにもはじかれているので文字の数字として受け取ったものは数値として認識されないんでしょうか・・・。またどのように直したら正常に動くようになるか教えてください。
よろしくお願いします。

maru
記事: 150
登録日時: 13年前

Re: 数字以外の文字をはじきたい。

#2

投稿記事 by maru » 6年前

二つの else 節の両方に中かっこ({})がないため、余分なメッセージが出力されているだけのように見えますが。
インデントを適切にすればすぐにわかることです。

かずま

Re: 数字以外の文字をはじきたい。

#3

投稿記事 by かずま » 6年前

まず、インデントをちゃんとしましょう。

コード:

#include <stdio.h>
#include <ctype.h>
#define stack_max 100
int stack[stack_max];
int sp = -1;

int push(int data)
{
    if (isdigit(data)) {
        if (data < 10) {
            printf("---------\n");
            printf("push:%2d\n", data);
            printf("---------\n");

            if (sp < stack_max) {
                stack[sp] = data;
                sp++;
                return stack[sp];
            }
            return 0;
        }
        else
            printf("---------------------------\n");
        printf("一桁の数値を入力してください\n");
        printf("---------------------------\n");
    }
    else
        printf("---------------------------\n");
    printf("数値を入力してください\n");
    printf("---------------------------\n");
}

int main(void)
{
    char t;

    printf("格納する一桁の数字を入力してください:");
    scanf("%s", &t);
    push(t);

}
最低限の修正です。
  • 38行目: "%s%" を "%c" に
  • 10行目の前に data -= '0'; を挿入

かずま

Re: 数字以外の文字をはじきたい。

#4

投稿記事 by かずま » 6年前

かずま さんが書きました: 最低限の修正です。
  • 38行目: "%s%" を "%c" に
  • 10行目の前に data -= '0'; を挿入
すみません。訂正です。
  • 38行目: "%s" を "%c" に
  • 10行目の前に data -= '0'; を挿入
  • 16行目と17行目を交換
関数 push の返却値の意味は何ですか?

しき
記事: 34
登録日時: 6年前

Re: 数字以外の文字をはじきたい。

#5

投稿記事 by しき » 6年前

maruさん、かずまさん返信ありがとうございます。
push関数の返却値というのは return stack[sp]のことでしょうか。これは数値をスタックに格納したのち、その次(上)に来る数値のために用意したものです。・・と認識していますが何か間違っているでしょうか・・・?

また書き忘れていたのでですがこのpush関数を呼び出す際に文字を入力してこの関数を呼び出しています。

コード:

int main(void)
 {

   char choice;
   printf("初期化します\n");
   init_stack();
   while(1){
     printf("実行するコマンドを選択肢を提示します。\n");
     printf("r・・・スタックを初期化します。\n");
     printf("i・・・スタックに数字を格納します。\n");
     printf("o・・・スタック内から数字を一つ取り出します。\n");
     printf("s・・・現在のスタック内の中身を表示します。\n");
     printf("e・・・終了します。\n");
     printf("それでは実行したいコマンドに対応する文字を入力してください:");
     scanf(" %c",&choice);
       if(choice!='r'&&choice!='i'&&choice!='o'&&choice!='s'&&choice!='e'){

          printf("-----------------------------------\n");
          printf("選択肢にある文字を入力してください。\n");
          printf("-----------------------------------\n");
     }

               if(choice=='i'){
                  char t;
                  printf("格納する一桁の数字を入力してください:");
                  scanf("%c",&t);
                  push(t);
                }
}
//今回はpush関数なので他は省略させていただきます・・。
このせいなのか
かずまさんが提示してくださった解答では解決にはなりませんでした。
実行例は以下の通りです。

コード:

初期化します
-----------------------------------
スタックポインタを初期化しました
-----------------------------------
実行するコマンドを選択肢を提示します。
r・・・スタックを初期化します。
i・・・スタックに数字を格納します。
o・・・スタック内から数字を一つ取り出します。
s・・・現在のスタック内の中身を表示します。
e・・・終了します。
それでは実行したいコマンドに対応する文字を入力してください:i
格納する一桁の数字を入力してください:---------------------------
数値を入力してください
---------------------------
実行するコマンドを選択肢を提示します。
r・・・スタックを初期化します。
i・・・スタックに数字を格納します。
o・・・スタック内から数字を一つ取り出します。
s・・・現在のスタック内の中身を表示します。
e・・・終了します。
それでは実行したいコマンドに対応する文字を入力してください:
push関数で判別する前にはじかれていると思ったので再度%cを%sに変更しました。
実行例は以下の通りです。

コード:

初期化します
-----------------------------------
スタックポインタを初期化しました
-----------------------------------
実行するコマンドを選択肢を提示します。
r・・・スタックを初期化します。
i・・・スタックに数字を格納します。
o・・・スタック内から数字を一つ取り出します。
s・・・現在のスタック内の中身を表示します。
e・・・終了します。
それでは実行したいコマンドに対応する文字を入力してください:i
格納する一桁の数字を入力してください:3
---------
push: 3
---------
実行するコマンドを選択肢を提示します。
r・・・スタックを初期化します。
i・・・スタックに数字を格納します。
o・・・スタック内から数字を一つ取り出します。
s・・・現在のスタック内の中身を表示します。
e・・・終了します。
それでは実行したいコマンドに対応する文字を入力してください:i
格納する一桁の数字を入力してください:l
---------------------------
数値を入力してください
---------------------------
と一応正常(?)に動くようになりました。
ですがなぜ%cを%sにして正常になったか理解できていません・・。
またかずまさんが提示してくださった{data-='0';}というのもよく分かったないです・・。
教ええいただけませんか。よろしくお願いします。

かずま

Re: 数字以外の文字をはじきたい。

#6

投稿記事 by かずま » 6年前

しき さんが書きました: push関数の返却値というのは return stack[sp]のことでしょうか。これは数値をスタックに格納したのち、その次(上)に来る数値のために用意したものです。・・と認識していますが何か間違っているでしょうか・・・?
push関数には出口が 3つあります。
  • 18行目: return stack[sp];
  • 20行目: return 0;
  • 31行目: }
push は int の値を呼出し元に返さないといけないのに、
何も返していないところがあります。
stack[sp] を返す時と、0 を返す時に違いがありますか?
sp の初期値は -1 です。最初の data は stack[-1] に
格納しています。これは、やってはいけないことです。
次に sp++; で sp は 0 になります。
stack[0] には何も値を入れていませんが、グローバル変数
ですから、初期値 0 が入っています。
return stack[sp]; でこの 0 を返したいのですか?
次にデータを入れる場所は、更新した sp が持っています。
それで十分です。stack[sp] の値を呼び出し元に返すことに
何の意味があるんですか?
しき さんが書きました: また書き忘れていたのでですがこのpush関数を呼び出す際に文字を入力してこの関数を呼び出しています。
 :
このせいなのか
かずまさんが提示してくださった解答では解決にはなりませんでした。
そりゃそうでしょう。私は、最初のプログラムの
main で動くように最低限の修正をしただけです。
別の main を持ち出して、解決にはなりませんでした、
と言われても困ります。
  • 38行目: "%s" を " %c" に
と言っておけば、その main でも動いたでしょう。
%c の前のスペースの意味をご存知ですか?
その main の 15行目でも scanf(" %c",&choice); と
使っていますね。意味も解らずそう書いているのですか?
試しにそこのスペースをとってみてください。
どうなりますか?
しき さんが書きました: push関数で判別する前にはじかれていると思ったので再度%cを%sに変更しました。
 :
と一応正常(?)に動くようになりました。
ですがなぜ%cを%sにして正常になったか理解できていません・・。
まず、"%c" でダメだった理由を説明します。
scanf(" %c",&choice);
scanf("%c",&t);
このように 2つの scanf を実行していますよね。
最初の scanf に対して、i と ENTER のキーを入力しました。
これらは 'i' と '\n' になって、標準入力のバッファに入ります。
scanf は 'i' だけを読み込んで choice に格納します。
次の scanf では '\n' を読み込んで t に格納します。
したがって、3 ENTER を入力する前に、push が呼び出されて、
data には '\n' が入っているので「格納する一桁の数字を
入力してください」が表示されるのです。

"%s" でうまくいった理由を説明します。
"%s" は先行する 0文字以上の空白類文字(スペースやタブや改行
などの文字)を読み飛ばしてから、空白類文字でない文字列を
1文字以上読み取ろうとします。
なので、改行文字の '\n'は読み飛ばされて、次の入力を待ちます。
3 ENTER を入力すると、'3' が t に格納されます。

" %c" でうまくいく理由を説明します。
書式のスペースには、0文字以上の連続する空白類文字を
読み飛ばす機能があります。これにより、%c で '3' を
t に格納できるようになります。

次に char t; scanf("%s", &t); がダメな理由を説明します。
"%s" で読み込むのは文字列です。
C がプログラム内で扱う文字列とは '\0' で終了するデータです。
たとえ '3' という 1文字を読み込んだとしても、
t に '3' と '\0' を格納しようとします。
t は char ですから 1文字しか入りません。
'\0' は t の場所の次のメモリに無理やり格納されます。
そこに重要なデータがあればそれが破壊されます。
今うまくいったように見えるのは、そこがたまたま 0 だったか、
参照されないデータだったかなのでしょう。"%s" を使うときは、
 char s[100]; scanf("%s", s);
のように十分な大きさの領域(char配列)を用意して文字列を
読み込まないといけません。
しき さんが書きました: またかずまさんが提示してくださった{data-='0';}というのもよく分かったないです・・。
t に '3' が読み込まれたとして、push が呼び出されると、
引数の data には '3' が入っています。
isdigit('3') は真です。
ところが '3' は 3 ではありません。
文字の '3' は文字コード表を見ると、
16進の 0x33、すなわち 10進の 51 という値です。
data < 10 は偽になります。
文字を数値に変換しておかないといけないのです。
data -= '0'; で data から '0' を引くと、
data が '3' だったのが 3 になります。

最後に、あれだけ何度もインデントをちゃんとする
ようにと言っているのに、そうなっていません。
main の終了はどこですか?
29行目の } ですか?
それなら while(1){ の終了の } はどれですか?

最初のプログラムの push 関数には、3つの if と
2つの else がありますが、その else がどの if に
対応するのかすぐにわかりますか?
No.4 でインデントをちゃんとしたプログラムでは
それらの対応がすぐにわかります。
さらに、maruさんの指摘のように 3つの printf が
一体化されていないこともわかります。

しき
記事: 34
登録日時: 6年前

Re: 数字以外の文字をはじきたい。

#7

投稿記事 by しき » 6年前

かずまさん返信ありがとうございます。
return stack[sp]を消してみても正常に動いたので余計だったんですね・・。
%sと%c説明もわかりやすかったです。ありがとうございました。

確認したところ確かにchar t; scanf("%s", &t);では二桁以上の数字を正常には読み取ってくれませんでした。

コード:

実行するコマンドを選択肢を提示します。
r・・・スタックを初期化します。
i・・・スタックに数字を格納します。
o・・・スタック内から数字を一つ取り出します。
s・・・現在のスタック内の中身を表示します。
e・・・終了します。
それでは実行したいコマンドに対応する文字を入力してください:i
格納する一桁の数字を入力してください:11
---------
push: 1
---------
次にchar t; scanf(" %c", &t);でやってみたんですがこれもまたうまくいきませんでした。

コード:

実行するコマンドを選択肢を提示します。
r・・・スタックを初期化します。
i・・・スタックに数字を格納します。
o・・・スタック内から数字を一つ取り出します。
s・・・現在のスタック内の中身を表示します。
e・・・終了します。
それでは実行したいコマンドに対応する文字を入力してください:i
格納する一桁の数字を入力してください:10
---------
push: 1
---------
実行するコマンドを選択肢を提示します。
r・・・スタックを初期化します。
i・・・スタックに数字を格納します。
o・・・スタック内から数字を一つ取り出します。
s・・・現在のスタック内の中身を表示します。
e・・・終了します。
それでは実行したいコマンドに対応する文字を入力してください:-----------------------------------
選択肢にある文字を入力してください。
-----------------------------------
" %c"でも二桁以上の数字は読み込めないんでしょうか・・。

またchar s[100]; scanf("%s", s);のように配列を確保する場合push関数に渡すにはどうしたらいいんでしょうか・・。
よろしくお願いします。

インデントの件前々から言われているのに治らなくて申し訳ないです。気を付けます。

フィーロ

Re: 数字以外の文字をはじきたい。

#8

投稿記事 by フィーロ » 6年前

> またchar s[100]; scanf("%s", s);のように配列を確保する場合push関数に渡すにはどうしたらいいんでしょうか・・。
> よろしくお願いします。

push関数に2桁の数字を渡せるようなコードができました。
書いてて途中で疲れてしまったので2桁の数字を渡すところだけでも参考にどうぞ。
間違ってるかもしれない&冗長な可能性が高いですが・・・。

コード:

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

#define STACK_MAX 100

void getString(char *ss);
int checkCommand(char *ss);
void push(char *ss);
int numberCheck(char *ss);


int main(int argc, char *argv[])
{
    char ss[STACK_MAX];
    
    while(1) {
        printf("1桁の数字を入力して下さい\n");
        printf("e,r,i,o,sで終了します。\n");

        getString(ss);

        if(checkCommand(ss) != 0){
            printf("終了\n");
            exit(1);
        }

        push(ss);
    }
}

/*
 * 文字列を取得する。
 * STACK_MAXを超える文字列が入力されている場合は読み捨てる。
 * 念のため、STACK_MAX文字目が'\n'の場合'\0'に置き換える。
 */
void getString(char *ss)
{
    memset(ss, 0, STACK_MAX);

    fgets(ss, STACK_MAX, stdin);
    if(ss[strlen(ss) - 1] != '\n'){
        while(getchar() != '\n');
    }
    if(ss[STACK_MAX] == '\n'){
        ss[STACK_MAX] = '\0';
    }
    fflush(stdin);
}

/*
 * e, i, o, r, sが含まされている場合'A'を引いた値を返す。
 * それ以外の場合 0 を返す。
 */
int checkCommand(char *ss)
{
    int ii;
    int c;

    for(ii = 0; ii < strlen(ss); ii++){
        c = ss[ii];
        switch(c){
            case 0x65:
                return c - 'A';     // e
            case 0x69:
                return c - 'A';     // i
            case 0x6F:
                return c - 'A';     // o
            case 0x72:
                return c - 'A';     // r
            case 0x73:
                return c - 'A';     // s
        }
    }
    return 0;
}

void push(char *data)
{
    int tmp = numberCheck(data);
    
    if(tmp < 10 && tmp > -1){
        printf("正しい数値が入力されました。\n");
    } else {
        printf("1桁の数値を入力してください。\n");
    }
}

/*
 * 0文字の場合は-1を返す。
 * 1文字目が0の場合は0を返す。
 * それ以外はatoiの値を返す。
 */
int numberCheck(char *ss)
{
    int tmp;
    if(strlen(ss) == 0){
        return -1;
    }
    
    tmp = atoi(ss);
    if(ss[0] == '0'){
        return 0;
    }
    if(tmp == 0){
        return -1;
    } else {
        return tmp;
    }
}

かずま

Re: 数字以外の文字をはじきたい。

#9

投稿記事 by かずま » 6年前

しき さんが書きました: 確認したところ確かにchar t; scanf("%s", &t);では二桁以上の数字を正常には読み取ってくれませんでした。
 :
次にchar t; scanf(" %c", &t);でやってみたんですがこれもまたうまくいきませんでした。
" %c"でも二桁以上の数字は読み込めないんでしょうか・・。
char t; と宣言された t に 2文字以上
格納するのが無理なことは自明です。
しき さんが書きました: またchar s[100]; scanf("%s", s);のように配列を確保する場合push関数に渡すにはどうしたらいいんでしょうか・・。
次のプログラムを参考にして、あなたのプログラムを修正してください。

コード:

#include <stdio.h>
 
#define MAX_SIZE  1000
 
int stack[MAX_SIZE];
int size = 0;
 
void menu(void)
{
    puts("  m        : display this menu\n"
         "  r        : reset stack\n"
         "  i [data] : input data\n"
         "  o        : output data\n"
         "  s        : show stack\n"
         "  e        : exit");
}
 
void reset_stack(void)
{
    size = 0;
}
 
void input_data(char *buf)
{
    int x;
    if (sscanf(buf+1, "%d", &x) != 1) {
        char buf2[1024];
        printf("input data: ");
        if (fgets(buf2, sizeof buf2, stdin) == NULL) return;
        if (sscanf(buf2, "%d", &x) != 1) { puts("no data"); return; }
    }
    if (size >= MAX_SIZE) { puts("stack full"); return; }
    stack[size++] = x;
}
 
void output_data(void)
{
    if (size <= 0) { puts("stack empty"); return; }
    printf("%d\n", stack[--size]);
}
 
void show_stack(void)
{
    for (int i = 0; i < size; i++) printf(" %d", stack[i]);
    putchar('\n');
}
 
int main(void)
{
    char buf[1024];
    menu();
    while (printf(">> "), fgets(buf, sizeof buf, stdin)) {
        if (buf[0] == 'm') menu();
        else if (buf[0] == 'r') reset_stack();
        else if (buf[0] == 'i') input_data(buf);
        else if (buf[0] == 'o') output_data();
        else if (buf[0] == 's') show_stack();
        else if (buf[0] == 'e') {
            printf("really exit[y/n]? ");
            if (fgets(buf, sizeof buf, stdin) && buf[0] == 'y') break;
        }
    }
    return 0; 
}
修正したプログラムは必ずきれいなインデントで見せてください。

いやその前に、このプログラムの実行例を示してもらえませんか?

しき
記事: 34
登録日時: 6年前

Re: 数字以外の文字をはじきたい。

#10

投稿記事 by しき » 6年前

フィーロさん。かずまさん返信ありがとうございます。
フィーロさんのコードで一桁以外の数字をはじけているので参考にしたいと思います。
かずまさんのコードも二桁以上の数値を配列に渡せているのでこれもまた参考にしたいと思います。
かずまさんのコード実行例

コード:

  m        : display this menu
  r        : reset stack
  i [data] : input data
  o        : output data
  s        : show stack
  e        : exit
>> m
  m        : display this menu
  r        : reset stack
  i [data] : input data
  o        : output data
  s        : show stack
  e        : exit
>> r
>> i
input data: 1
>> i
input data: 11
>> s
 1 11
>> o
11
>> s
 1
>> e
really exit[y/n]? y
返信期間が開いてしまって申し訳ないです。
この間にも考えていて最終的に以下のようなコードにしました。

コード:

      if(choice=='i'){
        char t[100];
        int x;
        int i;

        printf("格納する一桁の数字を入力してください:");
        scanf("%s",t);
        for(i=0;t[i]!='\0';i++){
          if(isdigit(t[i])){
            x=t[i]-'0';

            push(x);

          }
          else{
           printf("---------------------------\n");
           printf("数値を入力してください\n");
           printf("---------------------------\n");
          }
        }
      }
与えられた数値を一つずつ見ていきその数値を精査するというものです。
これには{i,11}→{push(1),push(1)}と認識してしまう問題があるのですがすいません、妥協点です・・。

最後にこのトピックでは数字以外をはじきたいというのに二桁入力の話までして申し訳ないです。
当初の目的である数値以外をはじくはできたので解決とさせていただきます。
また今回提示してくださったコードで不明な点があったらこのトピックの続きとして別トピックでお話を伺うと思いますのでその時はどうかよろしくお願します。
お付き合いありがとうございました。

かずま

Re: 数字以外の文字をはじきたい。

#11

投稿記事 by かずま » 6年前

しき さんが書きました: 与えられた数値を一つずつ見ていきその数値を精査するというものです。
これには{i,11}→{push(1),push(1)}と認識してしまう問題があるのですがすいません、妥協点です・・。
妥協しないでほしいなあ。

コード:

#include <stdio.h>
 
void menu(void)
{
    puts("コマンド一覧\n"
         "  i: input data\n"
         "  e: exit");
}

void push(int x) { printf("push: %d\n", x); }

int main(void)
{
    char t[1024], c;
    int x;
    menu();
    while (1) {
        printf("コマンド>> ");
        if (fgets(t, sizeof t, stdin) == NULL) break;
        if (t[0] == 'i') {
            printf("格納する一桁の数字を入力してください: ");
            if (fgets(t, sizeof t, stdin)
                    && sscanf(t, "%d%c", &x, &c) == 2
                    && x >= 0 && x <= 9 && c == '\n') {
                push(x);
            }
            else {
                puts("-------------------------------\n"
                     "一桁の数値を入力してください\n"
                     "-------------------------------");
            }
        }
        else if (t[0] == 'e') break;
        else menu();
    }
    return 0; 
}
実行例

コード:

コマンド一覧
  i: input data
  e: exit
コマンド>> i
格納する一桁の数字を入力してください: 45
-------------------------------
一桁の数値を入力してください
-------------------------------
コマンド>> i
格納する一桁の数字を入力してください: k
-------------------------------
一桁の数値を入力してください
-------------------------------
コマンド>> i
格納する一桁の数字を入力してください: 4g
-------------------------------
一桁の数値を入力してください
-------------------------------
コマンド>> i
格納する一桁の数字を入力してください: 7
push: 7
コマンド>> e
対話的入力には、scanf より fgets のほうが良いと思います。

しき
記事: 34
登録日時: 6年前

Re: 数字以外の文字をはじきたい。

#12

投稿記事 by しき » 6年前

かずまさん返信ありがとうございます。
コードの提示もありがとうございます。
時間が空いたら元の私のコードに取り込もうと思います。

fgetsはあまり使ったことがないですが調べて使ってみようと思います。
アドバイスありがとうございました。

返信

“C言語何でも質問掲示板” へ戻る