综合案例

Tutorial: 教程一 Category: C语言 Published: 2026-04-07 13:58:26 Views: 20 Likes: 0 Comments: 0
13. 综合案例

在本章中,我们将通过几个综合案例,应用前面章节中学习到的 C 语言知识,深入理解和掌握 C 语言的实际应用。这些案例涵盖了不同的难点和应用场景,旨在帮助你巩固所学知识,并提升解决实际问题的能力。每个案例都包含详细的代码示例、完整的注释以及详细的解释,确保你能够全面理解每个程序的工作原理。

13.1 计算器程序

本案例将实现一个功能齐全的命令行计算器,支持基本的算术运算(加、减、乘、除)以及更复杂的运算,如取余和幂运算。计算器将具备用户友好的界面,能够连续进行多次计算,直到用户选择退出。

程序需求
  1. 基本功能

    • 加法 (+)
    • 减法 (-)
    • 乘法 (*)
    • 除法 (/)
    • 取余 (%)
    • 幂运算 (^)
  2. 用户界面

    • 显示可用的操作符
    • 提示用户输入操作符和操作数
    • 显示计算结果
    • 提供退出选项
  3. 错误处理

    • 处理除以零错误
    • 处理无效的操作符输入
    • 处理输入格式错误
程序代码
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <locale.h>
#ifdef _WIN32
    #include <io.h>      // 包含 _setmode 和 _fileno
    #include <fcntl.h>   // 包含 _O_U8TEXT
#endif

// 函数声明
double add(double a, double b);
double subtract(double a, double b);
double multiply(double a, double b);
double divide(double a, double b);
int modulo(int a, int b);
double power_func(double a, double b);
void displayMenu();

int main() {

    // 在Windows上设置控制台为UTF-8编码
    #ifdef _WIN32
        // 将标准输出设置为UTF-8模式
        if (_setmode(_fileno(stdout), _O_U8TEXT) == -1) {
            perror("设置标准输出为UTF-8失败");
            return 1;
        }
    #endif

    // 设置区域,支持宽字符
    setlocale(LC_ALL, "");

    char operator;
    double num1, num2, result;
    int int_num1, int_num2, mod_result;
    int choice = 1;

    // 使用宽字符输出中文标题
    wprintf(L"===== 简易计算器 =====\n");

    while(choice) {
        displayMenu();
        wprintf(L"请输入操作符: ");
        scanf(" %c", &operator); // 注意在%c前加空格,跳过前面的换行符

        // 判断操作符并执行相应的操作
        switch(operator) {
            case '+':
                wprintf(L"请输入两个数(空格分隔): ");
                if(scanf("%lf %lf", &num1, &num2) != 2) {
                    wprintf(L"输入格式错误,请输入两个数。\n");
                    // 清空输入缓冲区
                    while(getchar() != '\n');
                    break;
                }
                result = add(num1, num2);
                wprintf(L"结果: %.2lf + %.2lf = %.2lf\n\n", num1, num2, result);
                break;
            case '-':
                wprintf(L"请输入两个数(空格分隔): ");
                if(scanf("%lf %lf", &num1, &num2) != 2) {
                    wprintf(L"输入格式错误,请输入两个数。\n");
                    while(getchar() != '\n');
                    break;
                }
                result = subtract(num1, num2);
                wprintf(L"结果: %.2lf - %.2lf = %.2lf\n\n", num1, num2, result);
                break;
            case '*':
                wprintf(L"请输入两个数(空格分隔): ");
                if(scanf("%lf %lf", &num1, &num2) != 2) {
                    wprintf(L"输入格式错误,请输入两个数。\n");
                    while(getchar() != '\n');
                    break;
                }
                result = multiply(num1, num2);
                wprintf(L"结果: %.2lf * %.2lf = %.2lf\n\n", num1, num2, result);
                break;
            case '/':
                wprintf(L"请输入两个数(空格分隔): ");
                if(scanf("%lf %lf", &num1, &num2) != 2) {
                    wprintf(L"输入格式错误,请输入两个数。\n");
                    while(getchar() != '\n');
                    break;
                }
                if(num2 == 0) {
                    wprintf(L"错误: 除数不能为零。\n\n");
                    break;
                }
                result = divide(num1, num2);
                wprintf(L"结果: %.2lf / %.2lf = %.2lf\n\n", num1, num2, result);
                break;
            case '%':
                wprintf(L"请输入两个整数(空格分隔): ");
                if(scanf("%d %d", &int_num1, &int_num2) != 2) {
                    wprintf(L"输入格式错误,请输入两个整数。\n");
                    while(getchar() != '\n');
                    break;
                }
                if(int_num2 == 0) {
                    wprintf(L"错误: 除数不能为零。\n\n");
                    break;
                }
                mod_result = modulo(int_num1, int_num2);
                wprintf(L"结果: %d %% %d = %d\n\n", int_num1, int_num2, mod_result);
                break;
            case '^':
                wprintf(L"请输入底数和指数(空格分隔): ");
                if(scanf("%lf %lf", &num1, &num2) != 2) {
                    wprintf(L"输入格式错误,请输入两个数。\n");
                    while(getchar() != '\n');
                    break;
                }
                result = power_func(num1, num2);
                wprintf(L"结果: %.2lf ^ %.2lf = %.2lf\n\n", num1, num2, result);
                break;
            case 'q':
            case 'Q':
                wprintf(L"退出计算器。\n");
                choice = 0;
                break;
            default:
                wprintf(L"无效的操作符,请重新输入。\n\n");
        }
    }

    // 使用宽字符输出中文关闭信息
    wprintf(L"===== 计算器已关闭 =====\n");
    return 0;
}

// 加法函数
double add(double a, double b) {
    return a + b;
}

// 减法函数
double subtract(double a, double b) {
    return a - b;
}

// 乘法函数
double multiply(double a, double b) {
    return a * b;
}

// 除法函数
double divide(double a, double b) {
    return a / b;
}

// 取余函数
int modulo(int a, int b) {
    return a % b;
}

// 幂运算函数
double power_func(double a, double b) {
    return pow(a, b);
}

// 显示操作菜单
void displayMenu() {
    wprintf(L"请选择操作:\n");
    wprintf(L"  + : 加法\n");
    wprintf(L"  - : 减法\n");
    wprintf(L"  * : 乘法\n");
    wprintf(L"  / : 除法\n");
    wprintf(L"  %% : 取余\n");
    wprintf(L"  ^ : 幂运算\n");
    wprintf(L"  Q : 退出\n");
}

程序说明
  1. 函数定义
    • 加法、减法、乘法、除法、取余、幂运算:分别定义了对应的函数,简化主程序中的操作。
    • displayMenu:用于显示操作菜单,提示用户选择操作。
  2. 主函数
    • 使用一个while循环,持续接受用户输入,直到用户选择退出(输入Qq)。
    • 通过switch语句,根据用户输入的操作符执行相应的计算。
    • 对每种操作,首先提示用户输入操作数,并进行输入格式和合法性检查。
    • 特别注意处理除以零和数组越界等错误,确保程序的健壮性。
    • 每次操作后,输出计算结果,并提供下一步操作的机会。
  3. 错误处理
    • 使用perror函数输出文件操作错误(在本例中未涉及文件操作,但保留以备扩展)。
    • 检查用户输入是否符合预期格式,避免因输入错误导致程序异常。
  4. 用户体验
    • 提供清晰的操作菜单,帮助用户理解可用的操作符。
    • 支持连续计算,用户可以在一次运行中进行多次计算,直到选择退出。
示例运行
===== 简易计算器 =====
请选择操作:
  + : 加法
  - : 减法
  * : 乘法
  / : 除法
  % : 取余
  ^ : 幂运算
  Q : 退出
请输入操作符: +

请输入两个数(空格分隔): 10 20
结果: 10.00 + 20.00 = 30.00

请选择操作:
  + : 加法
  - : 减法
  * : 乘法
  / : 除法
  % : 取余
  ^ : 幂运算
  Q : 退出
请输入操作符: /

请输入两个数(空格分隔): 15 3
结果: 15.00 / 3.00 = 5.00

请选择操作:
  + : 加法
  - : 减法
  * : 乘法
  / : 除法
  % : 取余
  ^ : 幂运算
  Q : 退出
请输入操作符: %

请输入两个整数(空格分隔): 10 3
结果: 10 % 3 = 1

请选择操作:
  + : 加法
  - : 减法
  * : 乘法
  / : 除法
  % : 取余
  ^ : 幂运算
  Q : 退出
请输入操作符: Q
退出计算器。
===== 计算器已关闭 =====
13.2 文件统计工具

本案例将实现一个文件统计工具,能够统计指定文本文件中的行数、单词数和字符数。该工具将模仿 Unix/Linux 中的wc命令,提供命令行界面,允许用户输入文件路径,并输出统计结果。

程序需求
  1. 功能

    • 统计文件中的行数
    • 统计文件中的单词数
    • 统计文件中的字符数
  2. 用户界面

    • 提示用户输入文件路径
    • 显示统计结果
  3. 错误处理

    • 文件不存在或无法打开
    • 处理大文件时的效率问题
程序代码
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <locale.h>
#include <string.h> // 添加了字符串处理函数头文件

#ifdef _WIN32
    #include <io.h>      // 包含 _setmode 和 _fileno
    #include <fcntl.h>   // 包含 _O_U8TEXT
#endif

// 函数声明
void countFileStatistics(const char *filename, int *lines, int *words, int *chars);

int main() {

    // 在Windows上设置控制台为UTF-8编码
    #ifdef _WIN32
        // 将标准输出设置为UTF-8模式
        if (_setmode(_fileno(stdout), _O_U8TEXT) == -1) {
            perror("设置标准输出为UTF-8失败");
            return 1;
        }
    #endif

    // 设置区域,支持宽字符
    setlocale(LC_ALL, "");

    char filename[256];
    int lines = 0, words = 0, chars = 0;

    // 使用宽字符输出中文标题
    wprintf(L"===== 文件统计工具 =====\n");
    wprintf(L"请输入要统计的文件路径(或输入 'exit' 退出): ");

    while(scanf("%255s", filename) == 1) {
        // 检查是否退出
        if(strcmp(filename, "exit") == 0 || strcmp(filename, "EXIT") == 0) {
            wprintf(L"退出文件统计工具。\n");
            break;
        }

        // 重置统计计数器
        lines = words = chars = 0;

        // 统计文件
        countFileStatistics(filename, &lines, &words, &chars);

        // 输出结果
        // 使用宽字符输出函数wprintf
        wprintf(L"文件: %hs\n", filename); // %hs 用于char*字符串
        wprintf(L"行数: %d\n", lines);
        wprintf(L"单词数: %d\n", words);
        wprintf(L"字符数: %d\n\n", chars);

        wprintf(L"请输入要统计的文件路径(或输入 'exit' 退出): ");
    }

    // 使用宽字符输出中文关闭信息
    wprintf(L"===== 文件统计工具已关闭 =====\n");
    return 0;
}

// 统计文件行数、单词数和字符数
void countFileStatistics(const char *filename, int *lines, int *words, int *chars) {
    FILE *fp = fopen(filename, "r");
    int c;
    int in_word = 0; // 标志是否在单词中

    if(fp == NULL) {
        perror("打开文件失败");
        return;
    }

    while((c = fgetc(fp)) != EOF) {
        (*chars)++;

        if(c == '\n') {
            (*lines)++;
        }

        // 判断是否为单词的开始
        if(isspace(c)) {
            in_word = 0;
        } else {
            if(!in_word) {
                in_word = 1;
                (*words)++;
            }
        }
    }

    fclose(fp);
}

程序说明
  1. 函数定义

    • countFileStatistics

      • 参数:
        • filename:要统计的文件路径。
        • lines:指向行数计数器的指针。
        • words:指向单词数计数器的指针。
        • chars:指向字符数计数器的指针。
      • 功能:
        • 打开指定文件,逐字符读取内容。
        • 统计行数、单词数和字符数。
        • 处理单词的边界(通过空白字符判断单词的开始和结束)。
        • 关闭文件。
  2. 主函数

    • 提示用户输入要统计的文件路径。
    • 支持连续输入多个文件路径,直到用户输入exitEXIT退出。
    • 调用countFileStatistics函数进行统计,并输出结果。
    • 错误处理:
      • 如果文件无法打开,使用perror输出错误信息,并提示用户重新输入。
  3. 错误处理

    • 检查文件是否成功打开,如果失败,输出错误信息。
    • 处理用户输入错误,如文件路径错误或文件不存在。
  4. 用户体验

    • 提供简洁的命令行界面,方便用户输入和查看统计结果。
    • 支持连续统计多个文件,无需重启程序。
示例运行

假设有一个文本文件sample.txt,内容如下:

Hello World!
This is a sample file.
It contains multiple lines,
words, and characters.
===== 文件统计工具 =====
请输入要统计的文件路径(或输入 'exit' 退出): sample.txt
文件: sample.txt
行数: 4
单词数: 11
字符数: 83

请输入要统计的文件路径(或输入 'exit' 退出): nonexistent.txt
打开文件失败: No such file or directory
文件: nonexistent.txt
行数: 0
单词数: 0
字符数: 0

请输入要统计的文件路径(或输入 'exit' 退出): exit
退出文件统计工具。
===== 文件统计工具已关闭 =====
13.3 学生成绩管理系统

本案例将实现一个简单的学生成绩管理系统,允许用户添加、查看、修改和删除学生记录。学生记录包括学生姓名、学号和成绩。数据将保存在一个二进制文件中,以实现数据的持久化存储。

程序需求
  1. 功能

    • 添加新学生记录
    • 查看所有学生记录
    • 修改现有学生记录
    • 删除学生记录
    • 搜索学生记录(按学号或姓名)
    • 保存数据到文件
    • 从文件加载数据
  2. 数据存储

    • 使用二进制文件存储学生记录,确保数据的完整性和安全性。
  3. 用户界面

    • 提供命令行菜单,允许用户选择操作。
  4. 错误处理

    • 处理文件操作错误
    • 处理输入错误
    • 确保数据一致性
程序代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <locale.h>
#include <wchar.h>   // 宽字符支持

#ifdef _WIN32
    #include <io.h>      // 包含 _setmode 和 _fileno
    #include <fcntl.h>   // 包含 _O_U8TEXT
#endif

// 定义学生结构体
typedef struct {
    char name[50];
    int id;
    float score;
} Student;

// 函数声明
void addStudent(const char *filename);
void viewStudents(const char *filename);
void modifyStudent(const char *filename);
void deleteStudent(const char *filename);
void searchStudent(const char *filename);
void displayMenu();

int main() {

    // 在Windows上设置控制台为UTF-8编码
    #ifdef _WIN32
        // 将标准输出设置为UTF-8模式
        if (_setmode(_fileno(stdout), _O_U8TEXT) == -1) {
            perror("设置标准输出为UTF-8失败");
            return 1;
        }
    #endif

    // 设置区域,支持宽字符
    setlocale(LC_ALL, "");

    int choice;
    const char *filename = "students.dat";

    // 使用宽字符输出中文标题
    wprintf(L"===== 学生成绩管理系统 =====\n");

    while(1) {
        displayMenu();
        wprintf(L"请输入您的选择: ");
        if(scanf("%d", &choice) != 1) {
            wprintf(L"输入无效,请输入数字。\n");
            // 清空输入缓冲区
            while(getchar() != '\n');
            continue;
        }

        switch(choice) {
            case 1:
                addStudent(filename);
                break;
            case 2:
                viewStudents(filename);
                break;
            case 3:
                modifyStudent(filename);
                break;
            case 4:
                deleteStudent(filename);
                break;
            case 5:
                searchStudent(filename);
                break;
            case 6:
                wprintf(L"退出学生成绩管理系统。\n");
                wprintf(L"===== 学生成绩管理系统已关闭 =====\n");
                exit(0);
            default:
                wprintf(L"无效的选择,请重新输入。\n");
        }
    }

    return 0;
}

// 显示菜单
void displayMenu() {
    wprintf(L"\n请选择操作:\n");
    wprintf(L"1. 添加新学生记录\n");
    wprintf(L"2. 查看所有学生记录\n");
    wprintf(L"3. 修改学生记录\n");
    wprintf(L"4. 删除学生记录\n");
    wprintf(L"5. 搜索学生记录\n");
    wprintf(L"6. 退出\n");
}

// 添加新学生记录
void addStudent(const char *filename) {
    FILE *fp = fopen(filename, "ab"); // 以追加二进制模式打开文件
    Student s;

    if(fp == NULL) {
        perror("无法打开文件");
        return;
    }

    wprintf(L"===== 添加新学生 =====\n");
    wprintf(L"请输入学生姓名: ");
    // 使用宽字符输入读取中文姓名
    // 由于结构体中的name是char数组,使用多字节字符输入
    scanf(" %[^\n]", s.name); // 读取包含空格的字符串
    wprintf(L"请输入学生学号: ");
    scanf("%d", &s.id);
    wprintf(L"请输入学生成绩: ");
    scanf("%f", &s.score);

    // 写入学生记录到文件
    fwrite(&s, sizeof(Student), 1, fp);
    wprintf(L"学生记录已添加。\n");

    fclose(fp);
}

// 查看所有学生记录
void viewStudents(const char *filename) {
    FILE *fp = fopen(filename, "rb"); // 以二进制读取模式打开文件
    Student s;
    int count = 0;

    if(fp == NULL) {
        perror("无法打开文件");
        return;
    }

    wprintf(L"===== 所有学生记录 =====\n");

    while(fread(&s, sizeof(Student), 1, fp) == 1) {
        wprintf(L"学生%d:\n", ++count);
        wprintf(L"  姓名: %hs\n", s.name); // %hs 用于输出 char* 字符串
        wprintf(L"  学号: %d\n", s.id);
        wprintf(L"  成绩: %.2f\n\n", s.score);
    }

    if(count == 0) {
        wprintf(L"没有学生记录。\n");
    }

    fclose(fp);
}

// 修改学生记录
void modifyStudent(const char *filename) {
    FILE *fp = fopen(filename, "r+b"); // 以读写二进制模式打开文件
    Student s;
    int target_id;
    int found = 0;

    if(fp == NULL) {
        perror("无法打开文件");
        return;
    }

    wprintf(L"===== 修改学生记录 =====\n");
    wprintf(L"请输入要修改的学生学号: ");
    scanf("%d", &target_id);

    while(fread(&s, sizeof(Student), 1, fp) == 1) {
        if(s.id == target_id) {
            wprintf(L"找到学生: %hs (学号: %d, 成绩: %.2f)\n", s.name, s.id, s.score);
            wprintf(L"请输入新的姓名: ");
            scanf(" %[^\n]", s.name);
            wprintf(L"请输入新的成绩: ");
            scanf("%f", &s.score);

            // 获取当前文件指针的位置
            long pos = ftell(fp);
            // 移动文件指针到当前记录的起始位置
            fseek(fp, pos - sizeof(Student), SEEK_SET);
            // 写入修改后的记录
            fwrite(&s, sizeof(Student), 1, fp);
            wprintf(L"学生记录已更新。\n");
            found = 1;
            break;
        }
    }

    if(!found) {
        wprintf(L"未找到学号为 %d 的学生记录。\n", target_id);
    }

    fclose(fp);
}

// 删除学生记录
void deleteStudent(const char *filename) {
    FILE *fp = fopen(filename, "rb"); // 以二进制读取模式打开原文件
    FILE *temp = fopen("temp.dat", "wb"); // 打开临时文件用于存储删除后的数据
    Student s;
    int target_id;
    int found = 0;

    if(fp == NULL) {
        perror("无法打开原文件");
        return;
    }

    if(temp == NULL) {
        perror("无法创建临时文件");
        fclose(fp);
        return;
    }

    wprintf(L"===== 删除学生记录 =====\n");
    wprintf(L"请输入要删除的学生学号: ");
    scanf("%d", &target_id);

    while(fread(&s, sizeof(Student), 1, fp) == 1) {
        if(s.id == target_id) {
            wprintf(L"删除学生: %hs (学号: %d, 成绩: %.2f)\n", s.name, s.id, s.score);
            found = 1;
            // 不将该记录写入临时文件,实现删除效果
            continue;
        }
        // 写入其他记录到临时文件
        fwrite(&s, sizeof(Student), 1, temp);
    }

    if(!found) {
        wprintf(L"未找到学号为 %d 的学生记录。\n", target_id);
    } else {
        wprintf(L"学生记录已删除。\n");
    }

    fclose(fp);
    fclose(temp);

    // 删除原文件
    if(remove(filename) != 0) {
        perror("删除原文件失败");
        return;
    }

    // 重命名临时文件为原文件名
    if(rename("temp.dat", filename) != 0) {
        perror("重命名临时文件失败");
        return;
    }
}

// 搜索学生记录
void searchStudent(const char *filename) {
    FILE *fp = fopen(filename, "rb"); // 以二进制读取模式打开文件
    Student s;
    int choice;
    int target_id;
    char target_name[50];
    int found = 0;

    if(fp == NULL) {
        perror("无法打开文件");
        return;
    }

    wprintf(L"===== 搜索学生记录 =====\n");
    wprintf(L"选择搜索方式:\n");
    wprintf(L"1. 按学号搜索\n");
    wprintf(L"2. 按姓名搜索\n");
    wprintf(L"请输入选择: ");
    if(scanf("%d", &choice) != 1) {
        wprintf(L"输入无效。\n");
        while(getchar() != '\n');
        fclose(fp);
        return;
    }

    if(choice == 1) {
        wprintf(L"请输入学生学号: ");
        scanf("%d", &target_id);

        while(fread(&s, sizeof(Student), 1, fp) == 1) {
            if(s.id == target_id) {
                wprintf(L"找到学生:\n");
                wprintf(L"  姓名: %hs\n", s.name);
                wprintf(L"  学号: %d\n", s.id);
                wprintf(L"  成绩: %.2f\n", s.score);
                found = 1;
                break;
            }
        }

        if(!found) {
            wprintf(L"未找到学号为 %d 的学生记录。\n", target_id);
        }
    }
    else if(choice == 2) {
        wprintf(L"请输入学生姓名: ");
        scanf(" %[^\n]", target_name);

        while(fread(&s, sizeof(Student), 1, fp) == 1) {
            if(strcmp(s.name, target_name) == 0) {
                wprintf(L"找到学生:\n");
                wprintf(L"  姓名: %hs\n", s.name);
                wprintf(L"  学号: %d\n", s.id);
                wprintf(L"  成绩: %.2f\n", s.score);
                found = 1;
                break;
            }
        }

        if(!found) {
            wprintf(L"未找到姓名为 %hs 的学生记录。\n", target_name);
        }
    }
    else {
        wprintf(L"无效的选择。\n");
    }

    fclose(fp);
}

程序说明
  1. 结构体定义

    • Student:包含学生的姓名(字符串)、学号(整数)和成绩(浮点数)。
  2. 函数定义

    • addStudent

      • 打开文件以追加二进制模式,添加新学生记录。
      • 提示用户输入学生姓名、学号和成绩。
      • 将新记录写入文件。
    • viewStudents

      • 打开文件以二进制读取模式,遍历并显示所有学生记录。
      • 如果文件为空,提示无记录。
    • modifyStudent

      • 打开文件以读写二进制模式。
      • 提示用户输入要修改的学生学号。
      • 遍历文件,查找匹配的记录。
      • 提示用户输入新的姓名和成绩,并更新记录。
    • deleteStudent

      • 打开原文件以二进制读取模式,打开临时文件以二进制写入模式。
      • 提示用户输入要删除的学生学号。
      • 遍历原文件,复制非目标记录到临时文件,实现删除效果。
      • 关闭文件后,删除原文件,重命名临时文件为原文件名。
    • searchStudent

      • 打开文件以二进制读取模式。
      • 提供按学号或按姓名搜索的选项。
      • 根据用户选择,提示输入相应的搜索关键字,并遍历文件查找匹配的记录。
  3. 主函数

    • 显示菜单,提示用户选择操作。
    • 根据用户输入,调用相应的函数执行操作。
    • 支持连续操作,直到用户选择退出。
  4. 错误处理

    • 检查文件是否成功打开,如果失败,输出错误信息。
    • 处理用户输入错误,如输入格式不正确。
    • 确保文件操作的正确性,如修改和删除操作确保目标记录存在。
  5. 数据持久化

    • 使用二进制文件students.dat存储学生记录,确保数据在程序运行结束后依然存在。
示例运行
===== 学生成绩管理系统 =====

请选择操作:
1. 添加新学生记录
2. 查看所有学生记录
3. 修改学生记录
4. 删除学生记录
5. 搜索学生记录
6. 退出
请输入您的选择: 1
===== 添加新学生 =====
请输入学生姓名: 张三
请输入学生学号: 1001
请输入学生成绩: 85.5
学生记录已添加。

请选择操作:
1. 添加新学生记录
2. 查看所有学生记录
3. 修改学生记录
4. 删除学生记录
5. 搜索学生记录
6. 退出
请输入您的选择: 1
===== 添加新学生 =====
请输入学生姓名: 李四
请输入学生学号: 1002
请输入学生成绩: 90.0
学生记录已添加。

请选择操作:
1. 添加新学生记录
2. 查看所有学生记录
3. 修改学生记录
4. 删除学生记录
5. 搜索学生记录
6. 退出
请输入您的选择: 2
===== 所有学生记录 =====
学生1:
  姓名: 张三
  学号: 1001
  成绩: 85.50

学生2:
  姓名: 李四
  学号: 1002
  成绩: 90.00

请选择操作:
1. 添加新学生记录
2. 查看所有学生记录
3. 修改学生记录
4. 删除学生记录
5. 搜索学生记录
6. 退出
请输入您的选择: 3
===== 修改学生记录 =====
请输入要修改的学生学号: 1001
找到学生: 张三 (学号: 1001, 成绩: 85.50)
请输入新的姓名: 张三丰
请输入新的成绩: 88.0
学生记录已更新。

请选择操作:
1. 添加新学生记录
2. 查看所有学生记录
3. 修改学生记录
4. 删除学生记录
5. 搜索学生记录
6. 退出
请输入您的选择: 2
===== 所有学生记录 =====
学生1:
  姓名: 张三丰
  学号: 1001
  成绩: 88.00

学生2:
  姓名: 李四
  学号: 1002
  成绩: 90.00

请选择操作:
1. 添加新学生记录
2. 查看所有学生记录
3. 修改学生记录
4. 删除学生记录
5. 搜索学生记录
6. 退出
请输入您的选择: 4
===== 删除学生记录 =====
请输入要删除的学生学号: 1002
删除学生: 李四 (学号: 1002, 成绩: 90.00)
学生记录已删除。

请选择操作:
1. 添加新学生记录
2. 查看所有学生记录
3. 修改学生记录
4. 删除学生记录
5. 搜索学生记录
6. 退出
请输入您的选择: 2
===== 所有学生记录 =====
学生1:
  姓名: 张三丰
  学号: 1001
  成绩: 88.00

请选择操作:
1. 添加新学生记录
2. 查看所有学生记录
3. 修改学生记录
4. 删除学生记录
5. 搜索学生记录
6. 退出
请输入您的选择: 5
===== 搜索学生记录 =====
选择搜索方式:
1. 按学号搜索
2. 按姓名搜索
请输入选择: 1
请输入学生学号: 1001
找到学生:
  姓名: 张三丰
  学号: 1001
  成绩: 88.00

请选择操作:
1. 添加新学生记录
2. 查看所有学生记录
3. 修改学生记录
4. 删除学生记录
5. 搜索学生记录
6. 退出
请输入您的选择: 6
退出学生成绩管理系统。
13.4 数据排序与查找

本案例将实现一个数据排序与查找程序,允许用户输入一组整数,然后选择不同的排序算法对数据进行排序,并提供查找功能。程序将实现以下功能:

  1. 排序算法

    • 冒泡排序
    • 选择排序
    • 插入排序
    • 快速排序
    • 归并排序
  2. 查找算法

    • 线性查找
    • 二分查找
  3. 用户界面

    • 提示用户输入数据
    • 提供排序和查找的选项
    • 显示排序后的数据和查找结果
  4. 错误处理

    • 处理输入错误
    • 确保数据有效性
程序代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <locale.h>
#include <wchar.h>   // 宽字符支持

#ifdef _WIN32
#include <io.h>      // 包含 _setmode 和 _fileno
#include <fcntl.h>   // 包含 _O_U8TEXT
#endif

// 函数声明
void bubbleSort(int arr[], int n);
void selectionSort(int arr[], int n);
void insertionSort(int arr[], int n);
void quickSort(int arr[], int low, int high);
int partition(int arr[], int low, int high);
void mergeSort(int arr[], int left, int right);
void merge(int arr[], int left, int mid, int right);
void displayArray(int arr[], int n);
int linearSearch(int arr[], int n, int target);
int binarySearch(int arr[], int n, int target);
int compare(const void *a, const void *b);
void displayMenu();

int main() {
#ifdef _WIN32
    // 设置标准输出为 UTF-8 模式
    if (_setmode(_fileno(stdout), _O_U8TEXT) == -1) {
        perror("设置标准输出为 UTF-8 失败");
        return 1;
    }
#endif

    setlocale(LC_ALL, "");  // 设置区域支持宽字符

    int n, choice, sort_choice, search_choice, target;
    int *arr = NULL;
    int sorted = 0; // 标志数组是否已排序

    wprintf(L"===== 数据排序与查找工具 =====\n");
    wprintf(L"请输入要输入的整数个数: ");

    if (wscanf(L"%d", &n) != 1 || n <= 0) {
        wprintf(L"输入无效,请输入一个正整数。\n");
        return 1;
    }

    // 动态分配内存
    arr = (int *)malloc(n * sizeof(int));
    if (arr == NULL) {
        wprintf(L"内存分配失败。\n");
        return 1;
    }

    wprintf(L"请输入 %d 个整数(空格分隔): ", n);
    for (int i = 0; i < n; i++) {
        if (wscanf(L"%d", &arr[i]) != 1) {
            wprintf(L"输入无效,请输入整数。\n");
            free(arr);
            return 1;
        }
    }

    while (1) {
        displayMenu();
        wprintf(L"请输入您的选择: ");
        if (wscanf(L"%d", &choice) != 1) {
            wprintf(L"输入无效,请输入数字。\n");
            while (getwchar() != L'\n');  // 清理缓冲区
            continue;
        }

        switch (choice) {
            case 1:  // 数据排序
                wprintf(L"选择排序算法:\n");
                wprintf(L"1. 冒泡排序\n");
                wprintf(L"2. 选择排序\n");
                wprintf(L"3. 插入排序\n");
                wprintf(L"4. 快速排序\n");
                wprintf(L"5. 归并排序\n");
                wprintf(L"请输入排序算法的选择: ");
                if (wscanf(L"%d", &sort_choice) != 1) {
                    wprintf(L"输入无效。\n");
                    while (getwchar() != L'\n');  // 清理缓冲区
                    break;
                }

                switch (sort_choice) {
                    case 1:
                        bubbleSort(arr, n);
                        wprintf(L"使用冒泡排序后的数组:\n");
                        displayArray(arr, n);
                        sorted = 1;
                        break;
                    case 2:
                        selectionSort(arr, n);
                        wprintf(L"使用选择排序后的数组:\n");
                        displayArray(arr, n);
                        sorted = 1;
                        break;
                    case 3:
                        insertionSort(arr, n);
                        wprintf(L"使用插入排序后的数组:\n");
                        displayArray(arr, n);
                        sorted = 1;
                        break;
                    case 4:
                        quickSort(arr, 0, n - 1);
                        wprintf(L"使用快速排序后的数组:\n");
                        displayArray(arr, n);
                        sorted = 1;
                        break;
                    case 5:
                        mergeSort(arr, 0, n - 1);
                        wprintf(L"使用归并排序后的数组:\n");
                        displayArray(arr, n);
                        sorted = 1;
                        break;
                    default:
                        wprintf(L"无效的排序算法选择。\n");
                }
                break;

            case 2:  // 数据查找
                wprintf(L"选择查找算法:\n");
                wprintf(L"1. 线性查找\n");
                wprintf(L"2. 二分查找\n");
                wprintf(L"请输入查找算法的选择: ");
                if (wscanf(L"%d", &search_choice) != 1) {
                    wprintf(L"输入无效。\n");
                    while (getwchar() != L'\n');  // 清理缓冲区
                    break;
                }

                wprintf(L"请输入要查找的整数: ");
                if (wscanf(L"%d", &target) != 1) {
                    wprintf(L"输入无效,请输入整数。\n");
                    while (getwchar() != L'\n');  // 清理缓冲区
                    break;
                }

                if (search_choice == 1) {
                    int index = linearSearch(arr, n, target);
                    if (index != -1) {
                        wprintf(L"找到整数 %d,在数组中的索引为 %d。\n", target, index);
                    } else {
                        wprintf(L"未找到整数 %d。\n", target);
                    }
                } else if (search_choice == 2) {
                    if (!sorted) {
                        wprintf(L"请先对数组进行排序,以使用二分查找。\n");
                        break;
                    }
                    int index = binarySearch(arr, n, target);
                    if (index != -1) {
                        wprintf(L"找到整数 %d,在数组中的索引为 %d。\n", target, index);
                    } else {
                        wprintf(L"未找到整数 %d。\n", target);
                    }
                } else {
                    wprintf(L"无效的查找算法选择。\n");
                }
                break;

            case 3:  // 使用标准库函数 qsort
                qsort(arr, n, sizeof(int), compare);
                wprintf(L"使用 qsort 排序后的数组:\n");
                displayArray(arr, n);
                sorted = 1;
                break;

            case 4:  // 退出程序
                wprintf(L"退出程序。\n");
                free(arr);
                return 0;

            default:
                wprintf(L"无效的选择,请重新输入。\n");
        }
    }

    return 0;
}

// 功能函数实现部分

void displayMenu() {
    wprintf(L"\n===== 操作菜单 =====\n");
    wprintf(L"1. 数据排序\n");
    wprintf(L"2. 数据查找\n");
    wprintf(L"3. 使用 qsort 进行排序\n");
    wprintf(L"4. 退出\n");
}

void bubbleSort(int arr[], int n) {
    for (int i = 0; i < n - 1; i++) {
        for (int j = 0; j < n - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
}

void selectionSort(int arr[], int n) {
    for (int i = 0; i < n - 1; i++) {
        int minIdx = i;
        for (int j = i + 1; j < n; j++) {
            if (arr[j] < arr[minIdx]) {
                minIdx = j;
            }
        }
        int temp = arr[i];
        arr[i] = arr[minIdx];
        arr[minIdx] = temp;
    }
}

void insertionSort(int arr[], int n) {
    for (int i = 1; i < n; i++) {
        int key = arr[i];
        int j = i - 1;
        while (j >= 0 && arr[j] > key) {
            arr[j + 1] = arr[j];
            j--;
        }
        arr[j + 1] = key;
    }
}

int partition(int arr[], int low, int high) {
    int pivot = arr[high];
    int i = low - 1;

    for (int j = low; j < high; j++) {
        if (arr[j] <= pivot) {
            i++;
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
    }
    int temp = arr[i + 1];
    arr[i + 1] = arr[high];
    arr[high] = temp;
    return i + 1;
}

void quickSort(int arr[], int low, int high) {
    if (low < high) {
        int pi = partition(arr, low, high);
        quickSort(arr, low, pi - 1);
        quickSort(arr, pi + 1, high);
    }
}

void merge(int arr[], int left, int mid, int right) {
    int n1 = mid - left + 1;
    int n2 = right - mid;

    int *L = malloc(n1 * sizeof(int));
    int *R = malloc(n2 * sizeof(int));

    for (int i = 0; i < n1; i++)
        L[i] = arr[left + i];
    for (int j = 0; j < n2; j++)
        R[j] = arr[mid + 1 + j];

    int i = 0, j = 0, k = left;
    while (i < n1 && j < n2) {
        if (L[i] <= R[j]) {
            arr[k] = L[i];
            i++;
        } else {
            arr[k] = R[j];
            j++;
        }
        k++;
    }

    while (i < n1) {
        arr[k] = L[i];
        i++;
        k++;
    }

    while (j < n2) {
        arr[k] = R[j];
        j++;
        k++;
    }

    free(L);
    free(R);
}

void mergeSort(int arr[], int left, int right) {
    if (left < right) {
        int mid = left + (right - left) / 2;
        mergeSort(arr, left, mid);
        mergeSort(arr, mid + 1, right);
        merge(arr, left, mid, right);
    }
}

void displayArray(int arr[], int n) {
    wprintf(L"数组内容: ");
    for (int i = 0; i < n; i++) {
        wprintf(L"%d ", arr[i]);
    }
    wprintf(L"\n");
}

int linearSearch(int arr[], int n, int target) {
    for (int i = 0; i < n; i++) {
        if (arr[i] == target)
            return i;
    }
    return -1;
}

int binarySearch(int arr[], int n, int target) {
    int left = 0, right = n - 1;
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (arr[mid] == target)
            return mid;
        else if (arr[mid] < target)
            left = mid + 1;
        else
            right = mid - 1;
    }
    return -1;
}

int compare(const void *a, const void *b) {
    return (*(int *)a - *(int *)b);
}

程序说明
  1. 函数定义

    • 排序算法

      • bubbleSort:实现冒泡排序,通过多次交换相邻逆序对,逐步将最大元素“冒”到数组末端。
      • selectionSort:实现选择排序,每次选择未排序部分的最小元素,放到已排序部分的末尾。
      • insertionSort:实现插入排序,通过将未排序元素插入到已排序部分的正确位置。
      • quickSort:实现快速排序,选择基准元素,将数组分为左右两部分,递归排序。
      • mergeSort:实现归并排序,将数组分割成更小的部分,递归排序后合并。
    • 查找算法

      • linearSearch:实现线性查找,从头到尾依次比较,找到目标元素返回其索引。
      • binarySearch:实现二分查找,要求数组已排序,通过逐步缩小搜索范围找到目标元素。
    • 辅助函数

      • displayArray:打印数组内容。
      • compare:用于qsort的比较函数。
  2. 主函数

    • 提示用户输入要排序的整数个数,并动态分配内存存储数据。
    • 提示用户输入数据,并存储到数组中。
    • 进入操作循环,提供排序和查找的选项:
      • 排序:用户选择排序算法,执行相应的排序,并显示排序后的数组。
      • 查找:用户选择查找算法,输入目标值,执行查找,并显示结果。注意,二分查找要求数组已排序。
      • 使用 qsort:调用标准库函数qsort对数组进行排序,展示其简便性。
      • 退出:释放动态分配的内存,退出程序。
    • 错误处理:
      • 检查用户输入的有效性。
      • 确保动态内存分配成功。
  3. 内存管理

    • 使用malloc动态分配内存存储用户输入的数据。
    • 在程序结束前,通过free释放内存,避免内存泄漏。
  4. 用户体验

    • 提供清晰的操作菜单,方便用户选择所需的功能。
    • 提供详细的操作反馈,如排序后的数组内容和查找结果。
    • 支持多次操作,直到用户选择退出。
示例运行
===== 数据排序与查找工具 =====
请输入要输入的整数个数: 8
请输入 8 个整数(空格分隔): 34 7 23 32 5 62 32 5

===== 操作菜单 =====
1. 数据排序
2. 数据查找
3. 使用 qsort 进行排序
4. 退出
请输入您的选择: 1

选择排序算法:
1. 冒泡排序
2. 选择排序
3. 插入排序
4. 快速排序
5. 归并排序
请输入排序算法的选择: 4

===== 快速排序 =====
使用快速排序后的数组:
数组内容: 5 5 7 23 32 32 34 62

===== 操作菜单 =====
1. 数据排序
2. 数据查找
3. 使用 qsort 进行排序
4. 退出
请输入您的选择: 2

选择查找算法:
1. 线性查找
2. 二分查找
请输入查找算法的选择: 2
请输入要查找的整数: 23
找到整数 23,在数组中的索引为 3。

===== 操作菜单 =====
1. 数据排序
2. 数据查找
3. 使用 qsort 进行排序
4. 退出
请输入您的选择: 3

使用标准库函数 qsort 进行排序。
使用 qsort 排序后的数组:
数组内容: 5 5 7 23 32 32 34 62

===== 操作菜单 =====
1. 数据排序
2. 数据查找
3. 使用 qsort 进行排序
4. 退出
请输入您的选择: 4
退出