您的位置:首页 > 编程语言 > C语言/C++

一个简单的四则运算程序C语言实现--实现处理括号

2011-06-14 23:10 871 查看
说明:经过今天的测试解决了括号处理的一些问题。比如由于增加了paren 结构元素之后,复制函数没有更新。

及非直接括号。等类型。如(2+4)等非必须性括号。

没有使用高级的数据结构如堆栈啦。

但程序本还是挺好的。注释也挺 不错。

写得也不错。 哈哈。两天的成果啊!完全自己写的啊!

/***
* @author banxi1988
* @mail  banxi1988 at gmail.com
* @qq    787928000
* 一个简单的表达式四则运算器。C语言实现。实现处理括号。
* @bug-report : if have any suggestion and find any bug please send mail  to  my mail or send massage to my qq! thanks
*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>
//#define BANXI_DEBUG 1 // 是否为调试模式
#define MAX_LEN  1024 //最长的运算表达式。
#define D_BIT_LEN  32 //最大数值位包括小数点。
/***
* 一些有用的宏,关于字符操作
*/
#define is_numerical(ch) (strchr("0123456789.",(ch)) != NULL) // 决断是否是组成数值的元素
#define is_operator(ch) (strchr("+-*/",(ch)) != NULL) // 判断是否是运算符
#define is_muldiv(ch)   (strchr("*/",(ch))   != NULL) // 判断是否是乘法或除法
#define is_paren(ch)    (strchr("()",(ch))   != NULL) //判断是否是括号
#define is_end_input(ch) (ch == EOF || ch == '/n') //判断是否是输入的结尾。一次只允许输入一行。所以..
typedef enum OP_TYPE_TAG{OPERAND,OPERATOR,LPAREN,RPAREN}OP_TYPE; // 表达式元素类型,lparen 意为左括号。rparen意为右括号
typedef struct OP_ITEM_TAG{ // 表达式元素项类型
double operand; // 用来存储操作数
char operator; // 存储操作符
char paren; // 存储括号
OP_TYPE op_type; // 存储元素类型
}OP_ITEM;
OP_ITEM op_items[MAX_LEN] = {{0,0,0,0}};// 表达式元素项数组。
int items_count = 0;  // 元素项数
int operand_count = 0; // 操作数数
int operator_count = 0;// 操作符数
int lparen_count = 0; //左括号数
int rparen_count = 0; //右括号数
/***
* @function: 移除表达式元素之间空白符。
*  因为'/n'也是空白符。一般输入当中也容易把作为输入结束标志。
*  所以。如果处理'/n'当作结束标志。
*/
int remove_space(){
char ch;
while(isspace(ch=getchar())&&(!is_end_input(ch)));
ungetc(ch,stdin);
}//end remove_space
/***
* @function: 如果输入的表达式操作数位数过大,进行提示。
*  虽然double型数据可以达到正负37位。但是此程序只处理32位。
*  所以提示。因为小程序,不想处理那么大的数值
*/
int bits_length_tips(int index){
if(index > 37){
printf("WARNING:YOU HAVE INPUT A TOO LARGER NUMBER,IF HAVE NO POINT!/n");
}else if(index > D_BIT_LEN){
printf("WARNING:YOU HAVA INPUT A LARGER NUMBER!/n");
}//
}//end bits_length_tips
/***
* @function: 扫描标准输入。组建表达式。并判断是否是有效的运算表达式。
*      此函数比较关键。逻辑相当复杂点。经过重构之后,显得稍微清楚了点。
*      合理利用库函数。是一大亮点。
*/
int scan_input(){
char buffer[D_BIT_LEN] = {'/0'};
char operator = 'a';
double operand = 0.0;
char ch = 'a';
OP_TYPE op_type;
int index = 0; //for digit bit count
int flag = 0; //for float . token count
int fail = 0;
while(1){
remove_space();
ch = getchar();
if(is_end_input(ch)){
break;
}//end end if

if(is_numerical(ch)){
/// 接受到非数字组成字符跳出,或者空格也跳出
index = 0;
do{
if(isdigit(ch)){
buffer[index++] = ch;
}else{
flag++;
if(flag > 1){
printf("ERROR:too many /'./' char,need a number/n");
exit(EXIT_FAILURE);
}else{
buffer[index++] = ch;
}
}//
}while(is_numerical(ch=getchar()));//end numerical while
ungetc(ch,stdin);// 将空格或者其它字符放回,以便下面处理
bits_length_tips(index); // 超多位数可能越界提示
index = (index > 36)?36:index;
buffer[index] = '/0';
operand = atof(buffer);

op_items[items_count].operand = operand;
op_items[items_count].op_type = OPERAND;
operand_count++;
items_count++;
}else if(is_operator(ch)){
op_items[items_count].operator = ch;
op_items[items_count].op_type = OPERATOR;
operator_count++;
items_count++;
}else if(is_paren(ch)){
op_items[items_count].paren = ch;
//	op_type = (ch == '(')?LPAREN:RPAREN;
if(ch == '('){
op_type = LPAREN;
lparen_count++;
}else{
op_type = RPAREN;
rparen_count++;
}
op_items[items_count].op_type = op_type;
items_count++;
}else{
printf("ERROR:NON VALID CHAR %c/n",ch);
exit(EXIT_FAILURE);
}//end ch judge
}//end scan while
if(items_count % 2 == 0){
printf("ERROR:non valid expression/n");
fail = 1;
}//
if(lparen_count != rparen_count){
printf("ERROR:PARENTHESIS NOT MATCH!/n");
fail = 1;
}//
if(operand_count != (operator_count + 1)){
printf("ERROR:OPERAND OR OPERATOR NOT ENOUGH/n");
fail = 1;
}//
if(fail){
exit(EXIT_FAILURE);
}//
return 0;
}// end scan_input
/***
* @function:用于调试时打印op_items中的数据项。
*/
void
print_data(){
int i = 0;
for(i  = 0; i < items_count; i++){
if(op_items[i].op_type == OPERAND){
printf("%d/t%lf/t",i,op_items[i].operand);
}//
else if(op_items[i].op_type == OPERATOR){
printf("%d/t%c/t",i,op_items[i].operator);
}else{
printf("%d/t%c/t",i,op_items[i].paren);
}//
printf("/n");
}//end for
}//end print_data
/***
* @function :根据传入的两个数及运算符即操作符,返回运算结果
* @return : double
*  说明:均为从左到右运算
*/
double cal(double left,char op,double right){
switch(op){
case '+':return left+right;break;
case '-':return left - right ;break;
case '*':return left*right;break;
case '/':
if(right == 0){
printf("divide not be 0/n");exit(EXIT_FAILURE);
}//
else{
return left/right;
}
break;
default:
printf("non valid operator %c /n",op);
exit(EXIT_FAILURE);
break;
}//end op switch
}//end cal
/**
* @function :将下标from处的op_items内容复制到dest下标处。dest处的内容将被覆盖。
*/
void copy_items(int dest,int from){
op_items[dest].operand = op_items[from].operand;
op_items[dest].operator = op_items[from].operator;
op_items[dest].paren = op_items[from].paren;
op_items[dest].op_type = op_items[from].op_type;
}//end copy_items
/**
* @function: 从start下标处开始。将steps后的op_items中的内容往前移steps个单位。
*			  即start后面的steps个数组内容没有了将后面的向前移动以便对齐。
*
*/
int move_items(int start,int steps){
int index = 0;
for(index = start ; (index+steps) < items_count; index++){
copy_items(index,index+steps);
}//
return 0;
}//end move_items
/***
* @fuction: 按顺序查找操作符的在op_items中的下标。
* 依赖items_count
*/
int find_operator(){
int i = -1;
int end = items_count -1;
for(i = 1; i < end; i++){
if(op_items[i].op_type == OPERATOR){
return i;
}//END IF
}//end for
return -1;
}//
/**
*  @function : 扫描op_items数组,查找乘法或者除法。如果返回找到第一个的下标。
*				如果没有找到返回-1
*/
int find_muldiv(){
int i = 0;
int end = items_count -1;
for(i = 1; i < end; i++){
if(op_items[i].op_type == OPERATOR){
if(is_muldiv(op_items[i].operator)){
return i;
}//end
}//end op_type if
}//
return -1;
}//
/***
* @function  :查找最里面的左括号。如果找到返回相应op_items下标。否则返回-1
*
*/
int find_inner_lparen(){
int innerest = -1;
int i = 0;
int end = items_count - 1;
for(i = 0; i < end; i++){
if(op_items[i].op_type == LPAREN){
innerest = i;
}//
}//end i for
return innerest;
}//
/***
* @function  :查找最里面的左括号。如果找到返回相应op_items下标。否则返回-1
*
*/
int find_inner_rparen(){
int i = 0;
int end = items_count - 1;
for(i = 0; i < end; i++){
if(op_items[i].op_type == RPAREN){
return i;
}//
}//end i for
return -1;
}//

/***
* @function : 带优先级的表达式计算。括号,最优,乘除次之。接着加减。
*			   一种思路就是,先扫描整个op_items 数组。找到优先级最高的计算。
*			   然后将后面前移。
*/
double cal_exp_with_pri(){
int index = 0;
int rparen_index = 0;
int i = 0;
int op_index = 0; // 操作符下标
int result_index = 0;// 运算结果存储位置下标。
int steps = 0; // 前移项数
int end = 0;
double result = 0;
op_index = 0;
end = operator_count + 1; //因为把计算部分统一移到了for的开头。所以要多一次循环。
for(i = 0; i < end; i++){
if(op_index > 0){
result = cal(op_items[op_index - 1].operand,op_items[op_index].operator,op_items[op_index+1].operand);
op_items[result_index].operand = result;
op_items[result_index].op_type = OPERAND;
move_items(result_index+1,steps);
items_count -= steps;
#ifdef BANXI_DEBUG
printf("/n=====CURRENT OP_ITEMS=============================/n");
print_data();
#endif
}//end
index = find_inner_lparen();
rparen_index = find_inner_rparen(); // 处理外围大括号情况。
if((index > -1) && (rparen_index - index)== 4){
op_index = index + 2;
result_index = index;
steps = 4;
continue; //
}//end if
index = find_muldiv();
if(index > 0){
op_index = index;
result_index = index -1;
steps = 2;
continue; //
}//

index = find_operator(); // 执行到这里说明已经没有括号,乘除了。返回的是加减法操作符。
if(index > 0){
op_index = index;
result_index = index -1;
steps = 2;
continue;
}//end if

}//end operator_count for
if(items_count > 2)return op_items[1].operand; // 出现外围的括号无法依靠上面的处理完全消除(12) 这样的情况
return op_items[0].operand;
}//end
int main(void){
double result = 0.0;
scan_input();
#ifdef BANXI_DEBUG
printf("/n=======CURRENT OP_ITEMS================================/n");
print_data();
#endif // BANXI_DEBUG
result = cal_exp_with_pri();
printf("/t %.2lf/n",result);
printf("calculate takes time %ld seconds /n",clock()/CLOCKS_PER_SEC);
return EXIT_SUCCESS;
}//end
/*****
运行及测试结果:
调试模式输出如下:
banxi1988@banxi:~/cpp/c/exp_cal$ gcc -g -o simple simple.c
banxi1988@banxi:~/cpp/c/exp_cal$ ls
exp_calSession.vim  simple01.c           simple.c           test_simple.txt
simple              simple01_with_pri.c  simple_with_pri.c
banxi1988@banxi:~/cpp/c/exp_cal$ vi test_simple.txt
banxi1988@banxi:~/cpp/c/exp_cal$ cat test_simple.txt  | ./simple
0	(
1	(
2	2.000000
3	+
4	1.000000
5	)
6	*
7	3.000000
8	+
9	3.000000
10	)
0	(
1	3.000000
2	*
3	3.000000
4	+
5	3.000000
6	)
==============================================================
0	(
1	9.000000
2	+
3	3.000000
4	)
==============================================================
0	(
1	12.000000
2	)
==============================================================
0.00
calculate takes time 0 seconds
banxi1988@banxi:~/cpp/c/exp_cal$ gcc -g -o simple simple.c
banxi1988@banxi:~/cpp/c/exp_cal$ cat test_simple.txt  | ./simple
=====	CURRENT OP_ITEMS ====================================
0	(
1	(
2	2.000000
3	+
4	1.000000
5	)
6	*
7	3.000000
8	+
9	3.000000
10	)
=====	CURRENT OP_ITEMS ====================================
0	(
1	3.000000
2	*
3	3.000000
4	+
5	3.000000
6	)
=====	CURRENT OP_ITEMS ====================================
0	(
1	9.000000
2	+
3	3.000000
4	)
=====	CURRENT OP_ITEMS ====================================
0	(
1	12.000000
2	)
12.00
calculate takes time 0 seconds
banxi1988@banxi:~/cpp/c/exp_cal$ gcc -g -o simple simple.c
banxi1988@banxi:~/cpp/c/exp_cal$ cat test_simple.txt
((2+1)*3+3)
banxi1988@banxi:~/cpp/c/exp_cal$ cat test_simple.txt  | ./simple
=======CURRENT OP_ITEMS================================
0	(
1	(
2	2.000000
3	+
4	1.000000
5	)
6	*
7	3.000000
8	+
9	3.000000
10	)
=====CURRENT OP_ITEMS=============================
0	(
1	3.000000
2	*
3	3.000000
4	+
5	3.000000
6	)
=====CURRENT OP_ITEMS=============================
0	(
1	9.000000
2	+
3	3.000000
4	)
=====CURRENT OP_ITEMS=============================
0	(
1	12.000000
2	)
12.00
calculate takes time 0 seconds
banxi1988@banxi:~/cpp/c/exp_cal$ gcc -g -o simple simple.c
banxi1988@banxi:~/cpp/c/exp_cal$ cat test_simple.txt  | ./simple
12.00
calculate takes time 0 seconds
banxi1988@banxi:~/cpp/c/exp_cal$
*
*/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: