C言語関係掲示板

過去ログ

No695 電卓

[戻る] [ホームページ]
No.8165

数式の読み込みはgetsでは無理ですか?
投稿者---M/K(2003/07/09 14:20:56)


数式の読み込みはgetsでは無理ですか?
数式を読み込むためにはどうすればよろしいでしょうか?
電卓の製作途中の中途半端ですが・・・↓

#include <stdio.h>
#include <ctype.h>

int main()
{
char A[100]; //数式//
int cnt; //カウント//
int ans; //答え//
cnt=0;ans=0;

printf("式は?>");
gets(&A[cnt]);

while(A[cnt]!='\0')
{
if(A[cnt]=='+')
ans=ans+A[cnt];
if(A[cnt]=='-')
ans=ans-A[cnt];
cnt=cnt+1;
}
printf("答え:%d",ans);
}

No.8166

Re:数式の読み込みはgetsでは無理ですか?
投稿者---ふう(2003/07/09 14:58:12)


別に読み込むだけならgetsでもいいとおもいます。
問題は読み込んだ数式を元に計算結果を出している部分にあります。
コンパイルして結果を確かめてみましたか?

No.8167

Re:数式の読み込みはgetsでは無理ですか?
投稿者---M/K(2003/07/09 15:02:05)


計算結果はちゃんとでません。
どうすればうまく電卓を作れるのですか?
getsに問題があるような気がするのですが・・・。

>別に読み込むだけならgetsでもいいとおもいます。
>問題は読み込んだ数式を元に計算結果を出している部分にあります。
>コンパイルして結果を確かめてみましたか?


No.8172

Re:数式の読み込みはgetsでは無理ですか?
投稿者---ふう(2003/07/09 15:20:21)


>計算結果はちゃんとでません。
計算結果が正しくないならなぜ正しくないのか考えましたか?
考えたのなら、考えた結果をソースに反映させてください。

>どうすればうまく電卓を作れるのですか?
それを考えるのはあなたです。

>getsに問題があるような気がするのですが・・・。
その根拠はどこにあるのですか?
なんとなくではないですよね?

最後に、
管理人さんが書いておられる【掲示板ご利用上の注意】をよく読んでから
投稿しなおしたほうがいいのではないですか?


No.8175

Re:数式の読み込みはgetsでは無理ですか?
投稿者---nop(2003/07/09 15:31:47)


>数式の読み込みはgetsでは無理ですか?
>数式を読み込むためにはどうすればよろしいでしょうか?
>電卓の製作途中の中途半端ですが・・・↓


数式を読む込むのに「gets()」を使用することは可能です。

# バッファーオーバーフローの可能性が考えられるため、
# 個人的には gets() より fgets() をお薦めします。


ここで、注意しなければならないのは、

 読み込んだ数式は「文字列」である

と言うことです。
つまり、読み込んだ数式を元に答えを出すには、
文字列として入力された「数式」を、

 数字列を数値に変換
 演算子の判断
 変換した各数値を判断した演算子により計算

と言う処理を行わなければなりません。


>while(A[cnt]!='\0')
>{
>    if(A[cnt]=='+')
>        ans=ans+A[cnt];
>    if(A[cnt]=='-')
>        ans=ans-A[cnt];
>    cnt=cnt+1;
>}



ここで、その計算をやりたいようですが、
この処理ではその計算はなされていません。
上記処理は

1.文字列の終端までループ
  1)文字が「+」か?
  ・真の時
    a.変数「ans」に「+」の文字コードを加算
  2)文字が「-」か?
  ・真の時
    a.変数「ans」に「-」の文字コードを減算
  3)評価文字を次の文字へ移動

と言うものになっています。

No.8178

Re:数式の読み込みはgetsでは無理ですか?
投稿者---nop(2003/07/09 16:26:33)


まだ、穴もありますがザッと作ってみました。
四則演算を行う電卓です。
演算子が複数ある場合には対応していません。
とりあえず、参考程度に…。

# 数式計算は逆ポーランド記法とか使うといいかと思われます。


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

/* ************************************************************
    文字列から空白文字を除去
************************************************************ */
char    *strdelspace( char *str)
{
    /* ***** 内部変数定義 ***** */
    char    *ret = str;
    int        i;

    if( str )
    {
        do    /* 文字列終端までループ */
        {
            if( isspace(*str) )    /* 空白文字か? */
            {
                for( i=0; str[i]; i++ )    /* 現在の文字位置から文字列終端までループ */
                {
                    /* ***** 現在の文字に直後の文字を上書き ***** */
                    str[i] = str[i+1];
                }
            }
        } while( *str++ );
    }
    return ret;
}

int main( void )
{
    /* ***** 内部変数定義 ***** */
    char    buf[1024] = {'\0'};
    char    *work;
    char    *p;
    long    value1 = 0;
    long    value2 = 0;
    long    answer;
    char    op;

    /* ***** メッセージ表示 ***** */
    printf( "数式を入力 > " );

    /* ***** 数式入力 ***** */
    fgets( buf, sizeof(buf), stdin );
    ( p=strrchr(buf,'\n') ) ? *p='\0' : 0;    /* 改行除去 */
    strdelspace(buf);

    /* ***** 数値1取得 ***** */
    value1 = strtol( buf, &p, 10 );

    if( buf!=p )    /* 数値1取得正常か? */
    {
        switch( *p )    /* 文字種別判定 */
        {
        case '+':    /* 加算演算子 */
        case '-':    /* 減算演算子 */
        case '*':    /* 乗算演算子 */
        case '/':    /* 除算演算子 */
            /* ***** 演算子保存 ***** */
            op = *p;
            
            /* ***** 数値2取得 ***** */
            work = 1 + p;
            value2 = strtol( work, &p, 10 );

            if( *p=='\0' )    /* 数値2取得正常か? */
            {
                switch( op )    /* 演算子判定 */
                {
                case '+':    /* 加算演算子 */
                    /* ***** 加算実行 ***** */
                    answer = value1 + value2;
                    break;
                case '-':    /* 減算演算子 */
                    /* ***** 減算実行 ***** */
                    answer = value1 - value2;
                    break;
                case '*':    /* 乗算演算子 */
                    /* ***** 乗算実行 ***** */
                    answer = value1 * value2;
                    break;
                case '/':    /* 除算演算子 */
                    if( value2 )
                    {
                        /* ***** 除算実行 ***** */
                        answer = value1 / value2;
                    }
                    else
                    {
                        /* ***** 0 除算エラー処理 ***** */
                        puts( "ERROR:0 除算!" );
                        exit( -1 );
                    }
                    break;
                default:
                    break;
                }
                /* ***** 処理結果表示 ***** */
                printf( "%d\n", answer );
            }
            break;
        default:
            break;
        }
    }
    return 0;
}


No.8183

Re:数式の読み込みはgetsでは無理ですか?
投稿者---かずま(2003/07/09 19:09:19)


> まだ、穴もありますがザッと作ってみました。
> 四則演算を行う電卓です。
> 演算子が複数ある場合には対応していません。
> とりあえず、参考程度に…。

では、私も、穴のある電卓プログラムを。あくまでも、参考ということで。
以前、同じものを書いた憶えがあるのですが、ゼロで割り算をするとだめな
穴が残されています。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <math.h>

#define isSpace(c) isspace((c) & 0xFF)
#define isDigit(c) isdigit((c) & 0xFF)
#define isAlpha(c) isalpha((c) & 0xFF)

char buf[1024], c, *p, o[] = "+-*/^^";

int get(void) { do c = *p++; while (isSpace(c)); return c; }

double expr(const char *s)
{
    double v;

    if (*s)
        for (v = expr(s+2); c==s[0] || c==s[1]; )
            switch (c) {
            case '+': v += expr(s+2); break;
            case '-': v -= expr(s+2); break;
            case '*': v *= expr(s+2); break;
            case '/': v /= expr(s+2); break;
            case '^': v = pow(v, expr(s)); break;
            }
    else if (get() == '(') v = expr(o), (c == ')') ? get() : (c = 1);
    else if (c == '-') v = -expr(o);
    else if (c == '+') v = +expr(o);
    else if (isDigit(c) || c == '.') v = strtod(p-1, &p), get();
    else if (!memcmp(p-1, "sqrt",4)) p += 3, v = sqrt(expr(s));
    else if (!memcmp(p-1, "exp", 3)) p += 2, v = exp(expr(s));
    else if (!memcmp(p-1, "log", 3)) p += 2, v = log(expr(s));
    else if (!memcmp(p-1, "sin", 3)) p += 2, v = sin(expr(s));
    else if (!memcmp(p-1, "cos", 3)) p += 2, v = cos(expr(s));
    else if (!memcmp(p-1, "tan", 3)) p += 2, v = tan(expr(s));
    else if (!memcmp(p-1, "atan",4)) p += 3, v = atan(expr(s));
    else if (isAlpha(c)) exit(0);
    else c = 1;
    return v;
}

int main(void)
{
    double v;

    while (fgets(p = buf, sizeof buf, stdin))
        v = expr(o), printf(c ? "   error\n" : "   %.15g\n", v);
    return 0;
}


No.8283

Re:数式の読み込みはgetsでは無理ですか?
投稿者---かずま(2003/07/11 18:47:32)


> では、私も、穴のある電卓プログラムを。あくまでも、参考ということで。

すごい穴があります。-3 + 5 と入力すると -8 と表示されます。
>   else if (c == '-') v = -expr(o);
>   else if (c == '+') v = +expr(o);

これを次のように訂正します。

    else if (c == '-') v = -expr(s);
    else if (c == '+') v = +expr(s);


No.8284

Re:数式の読み込みはgetsでは無理ですか?
投稿者---nop(2003/07/11 18:52:47)


>> では、私も、穴のある電卓プログラムを。あくまでも、参考ということで。
>
>すごい穴があります。-3 + 5 と入力すると -8 と表示されます。

初めから「穴がある」と言っているので気にならないかと。
その穴を見つけるのも勉強になるかと思いますので…。

関係ないけど、
数式を逆ポーランド表記に変換して計算するバージョンを作ったけど、
掲示板に投稿するにはソースがでかい…。(-_-;)

No.8298

Re:数式の読み込みはgetsでは無理ですか?
投稿者---かずま(2003/07/12 17:23:11)


> 数式を逆ポーランド表記に変換して計算するバージョンを作ったけど、
> 掲示板に投稿するにはソースがでかい…。(-_-;)

では、私もやってみましょう。なんとか 50行に収めてみました。
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

#define isSpace(c)  isspace((c) & 0xFF)
#define isDigit(c)  isdigit((c) & 0xFF)
#define isAlpha(c)  isalpha((c) & 0xFF)

char buf[1024], buf2[1024], *p, *p2, c, o[] = "+-*/";

double stack[100]; int sp;
void   push(double x) { stack[sp++] = x; }
double pop(void) { return stack[--sp]; }
int    get(void) { do c = *p++; while (isSpace(c)); return c; }

void expr(const char *s)
{
    char c2;
    if (*s)
        for (expr(s+2); c==s[0] || c==s[1]; )
            c2 = c, expr(s+2), p2 += sprintf(p2, " %c", c2);
    else if (get() == '.' || isDigit(c))
        p2 += sprintf(p2, " %.15g", strtod(p-1, &p)), get();
    else if (c == '(') expr(o), c == ')' ? get() : (c = 1);
    else if (isAlpha(c)) exit(0);
    else c = 1;
}

double calc(char *p)
{
    double v;
    for(;;)
        switch (*p++) {
        case '\0': return pop();
        case ' ': break;
        case '+': push(pop() + pop()); break;
        case '*': push(pop() * pop()); break;
        case '-': v = pop(); push(pop() - v); break;
        case '/': v = pop(); push(pop() / v); break;
        default: push(strtod(p-1, &p)); break;
        }
}

int main(void)
{
    while (fgets(p = buf, sizeof buf, stdin))
        *(p2 = buf2) = 0, expr(o),
        printf(c ? " error\n" : " %s   ==> %.15g\n", buf2, calc(buf2));
    return 0;
}


No.8316

Re:数式の読み込みはgetsでは無理ですか?
投稿者---かずま(2003/07/13 13:26:30)


> では、私もやってみましょう。なんとか 50行に収めてみました。

単項-演算子がなくて、-3 + 5 の計算ができないので、追加してみました。
逆ポーランド記法では、二項演算子の - と区別するために _ としています。
また、50行以内に収めるため、スタックを calc 内に持ってきました。
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

#define isSpace(c)  isspace((c) & 0xFF)
#define isDigit(c)  isdigit((c) & 0xFF)
#define isAlpha(c)  isalpha((c) & 0xFF)

char buf[1024], buf2[1024], *p, *p2, c, o[] = "+-*/";

int get(void) { do c = *p++; while (isSpace(c)); return c; }

void expr(const char *s)
{
    char c2;
    if (*s)
        for (expr(s+2); c==s[0] || c==s[1]; )
            c2 = c, expr(s+2), p2 += sprintf(p2, " %c", c2);
    else if (get() == '.' || isDigit(c))
        p2 += sprintf(p2, " %.15g", strtod(p-1, &p)), get();
    else if (c == '(') expr(o), c == ')' ? get() : (c = 1);
    else if (c == '-') expr(s), p2 += sprintf(p2, " _");
    else if (isAlpha(c)) exit(0);
    else c = 1;
}

double calc(char *p)
{
    double v[100];  int i = 0;
    for(;;)
        switch (*p++) {
        case '\0': return v[i-1];
        case ' ': break;
        case '_': v[i-1] = -v[i-1]; break;
        case '+': --i; v[i-1] += v[i]; break;
        case '-': --i; v[i-1] -= v[i]; break;
        case '*': --i; v[i-1] *= v[i]; break;
        case '/': --i; v[i-1] /= v[i]; break;
        default: v[i++] = strtod(p-1, &p); break;
        }
}

int main(void)
{
    while (fgets(p = buf, sizeof buf, stdin))
        *(p2 = buf2) = 0, expr(o),
        printf(c ? " error\n" : " %s   ==> %.15g\n", buf2, calc(buf2));
    return 0;
}


No.8317

Re:数式の読み込みはgetsでは無理ですか?
投稿者---かずま(2003/07/13 13:41:01)


+2 や 2+ などで error のときも、calc に行ってしまうというバグがあるので
次のように訂正します。最初のプログラムも同様です。
>   printf(c ? " error\n" : " %s   ==> %.15g\n", buf2, calc(buf2));

    c ? puts(" error") : printf(" %s   ==> %.15g\n", buf2, calc(buf2));