计算器源码

来源:互联网 发布:怎么找淘宝小二 编辑:程序博客网 时间:2024/04/30 16:54
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <stdlib.h>
#include <math.h>


#define ZERO 0
#define LEN sizeof(struct Node) // 结构体的大小
#define SIZE 21 // 操作数的最大长度


// 表达式解析分段后的节点,可以是操作数,也可以是运算符号
struct Node {
char str[SIZE];
struct Node * next;
};
int log_on = 0; // 日志输出开关 1-开,0-关
const double EPSINON = 0.000001; // 浮点数精度
const unsigned int PRECITION = 6; // 这个一定要等于常量 EPSINON 的小数位数
const char operators[] = {'+', '-', '*', '/', '^', '(', ')'}; // 运算符号


/********************* 以下部分是工具类 *********************/


/**
 * 对链表结构进行输出查看
 */
void print(struct Node * p) {
printf("%s ", p -> str);
if (p -> next != ZERO) {
print(p -> next);
}
}


/**
 * 日志输出函数
 */
void logs(char * ch, struct Node * node) {
if (log_on == 1) {
printf("[LOG] - %s: ", ch);
print(node);
printf("\n");
}
}


/**
 * 判断是否是运算符,是运算符返回1,否则返回0。
 */
int isOperator(char ch) {
int i, flag = 0;
for (i = 0; i < sizeof(operators); i++) {
if (ch == operators[i]) {
flag = 1;
break;
}
}
return flag;
}


char * toString(double num) { // 将浮点数字转换为字符串
char ch[SIZE], * c;
memset(ch, 0, sizeof(ch));//清空变量
sprintf(ch, "%f", num);
c = ch;
return c;
}


double toDouble(char * a) { // 将字符串转换为浮点数字
return atof(a);
}


/**
 * 判断给定的字符串是否是数字,目前不支持指数e的表示方法
 */
int isNumber(char str[]) {
char a[SIZE];
unsigned int i = -1;
strcpy(a, str);
for (i = 0; i < strlen(a); i++) {
if (a[i] == '.') {
break;
}
}
if (i == strlen(a)) {
strcat(a, ".");
i = 0;
} else {
i = strlen(a) - i - 1;
}

while (i < PRECITION) {
strcat(a, "0");
i++;
}
if (strcmp(a, toString(toDouble(a))) == 0) {
return 1;
} else {
return 0; // 未实现,默认输入的操作数是数字
}
return 1;
}


/********************* 以下部分是对链表进行处理、运算 *********************/


char * divide(char * a, char * b) { // 除法运算
double d = toDouble(b);
if (d < EPSINON) {
printf("警告:除数不能为零!%f\n", d);
} else {
d = toDouble(a) / d;
}
return toString(d);
}


/**
 * 执行加减法运算
 */
void exec_plus(struct Node * node) {
struct Node * head, * p, * pre;
if (node == ZERO || node -> next == ZERO) {
return;
}
logs("执行加减法", node);
head = node;
p = node;
pre = ZERO;
while (p != ZERO) {
if (strlen(p -> str) == 1) {
if (strcmp(p -> str, "+") == 0) {
strcpy(pre -> str, toString(toDouble(pre -> str) + toDouble(p -> next -> str)));
pre -> next = p -> next -> next;
p = pre;
} else if (strcmp(p -> str, "-") == 0) {
strcpy(pre -> str, toString(toDouble(pre -> str) - toDouble(p -> next -> str)));
pre -> next = p -> next -> next;
p = pre;
}
}
pre = p;
p = p -> next;
}
}


/**
 * 执行乘除法运算
 */
void exec_multiply(struct Node * node) {
struct Node * head, * p, * pre;
if (node == ZERO || node -> next == ZERO) {
return;
}
logs("执行乘除法", node);
head = node;
p = node;
pre = ZERO;
while (p != ZERO) {
if (strlen(p -> str) == 1) {
if (strcmp(p -> str, "*") == 0) {
strcpy(pre -> str, toString(toDouble(pre -> str) * toDouble(p -> next -> str)));
pre -> next = p -> next -> next;
p = pre;
} else if (strcmp(p -> str, "/") == 0) {
strcpy(pre -> str, divide(pre -> str, p -> next -> str));
pre -> next = p -> next -> next;
p = pre;
}
}
pre = p;
p = p -> next;
}
exec_plus(head);  // 乘除法运算完之后,开始执行加减法运算
}


/**
 * 执行符号运算
 */
void exec_sign(struct Node * node) {
struct Node * head, * pre, * p;
if (node == ZERO || node -> next == ZERO) {
return;
}
logs("执行符号运算", node);
head = node;
pre = ZERO;
p = node;
while (p != ZERO) {
if (strcmp(p -> str, "+") == 0 || strcmp(p -> str, "-") == 0) {
if (pre != ZERO) {
if (strlen(pre -> str) == 1 && isOperator(pre -> str[0]) == 1 
&& isNumber(p -> next -> str) == 1) {
strcat(p -> str, "1");
strcpy(p -> str, toString(toDouble(p -> str) * toDouble(p -> next -> str)));
p -> next = p -> next -> next;
}
} else {
if (isNumber(p -> next -> str) == 1) {
strcat(p -> str, "1");
strcpy(p -> str, toString(toDouble(p -> str) * toDouble(p -> next -> str)));
p -> next = p -> next -> next;
}
}
}
pre = p;
p = p -> next;
}
exec_multiply(head);  // 正负符号运算完之后,开始执行乘除法运算
}


/**
 * 执行括号运算
 */
void exec_bracket(struct Node * node) {
struct Node * head;// 把链表头保存起来
// 括号区间将链表分为三个部分,end 为第一部分的末尾节点,
// begin 为第三部分的开始节点,execNode为需要执行的链表的开始节点
struct Node * end, * begin, * execNode;
struct Node * p, * pre;// p 循环遍历指针, pre是p的上一个指针
int i = 0; // 遇到开括号加1,遇到闭括号减1
int startWithBracket = 0;// 是否输入的链表以括号开头,1-是,0-否。
if (node == ZERO || node -> next == ZERO) {
return;
}
logs("执行括号运算", node);
head = node;
pre = ZERO;
p = node;
while (p != ZERO) {
if (strcmp(p -> str, "(") == 0) {
if (i == 0) { // 记录第一个括号
execNode = p -> next;// 把括号的后一个节点记录下来
if (pre != ZERO) {
end = pre; // 把括号的前一个节点记录下来
end -> next = execNode; // 括号的前一个节点连接到后一个节点上
} else { // 链表头就是一个括号
startWithBracket = 1;
end = head;
}
}
i++;
} else if (strcmp(p -> str, ")") == 0) {
i--;
if (i == 0) { // 找到了和第一个开括号对应的闭括号
begin = p -> next;// 把括号的后一个节点记录下来
pre -> next = ZERO;// 要计算的链切断
exec_bracket(execNode);// 执行切下来的链表,递归运算
execNode -> next = begin;// 执行之后的节点再和后面的链连接起来
if (startWithBracket == 1) {
strcpy(end -> str,execNode -> str);
end -> next = execNode -> next;
startWithBracket = 0;
}
}
}
pre = p;
p = p -> next;
}
exec_sign(head); // 括号都被去掉之后,开始执行符号运算
}


/**
 * 计算链表入口
 */
double calculate(struct Node * node) {
exec_bracket(node);
return toDouble(node -> str);
}


/********************* 以下是创建链表函数 *********************/


/**
 * 对输入的内容去除空格后,创建链表数据结构
 */
struct Node * createLink () {
// head 链表头,previous 上一个节点,current 当前节点
struct Node * head, * previous, * current;
int i = 0;
char ch, str[SIZE];
head = previous = ZERO;
while ((ch = getchar()) != '\n') {
if (ch == ' ') {
continue;
}
if (isOperator(ch) == 1) {
str[i] = '\0';
if (strlen(str) > 0) { // 创建操作数
current = (struct Node *) malloc(LEN); // 开辟一个节点的内存空间
strcpy(current -> str, str);
if (previous != ZERO) {
previous -> next = current; // 上一个节点指向当前节点
} else {
head = current;
}
previous = current;
}
current = (struct Node *) malloc(LEN); // 创建操作符
current -> str[0] = ch;
current -> str[1] = '\0';
if (previous != ZERO) {
previous -> next = current; // 上一个节点指向当前节点
} else {
head = current;
}
previous = current;
i = 0;
str[i] = '\0';
} else {
// 在不超过数组最大长度的情况下,可以继续加入,
// 减1是因为要留一个字符串结束标识。
if (i < SIZE - 1) {
str[i++] = ch;
} else {
printf("警告:操作数长度不能超过 %d 个字符!\n", SIZE - 1);
}
}
}
str[i] = '\0';
if (strlen(str) > 0) {
current = (struct Node *) malloc(LEN);
strcpy(current -> str, str);
current -> next = ZERO;
} else {
current = ZERO;
}
if (previous != ZERO) {
previous -> next = current;
} else {
head = current;
}
return head;
}


/******************************** 以下部分是主函数 *************************************/


void main() {
struct Node * head;


printf("/************************************************\n");
printf(" * 描述:该计算器可执行四则混合运算。           *\n");
printf(" * 命令:logon -> 打开日志输出                  *\n");
printf(" *       logoff -> 关闭日志输出                 *\n");
printf(" *       exit -> 退出程序                       *\n");
printf(" *       help -> 帮助                           *\n");
printf(" ************************************************/\n");

while (1 == 1) {
printf("\ninput>");
head = createLink(); // 创建链表
if (strcmp(head -> str, "exit") == 0) {
break;
} else if (strcmp(head -> str, "logon") == 0) {
log_on = 1;
printf("打开日志开关!\n");
} else if (strcmp(head -> str, "logoff") == 0) {
log_on = 0;
printf("关闭日志开关!\n");
} else if (strcmp(head -> str, "help") == 0) {
printf("命令:logon -> 打开日志输出\n");
printf("      logoff -> 关闭日志输出\n");
printf("      exit -> 退出程序\n");
printf("      help -> 帮助\n");
} else {
logs("输入的运算表达式", head);
printf("=%.2f\n", calculate(head));
}
}
}
原创粉丝点击