アプリ版:「スタンプのみでお礼する」機能のリリースについて

c++でファイルに書かれている数式を読む込んで計算をしたいのですが、例えば8%(1+3)というのがファイルに書かれていた場合fscanfを使って読みこめられますか?
もしできたらどうすればよいでしょうか?

A 回答 (4件)

二分木が解るなら以下のページを参考にしてください。


対応演算子は()/*-+です。

参考URL:http://www.warp.or.jp/~tender/reversepoland_html …
    • good
    • 0

トークンは数字、演算子などの一塊の意味のある文字列とでも


考えてください。

(ファイルだけではなく)文字列中に収められた式などを扱う場合、コンピュータではそのままでは
扱えないので、一旦意味の解釈をしやすい単位(トークン単位)に変換します。
この後で、トークン同士の関係を見ながら、式の意味を解析して
計算していきます。
トークンに解析する際は、文字列からトークンを切り出すので、
解析対象の文字列から1文字ずつ読み込んで判断する必要があります。
1文字ずつなので、これはfgetcで取り込んでも良いですし、
fgetsで1行分取り込んで、読み込みバッファから1文字ずつ読み込む
(char* buff[i])でもいいです。

結局、ファイルに書かれている内容は、単純にcharの配列としてしか
見てくれないので、文字列のデータを意味のあるデータに変換し、
計算を行わせる必要があります。この処理は、コンピュータで自動的に
やってくれないので、全て手動で行う必要があります。
(fscanfなどでは扱えないということになります)
解析処理自体は、kotonさんが対応させたい式の要素(=トークン)に
分解できれば良いので、汎用でなければ、ある程度コンパクトに出来ると思います。

後は、試行錯誤していくしかないと思います(^^;
#こういう、構文解析などは学校の課題でやった記憶はありますが・・・
    • good
    • 0

以前、整数の数式を処理するのに、作ったCのプログラムです。


ちゃんとした、数式の解析をやっていないので、そういう点ではあんまり参考にならないかもしれませんが、参考までに
剰余計算(%)は未実装ですが、割り算とほぼ同じ考えでいけると思います。
()が最優先で、*/が同列で、+-が同列左優先で計算しています。
参考URLは、OKWebでアップするのに、JAVAで書いたバージョンです。やっていることは、ほぼ同じです。(有理数演算になっています)
C++の場合、java版からの方が簡単に変更できるかもしれません。
-------------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define MAXLEN 80
#define STACKSIZE 3

int calc(char *str){
/* (3+5)*7-2 のような文字列を計算する */
char cstack[STACKSIZE]; /* 演算子スタック */
int vstack[STACKSIZE]; /* 値スタック */
int vsp,csp;
int level;
int sign=0;
int wk=0;
char buff[MAXLEN];
char *p,*pwk,cwk;

/* trim */
for(pwk=p=str;*p;p++)
if(*p=='\t' || *p==' ')
continue;
else
*pwk++=*p;
*pwk=*p;
vsp=csp=STACKSIZE;
cstack[--csp]='@';/* empty mark */
p=str;
while(*p){
if(isdigit(*p)){
if(sign==0)sign=1;
wk=wk*10+ *p++ - '0';
} else { /* 数字以外の文字が現れた時は、数が決定する */
if(sign!=0)vstack[--vsp]=sign*wk; /* 数をスタックに積む */
wk=0; /* wkを次の数の読み取りのために初期化する */
sign=0;
switch(*p){
case '*':
case '/':
cwk=cstack[csp];
if(cwk=='@' || cwk=='+' || cwk=='-')
cstack[--csp]=*p;
else{
if(cwk=='*')
vstack[vsp+1]*=vstack[vsp];
else if(cwk=='/')
vstack[vsp+1]/=vstack[vsp];
vsp++;
cstack[csp]=*p;
}
p++;
break;
case '-':
if(str==p){
sign=-1;
p++;
break;
} else if(NULL!=strchr("*/+-",*(p-1))){
sign=-1;
p++;
break;
}
case '+':
cwk=cstack[csp];
if(cwk=='@')
cstack[--csp]=*p;
else {
switch(cwk){
case '+':
vstack[vsp+1]+=vstack[vsp];break;
case '-':
vstack[vsp+1]-=vstack[vsp];break;
case '*':
vstack[vsp+1]*=vstack[vsp];break;
case '/':
vstack[vsp+1]/=vstack[vsp];break;
}
vsp++;
cstack[csp]=*p;
}
p++;
break;
case '(': /* カッコ中身でcalcを呼び出す */
p++;
level=1;
sign=1;
for(pwk=buff;*p;p++){
if(*p==')'){
if(--level==0){
*pwk='\0';
wk=calc(buff);
break;
}
} else if(*p=='('){
level++;
}
*pwk++=*p;
}
if(level){/* カッコの数が合っていない時 */
*pwk='\0';
wk=calc(buff);
} else
p++;
break;
case ')':/* ありえないはず */
p++;
fprintf(stderr,"閉じカッコが多すぎる\n");
break;
default:
fprintf(stderr, "使用できない文字[%c]がある\n",*p++);
}
}
}
vstack[--vsp]=sign*wk;
while('@'!=(cwk=cstack[csp++])){
switch(cwk){
case '+':
vstack[vsp+1]+=vstack[vsp];break;
case '-':
vstack[vsp+1]-=vstack[vsp];break;
case '*':
vstack[vsp+1]*=vstack[vsp];break;
case '/':
vstack[vsp+1]/=vstack[vsp];break;
}
vsp++;
}
return (vstack[vsp]);
}
void main(void){
char buff[MAXLEN];

gets(buff);
printf("=%d\n", calc(buff));
exit(EXIT_SUCCESS);
}

参考URL:http://okweb.jp/kotaeru.php3?q=1384055
    • good
    • 0

数式の書き方のバリエーションがあり過ぎるので、fscanf では困難かと思います。


数式を読み込んで処理するという場合セオリー通りやるとしたら、
---
(1) fgets で行ごと読み込んで、
(2) トークン単位に分解しつつ、
(3) 優先順位を考慮して値を計算する
---
という事になります。トークン単位の分解というのは、上記の例では「8」「%」「(」「1」「+」「3」「)」という具合に、数値や演算子単位に切り出す事です。
優先順位の考慮は、「%」より「(」「)」の方を優先して計算しなければならないという事で、スタックを使って優先度の低い計算を一時棚上げしておいて優先度の高い方の計算が終わったら、続きを計算するといった処理が必要です。

この辺りの処理は、説明し出すと本が書けてしまう程長くなるので、詳しくはコンパイラの教科書的な本を読むと良いと思います。
学生の時にコンパイラの授業でこういった理論を学んで目からウロコだった記憶があります。読んでおいて損はありません!今でもものすごく参考になっています。

この回答への補足

なるほどfgetsですか。ありがとうございます。
トークン単位というのはfgets(buff,255,fp)と
buffに読み込んで処理するのですか?
%cで一文字ずつ読み込んで数か否かを判別すると
言うのを思いついたのですが、どうやるのか分からないので悩んでいます。
教科書にはトークン単位は載っていませんでした。

補足日時:2005/07/24 13:11
    • good
    • 0

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