設置 | 登錄 | 註冊

目前共有3篇帖子。

計算器代碼備份

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

內容轉換:

回覆帖子
內容:
用戶名: 您目前是匿名發表。
驗證碼:
看不清?換一張
©2010-2025 Purasbar Ver3.0 [手機版] [桌面版]
除非另有聲明,本站採用知識共享署名-相同方式共享 3.0 Unported許可協議進行許可。