目前共有3篇帖子。 内容转换:不转换▼
 
点击 回复
455 2
计算器代码备份
一派护法 十九级
1楼 发表于:2016-6-12 22:37
#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);
}
一派护法 十九级
2楼 发表于:2016-6-12 22:37
【头文件】
#define FILENAME "logs.txt"
#define MAXLEN 300

#define NOERR 0
#define ERR_INVALID 1
#define ERR_TOOBIG 2

typedef unsigned int UINT; // 32位无符号整数
typedef long long LONGLONG; // 64位有符号整数
typedef unsigned long long ULONGLONG; // 64位无符号整数
typedef enum {FALSE = 0, TRUE = 1} BOOL;

void addstr(char **dest, char *src);
double calc(char *str, int len);
int convert(char *str, int source, int buffer);
BOOL format(char *str, char *buf);
void getstr(char *buffer, int capacity);
char get_operator(char **str, int *len);
void reverse(char *str, int n);
BOOL valid_radix(int n);
void view_logs(void);
一派护法 十九级
3楼 发表于:2016-6-12 22:37
【生成的日志】
476ABC由19进制转换为10进制后是: 10861628
7f由16进制转换为10进制后是: 127
-476ABC由19进制转换为10进制后是: -10861628
123456789111格式化后是: $123,456,789,111
-4571564格式化后是: $-4,571,564
-3.85214格式化后是: -3.85214
-7414156156.4545414545564864564564545641515154154154545455546455644556454564545645456454564545645546454556445564456545465454645564545644556454654546545465454645546455644556445654516245614526414556445564格式化后是: -7,414,156,156.4545414545564864564564545641515154154154545455546455644556454564545645456454564545645546454556445564456545465454645564545644556454654546545465454645546455644556445654516245614526414556445564

回复帖子

内容:
用户名: 您目前是匿名发表
验证码:
(快捷键:Ctrl+Enter)
 

本帖信息

点击数:455 回复数:2
评论数: ?
作者:巨大八爪鱼
最后回复:巨大八爪鱼
最后回复时间:2016-6-12 22:37
 
©2010-2024 Arslanbar Ver2.0
除非另有声明,本站采用知识共享署名-相同方式共享 3.0 Unported许可协议进行许可。