スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

コンソール数式電卓

今回は、先日、コンソールで動く「数式電卓」なるものを作ったので、
それを載せます・・・。

数式電卓は、数式をそのまま入力すると、その計算結果を返すものです。
といっても、最初なのでいろいろな制約、というか機能を激しく制限してます・・・。

1.オペランド(値)は自然数のみとする。

小数、負の数値、文字を使った定数、変数は使えません・・・。

2.オペレータ(演算子)は加算(+)、減算(-)のみとする。

掛け算(*)、割り算(/)、剰余算(%)、などは使えません。
演算子の優先順位を考慮するのは面倒だからです。(え・・・

3.数式にはスペースなど空白を入れない

トリム関数とかは入れてないので、空白入れないでください・・・。

4.括弧は使えない

2と同様、演算子の優先順位を考慮しないので括弧も使えません・・・。

5.最初の項負の数は使えない

1で自然数と限定しているので、負の整数は使えません。
特に、先頭で負の数を指定すると、計算ができ、しかも答えが合っています。
しかし、内部では正しい処理が出来てないためバッファオーバーランなどで落ちることがあります。
なので負の数は使わないでください・・・。

結局は足し算、引き算、しか使えないとっても残念な電卓ですね・・・。
(※.補足ですが、答えが負の値になるのは問題ありません・・・。)

さて中身ですが、


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


標準入出力stdio.h以外に、

・stdlib.h(atoiで使う)
・string.h(文字列操作)
・ctype.h(文字が数字か否かを判定するisdigitなどがある)

などをインクルード。

あと、文字列バッファのサイズとかをマクロで定義しておきます。


#define EXPRESSION_STRING_MAX_LENGTH 128
#define OPERAND_STRING_MAX_LENGTH 10
#define OPERAND_MAX_COUNT 10
#define OPERATOR_STRING_MAX_LENGTH 1
#define OPERATOR_MAX_COUNT 10


変数の宣言とかは置いといて、


printf("Input expression\n");

while(1){

        .
        .
        .

  if (strcmp(expression_string_buffer, "end") == 0){

    return 0;

  }


endが入力されるまで、数式を入力することが可能です・・・。


fgets(expression_string_buffer, EXPRESSION_STRING_MAX_LENGTH + 2, stdin);

expression_string_length = strlen(expression_string_buffer);

expression_string_buffer[expression_string_length - 1] = '\0';


fgetsで数式文字列を取得、
改行文字'\n'に'\0'を代入して、改行を除去してます・・・。


while(1){

  is_digit = isdigit(expression_string_buffer[i]);

        .
        .
        .

}


数式文字列の文字をisdigitで1つずつ調べます。
12+345を例に考えてみましょう。


if (is_digit){

  if (i >= expression_string_length - 1){

        .
        .
        .

  else{

    operand_e = i;
    i++;
    continue;

  }

}


チェックしている文字が数字で、かつ最後の文字でない時は、次の文字を調べます。
最初の2回は'1'、'2'、と数字なのでここに来ます。


if (is_digit){

        .
        .
        .

}
else{

  if (expression_string_buffer[i] == '+' || expression_string_buffer[i] == '-'){

    operand_l = operand_e - operand_s + 1;
    if (operand_l > OPERAND_STRING_MAX_LENGTH){
      operand_l = OPERAND_STRING_MAX_LENGTH;
    }
    strncpy(operand_string_array_buffer[operand_count], operand_ptr_s, operand_l);


文字が'+'、'-'、の時はオペランド配列にそこまでの数字文字列を格納します。
ここでは、'1'、'2'、の後に、'+'、が出たので、"12"を格納します。


    operator_string_array_buffer[operator_count][0] = expression_string_buffer[i];
    operator_string_array_buffer[operator_count][1] = '\0';


'+'、または、'-'、をオペレータ配列に格納する。


    /*printf("operand[%d]%s\n", operand_count, operand_string_array_buffer[operand_count]);*/
    /*printf("operator[%d]%s\n", operator_count, operator_string_array_buffer[operator_count]);*/

    i++;
    operand_ptr_s = &expression_string_buffer[i];
    operand_s = i;
    operand_e = i;
    operand_count++;
    operator_count++;

  }


両方とも格納したので、次の準備へ・・・。
数字の連続 -> 記号 -> 数字の連続 -> 記号 ->・・・と繰り返し、オペランド配列とオペレータ配列に格納していきます。


  else{

    printf("error!\n");
    return -1;

  }

}


不正な文字なら、エラー終了・・・。


if (is_digit){

  if (i >= expression_string_length - 1){

    operand_l = operand_e - operand_s + 2;
    if (operand_l > OPERAND_STRING_MAX_LENGTH){
      operand_l = OPERAND_STRING_MAX_LENGTH;
    }
    strncpy(operand_string_array_buffer[operand_count], operand_ptr_s, operand_l);

    /*printf("operand[%d]%s\n", operand_count,     operand_string_array_buffer[operand_count]);*/

    break;

  }
  else{

        .
        .
        .

  }

}


数字で最後の文字だったときは、記号を待たずにオペランド配列に入れます。
"345"の'5'が来た時ですね・・・。
オペランド配列はオペレータより1個多い状態です。

これで、オペランドとオペレータを分解できました。
あとは、オペランド配列の数字をatoiで数値に換え、オペレータ配列の記号に沿って、足し引きしていけばいいわけです・・・。


calc_result = atoi(operand_string_array_buffer[0]);
for (i = 0; i < operator_count; i++){
  if (operator_string_array_buffer[i][0] == '+'){
    calc_result = calc_result + atoi(operand_string_array_buffer[i + 1]);
    }
  else if (operator_string_array_buffer[i][0] == '-'){
    calc_result = calc_result - atoi(operand_string_array_buffer[i + 1]);
  }
}

printf("= %d\n", calc_result);


以下ソース全文

main.c

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

#define EXPRESSION_STRING_MAX_LENGTH 128
#define OPERAND_STRING_MAX_LENGTH 10
#define OPERAND_MAX_COUNT 10
#define OPERATOR_STRING_MAX_LENGTH 1
#define OPERATOR_MAX_COUNT 10

int main(void){

  char expression_string_buffer[EXPRESSION_STRING_MAX_LENGTH + 3];
  char operand_string_array_buffer[OPERAND_MAX_COUNT][OPERAND_STRING_MAX_LENGTH + 1];
  char operator_string_array_buffer[OPERATOR_MAX_COUNT][OPERATOR_STRING_MAX_LENGTH + 1];
  int expression_string_length = 0;
  int operator_count = 0;
  int operand_count = 0;
  int i;
  int is_digit;
  int operand_s;
  int operand_e;
  int operand_l;
  char *operand_ptr_s;
  int calc_result = 0;

  printf("Input expression\n");

  while(1){

    memset(expression_string_buffer, 0, sizeof(char) * (EXPRESSION_STRING_MAX_LENGTH + 3));
    for (i = 0; i < OPERAND_MAX_COUNT; i++){
      memset(operand_string_array_buffer[i], 0, sizeof(char) * (OPERAND_STRING_MAX_LENGTH + 1));
    }

    printf(">");

    fgets(expression_string_buffer, EXPRESSION_STRING_MAX_LENGTH + 2, stdin);

    expression_string_length = strlen(expression_string_buffer);

    expression_string_buffer[expression_string_length - 1] = '\0';

    /*printf("%s", expression_string_buffer);*/

    if (strcmp(expression_string_buffer, "end") == 0){

      return 0;

    }

    expression_string_length = strlen(expression_string_buffer);

    i = 0;
    is_digit = 0;
    operand_s = 0;
    operand_e = 0;
    operand_ptr_s = expression_string_buffer;

    while(1){

      is_digit = isdigit(expression_string_buffer[i]);
      if (is_digit){

        if (i >= expression_string_length - 1){

          operand_l = operand_e - operand_s + 2;
          if (operand_l > OPERAND_STRING_MAX_LENGTH){
            operand_l = OPERAND_STRING_MAX_LENGTH;
          }
          strncpy(operand_string_array_buffer[operand_count], operand_ptr_s, operand_l);

          /*printf("operand[%d]%s\n", operand_count, operand_string_array_buffer[operand_count]);*/

          break;

        }
        else{

          operand_e = i;
          i++;
          continue;

        }

      }
      else{

        if (expression_string_buffer[i] == '+' || expression_string_buffer[i] == '-'){

          operand_l = operand_e - operand_s + 1;
          if (operand_l > OPERAND_STRING_MAX_LENGTH){
            operand_l = OPERAND_STRING_MAX_LENGTH;
          }
          strncpy(operand_string_array_buffer[operand_count], operand_ptr_s, operand_l);

          operator_string_array_buffer[operator_count][0] = expression_string_buffer[i];
          operator_string_array_buffer[operator_count][1] = '\0';

          /*printf("operand[%d]%s\n", operand_count, operand_string_array_buffer[operand_count]);*/
          /*printf("operator[%d]%s\n", operator_count, operator_string_array_buffer[operator_count]);*/

          i++;
          operand_ptr_s = &expression_string_buffer[i];
          operand_s = i;
          operand_e = i;
          operand_count++;
          operator_count++;

        }
        else{

          printf("error!\n");
          return -1;

        }

      }

    }

    calc_result = atoi(operand_string_array_buffer[0]);
    for (i = 0; i < operator_count; i++){
      if (operator_string_array_buffer[i][0] == '+'){
        calc_result = calc_result + atoi(operand_string_array_buffer[i + 1]);
      }
      else if (operator_string_array_buffer[i][0] == '-'){
        calc_result = calc_result - atoi(operand_string_array_buffer[i + 1]);
      }
    }

    printf("= %d\n", calc_result);

  }

  return 0;

}


実行すると、


Input expression
>


と出るので、


Input expression
>12+345


と入力すると、


Input expression
>12+345
= 357
>


と計算結果が出て、次の入力待ちをします・・・。
終了するなら、


Input expression
>end


とします。

ということで、加減算は出来ましたが、多くの課題が残っています。
やってみて気づいただけでも、

・-が使えない
・乗算、除算
・空白
・括弧、演算子の優先順位
・1++3や、オペレータが多いときなどのエラー処理(場合によっては、落ちることもある・・・。)

とこれだけあります・・・。ロジックとしてもひどいもんですわ・・・。

まあ、次回以降、一つずつ課題を潰していこうと思います・・・。

これにて・・・。
スポンサーサイト

テーマ : プログラミング
ジャンル : コンピュータ

コメントの投稿

非公開コメント

承認待ちコメント

このコメントは管理者の承認待ちです

承認待ちコメント

このコメントは管理者の承認待ちです
検索フォーム
RSSリンクの表示
リンク
ブロとも申請フォーム

この人とブロともになる

QRコード
QR
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。