一派护法 十九级              | 
          
            
            
             
              #include <errno.h> #include <math.h> #include <stdio.h> #include <string.h> #include "calc.h"
  int main(void) {     char buf[MAXLEN + 1];     char str[MAXLEN + 1];     double result;     FILE *fp;     int i, state;     int source, dest;
      puts("--------- 李玉牌计算器 ---------");     puts("- 1. 算式计算                  -");     puts("- 2. 进制转换                  -");     puts("- 3. 输出大写                  -");     puts("- 4. 格式化显示                -");     puts("- 5. 查看历史记录              -");     puts("--------------------------------");          printf("请选择: ");     scanf("%d", &i);     switch (i)     {     case 1:         printf("请输入算式 (不多于%d字符): ", MAXLEN);         getstr(str, sizeof(str));         errno = NOERR;         result = calc(str, strlen(str));         switch (errno)         {         case NOERR:             printf("计算结果为: %lf\n", result);             /*fp = fopen(FILENAME, "a");             fprintf(fp, "%s=%lf\n", str, result);             fclose(fp);*/             break;         case ERR_INVALID:             puts("输入的表达式有误, 无法计算");             break;         case ERR_TOOBIG:             puts("运算数超出了计算机运算范围, 无法计算");             break;         }         break;     case 2:         printf("请输入要转换的数: ");         getstr(str, sizeof(str));         strcpy(buf, str);
          printf("请输入原进制: ");         scanf("%d", &source);         if (!valid_radix(source))             break;
          printf("请输入目标进制: ");         scanf("%d", &dest);         if (!valid_radix(dest))             break;
          state = convert(str, source, dest);         switch (state)         {         case NOERR:             printf("转换的结果为: %s\n", str);             fp = fopen(FILENAME, "a");             fprintf(fp, "%s由%d进制转换为%d进制后是: %s\n", buf, source, dest, str);             fclose(fp);             break;         case ERR_INVALID:             puts("要转换的数中含有无效字符, 转换失败");             break;         case ERR_TOOBIG:             puts("数字太大, 无法转换");             break;         }         break;     case 3:
          break;     case 4:         printf("请输入要格式化的数: ");         getstr(str, sizeof(str));         if (format(str, buf))         {             printf("转换结果: %s\n", buf);             fp = fopen(FILENAME, "a");             fprintf(fp, "%s格式化后是: %s\n", str, buf);             fclose(fp);         }         else             puts("输入的数有误");         break;     case 5:         view_logs();         break;     default:         puts("输入错误");     }     return 0; }
  void addstr(char **dest, char *src) {     while (*(*dest)++ = *src++); }
  // 计算指定算式 double calc(char *str, int len) {     int n; // n为sscanf读到的字符个数     double a, b;     char op;     if (sscanf(str, "%lf%n", &a, &n) <= 0) // 读取第一个数         return errno = ERR_INVALID; // 读取失败时退出     str += n;     len -= n; // 可用字符
      while ((op = get_operator(&str, &len)) != '\0') // 获取运算符, 如果运算符不为空就循环     {         if (sscanf(str, "%lf%n", &b, &n) <= 0) // 读取另一个数             return errno = ERR_INVALID;         str += n;         len -= n;
          switch (op)         {         case '+':             a += b;             break;         case '-':             a -= b;             break;         case '*':             a *= b;             break;         case '/':             if (b == 0.0)                 return errno = ERR_INVALID;             a /= b;             break;         case '^':             a = pow(a, b);             break;         default:             return errno = ERR_INVALID;         }     }     return a; }
  // 进制转换 // 返回值为错误号 int convert(char *str, int source, int dest) {     ULONGLONG n;     ULONGLONG num = 0; // 用于存储原数     char *p;     double dbl;     static char *tpl = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; // 字符串中允许出现的字符列表     int i, j;     // 当source=dest时不能直接return, 否则无法检查出str中的无效字符
      errno = 0; // 清除错误标志(errno.h中定义的全局变量)     if (*str == '+' || *str == '-')         str++; // 忽略正负号
      /* 将原数由字符串转换为int类型 */     i = strlen(str); // str不计正负号的长度     j = 0; // j表示当前处理的是source进制下该数的哪一位     for (i--; i >= 0; i--, j++) // 遍历str的每一位数     {         if (str[i] >= 'a' && str[i] <= 'z')             str[i] -= 'a' - 'A'; // 将小写字母转换为大写字母         p = strchr(tpl, str[i]); // 在tpl字符串中查找str[i]字符         if (p == NULL)             return ERR_INVALID; // 在tpl中未找到, 说明这个字符无效                  n = p - tpl; // 当前位的数值, 例如A为10, G为16         if (n >= source)             return ERR_INVALID; // 出现了原进制中不允许出现的数字
          /*         如果powl函数计算出的结果太大以致于用double型无法表示, 则会将errno改为非零值         但double型也可以表示很大但不精确的数, 所以将其值转换为无符号int型后与原值进行比较         如果不相等, 就认为计算出错         */         dbl = powl(source, j); // source的j次方         if (errno || dbl != (UINT)dbl)             return ERR_TOOBIG; // 如果数字太大powl无法计算                  n *= (ULONGLONG)dbl;         num += n;     }
      /* 再转换为目标进制数的字符串(从低位到高位) */     for (i = 0; num != 0; i++)     {         str[i] = tpl[num % dest];         num /= dest;     }     str[i] = '\0';     reverse(str, i); // 调换字符串方向, 变为从高位到低位     return NOERR; // 无错误 }
  BOOL format(char *str, char *buf) {     BOOL only_zero = TRUE; // 是否在字符串中只发现了数字0     BOOL pt_flag = FALSE; // 确保小数点只出现一次     char *pstr = buf; // 存储buf的开始位置     int i;     *buf = '$';     if (*str == '+')         str++; // 忽略正号     else if (*str == '-')         *buf++ = *str++;
      // 忽略开头的0     while (*str == '0' && str[1] != '\0' && str[1] != '.')         str++;
      // 求整数部分的位数     for (i = 0; str[i] != '.' && str[i] != '\0'; i++);
      // 复制整数部分和小数部分的字符     // i只表示整数部分的位数     while (*str != '\0')     {         if ((*str >= '0' && *str <= '9') || (!pt_flag && *str == '.'))         {             if (*str == '.')                 pt_flag = TRUE;             else if (*str != '0')                 only_zero = FALSE; // 发现了非0数
              *buf++= *str++; // 复制字符
              if (!pt_flag)             {                 i--; // 剩余位数减1                 if (i != 0 && i % 3 == 0)                     *buf++ = ','; // 添加千位分隔符             }         }         else             return FALSE; // 出现了非法字符     }     if (*(buf - 1) == '.')         *(buf - 1) = '\0'; // 最后一个字符不能只是小数点     else         *buf = '\0';
      // 不允许出现-0或-0.0000     if (only_zero)         memmove(pstr, pstr + 1, strlen(pstr));
      return TRUE; }
  // 输入字符串到字符数组中 // buffer为数组, capacity为数组容量 void getstr(char *buffer, int capacity) {     char ch = 0;     fflush(stdin); // 删除之前输入的内容     if (capacity > 1)     {         // 跳过开头的无效字符         do         {             ch = getchar();         } while (ch == ' ' || ch == '\n' || ch == '\r');
          // 存储第一个字符         *buffer++ = ch;         capacity--;
          // 存储其他字符, 直到容量用完或者遇到回车符         while (ch = getchar(), capacity-- > 1 && ch != '\n')             *buffer++ = ch;         *buffer = '\0';     }     else if (capacity == 1)     {         *buffer = '\0'; // 若容量为1, 则只能存放\0         capacity--;     }           if (ch != '\n')         puts("输入的内容太长, 已截断部分内容");     fflush(stdin); // 跳过剩下的未存储的字符 }
  // 获取下一个运算符 // 如果没有运算符则返回\0 char get_operator(char **str, int *len) {     char ch;     // 跳过运算符前的空格和换行     while (**str == ' ' || **str == '\n' || **str == '\r')     {         (*str)++;         (*len)--;     }     ch = *(*str)++; // 获得的运算符     (*len)--;     // 跳过运算符后的空格和换行     if (ch != '\0') // 如果ch为\0, 则后续内容可以不管     {         while (**str == ' ' || **str == '\n' || **str == '\r')         {             (*str)++;             (*len)--;         }     }     return ch; }
  // 反转字符串中的部分字符 void reverse(char *str, int n) {     char temp;     int i;     int m = n / 2;     for (i = 0; i < m; i++)     {         temp = str[i];         str[i] = str[n - i - 1];         str[n - i - 1] = temp;     } }
  // 判断进制是否合法 BOOL valid_radix(int n) {     if (n >= 2 && n <= 36)         return TRUE;     else     {         puts("抱歉, 本计算器只能转换2~36进制的数");         return FALSE;     } }
  void view_logs(void) {     char ch;     FILE *fp = fopen(FILENAME, "r");     if (fp == NULL)     {         puts("无历史记录");         return;     }     // 输出文件中的所有字符, 直到结束     // 文件的末尾一定是换行符     while (ch = fgetc(fp), !feof(fp))         putchar(ch);     fclose(fp); }             
                       |