200字
C++实践:班级随机点名软件
2025-11-21
2025-12-20

好久没更新了,今天有点能写的就来写一下吧~


起因:曹老师让我们在班上的电脑上搞一个随机点名,我们几个都对这个比较兴奋(有正经的理由能中午在班里玩电脑了),胡、李、余觉得应该用班级优化大师,于是我们下了班级优化大师,然后发现居然要登录,而我们手机都交了(啊啊啊气死我了)

于是我提议用C++写一个……于是就有了下文

程序运行截图:

让我们来实现这个代码吧:

#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <ctime>
#include <cstdlib>
#include <windows.h>
#include <lmaccess.h>
#include <tlhelp32.h>
#include <iomanip>
#include <algorithm>
#include <random>
#include <shlobj.h>

using namespace std;

const HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
string g_selectedName;

// 工具函数:去除字符串前后空格
string trim(const string& str) {
    size_t start = str.find_first_not_of(" \t\n\r");
    size_t end = str.find_last_not_of(" \t\n\r");
    return (start == string::npos || end == string::npos) ? "" : str.substr(start, end - start + 1);
}

// 工具函数:字符串转小写
string toLower(const string& str) {
    string res = str;
    transform(res.begin(), res.end(), res.begin(), ::tolower);
    return res;
}

// UTF8转GBK(带错误处理)
string UTF8ToGBK(const string& utf8Str) {
    if (utf8Str.empty()) return "";

    int wLen = MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), -1, nullptr, 0);
    if (wLen <= 0) {
        cerr << "编码转换错误:UTF8转宽字符失败!" << endl;
        return "";
    }
    wchar_t* wstr = new (nothrow) wchar_t[wLen];
    if (!wstr) {
        cerr << "内存分配失败:无法分配宽字符缓冲区!" << endl;
        return "";
    }
    if (MultiByteToWideChar(CP_UTF8, 0, utf8Str.c_str(), -1, wstr, wLen) <= 0) {
        cerr << "编码转换错误:UTF8转宽字符执行失败!" << endl;
        delete[] wstr;
        return "";
    }

    int gbkLen = WideCharToMultiByte(CP_ACP, 0, wstr, -1, nullptr, 0, nullptr, nullptr);
    if (gbkLen <= 0) {
        cerr << "编码转换错误:宽字符转GBK失败!" << endl;
        delete[] wstr;
        return "";
    }
    char* gbkStr = new (nothrow) char[gbkLen];
    if (!gbkStr) {
        cerr << "内存分配失败:无法分配GBK字符缓冲区!" << endl;
        delete[] wstr;
        return "";
    }
    if (WideCharToMultiByte(CP_ACP, 0, wstr, -1, gbkStr, gbkLen, nullptr, nullptr) <= 0) {
        cerr << "编码转换错误:宽字符转GBK执行失败!" << endl;
        delete[] wstr;
        delete[] gbkStr;
        return "";
    }

    string result(gbkStr);
    delete[] wstr;
    delete[] gbkStr;
    return result;
}

// 检查目录是否存在,不存在则提示创建
bool checkDirectory(const string& dirPath) {
    DWORD attr = GetFileAttributesA(dirPath.c_str());
    if (attr == INVALID_FILE_ATTRIBUTES) {
        cout << "??  目录不存在:" << dirPath << endl;
        cout << "是否创建该目录?(y/n):";
        char choice;
        cin >> choice;
        cin.ignore(numeric_limits<streamsize>::max(), '\n');
        if (choice == 'y' || choice == 'Y') {
            if (CreateDirectoryA(dirPath.c_str(), nullptr)) {
                cout << "? 目录创建成功!" << endl;
                return true;
            } else {
                cerr << "? 目录创建失败!错误码:" << GetLastError() << endl;
                return false;
            }
        }
        return false;
    }
    return (attr & FILE_ATTRIBUTE_DIRECTORY) != 0;
}

// 获取用户文档目录(备选名单路径)
string getUserDocumentsPath() {
    char path[MAX_PATH];
    if (SHGetFolderPathA(nullptr, CSIDL_PERSONAL, nullptr, 0, path) == S_OK) {
        return string(path) + "\\2502sjdm";
    }
    return "";
}

// 读取名单文件(支持备选路径)
bool readUTF8NameList(string& filePath, vector<string>& names) {
    string dirPath = filePath.substr(0, filePath.find_last_of("\\"));
    if (!checkDirectory(dirPath)) {
        string backupDir = getUserDocumentsPath();
        if (!backupDir.empty() && checkDirectory(backupDir)) {
            filePath = backupDir + "\\sjdm.txt";
            cout << "??  切换到备选名单路径:" << filePath << endl;
        } else {
            cerr << "? 所有备选路径均不可用!无法读取名单文件。" << endl;
            return false;
        }
    }

    ifstream file(filePath, ios::binary);
    if (!file.is_open()) {
        cerr << "? 无法打开名单文件!" << endl;
        cerr << "路径:" << filePath << endl;
        cerr << "请检查文件是否存在,或是否有读取权限。" << endl;
        return false;
    }

    unsigned char bom[3] = {0};
    if (file.read((char*)bom, 3) && !(bom[0] == 0xEF && bom[1] == 0xBB && bom[2] == 0xBF)) {
        file.seekg(0, ios::beg);
    }

    string utf8Content((istreambuf_iterator<char>(file)), istreambuf_iterator<char>());
    file.close();

    size_t start = 0;
    size_t end = utf8Content.find_first_of("\r\n");
    while (end != string::npos) {
        string name = trim(utf8Content.substr(start, end - start));
        if (!name.empty()) {
            string gbkName = UTF8ToGBK(name);
            if (!gbkName.empty()) names.push_back(gbkName);
        }
        start = (utf8Content[end] == '\r' && (end + 1) < utf8Content.size() && utf8Content[end + 1] == '\n') 
                ? end + 2 : end + 1;
        end = utf8Content.find_first_of("\r\n", start);
    }
    string lastName = trim(utf8Content.substr(start));
    if (!lastName.empty()) {
        string gbkName = UTF8ToGBK(lastName);
        if (!gbkName.empty()) names.push_back(gbkName);
    }

    if (names.empty()) {
        cerr << "? 名单文件中未找到有效名字!" << endl;
        cerr << "请确保文件编码为UTF-8,且每行有一个非空名字。" << endl;
        return false;
    }

    cout << "? 成功读取 " << names.size() << " 个有效名字" << endl;
    return true;
}

// 随机抽取(mt19937引擎)
string randomSelect(const vector<string>& names) {
    if (names.empty()) return "无有效名字可抽取!";

    static random_device rd;
    static mt19937 gen(rd());
    uniform_int_distribution<> dis(0, names.size() - 1);

    return names[dis(gen)];
}

// 控制台样式设置
void setConsoleStyle(bool bold = true, int textColor = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE) {
    if (hConsole == INVALID_HANDLE_VALUE) return;
    WORD attributes = textColor;
    if (bold) attributes |= FOREGROUND_INTENSITY;
    CONSOLE_SCREEN_BUFFER_INFO csbi;
    if (GetConsoleScreenBufferInfo(hConsole, &csbi) && csbi.wAttributes != attributes) {
        SetConsoleTextAttribute(hConsole, attributes);
    }
}

// 重置控制台样式
void resetConsoleStyle() {
    if (hConsole != INVALID_HANDLE_VALUE) {
        SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
    }
}

// 保存结果(线程安全)
bool saveResult(const string& name) {
    cout << "\n请输入保存路径(例:D:\\点名结果.txt 或 直接回车使用默认路径):";
    string savePath;
    getline(cin, savePath);
    savePath = trim(savePath);

    if (savePath.empty()) {
        char docPath[MAX_PATH];
        if (SHGetFolderPathA(nullptr, CSIDL_PERSONAL, nullptr, 0, docPath) == S_OK) {
            savePath = string(docPath) + "\\点名结果.txt";
        } else {
            savePath = "点名结果.txt";
        }
        cout << "??  使用默认路径:" << savePath << endl;
    }

    time_t now = time(nullptr);
    tm localTime = {0};
    if (localtime_s(&localTime, &now) != 0) {
        cerr << "? 获取系统时间失败!" << endl;
        return false;
    }
    char timeBuf[64];
    strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", &localTime);

    ofstream outFile(savePath, ios::app | ios::out);
    if (!outFile.is_open()) {
        cerr << "? 无法创建/打开文件!" << endl;
        cerr << "请检查路径是否合法,或是否有写入权限。" << endl;
        return false;
    }

    outFile << "[" << timeBuf << "] 点名结果:" << name << endl;
    outFile.close();
    cout << "? 保存成功!文件路径:" << savePath << endl;
    return true;
}

// 软件信息
void showInfo() {
    cout << "\n===== 软件信息 =====\n" << endl;
    cout << "软件名称:韶关市中等职业技术学校信建2502班专用随机点名软件" << endl;
    cout << "版本号:v1.1(优化增强版)" << endl;
    cout << "适配系统:Windows 7/10/11" << endl;
    cout << "名单编码:UTF-8(支持带/不带BOM)" << endl;
    cout << "默认名单路径:C:\\Program Files\\2502sjdm\\sjdm.txt" << endl;
    cout << "备选名单路径:我的文档\\2502sjdm\\sjdm.txt" << endl;
    cout << "核心功能:随机点名、结果保存、多指令交互、自动路径适配" << endl;
    cout << "更新说明:修复已知bug,增强随机性,优化权限兼容性" << endl;
    cout << "====================\n" << endl;
}

// 使用帮助
void showHelp() {
    cout << "\n===== 使用帮助 =====\n" << endl;
    cout << "支持指令列表(不区分大小写,可忽略前后空格):" << endl;
    cout << "  回车/next        - 抽取下一位同学" << endl;
    cout << "  q/quit/exit      - 直接退出程序" << endl;
    cout << "  w/save           - 保存当前点名结果(支持默认路径)" << endl;
    cout << "  :wq/save&exit    - 保存当前结果并退出程序" << endl;
    cout << "  info/about       - 查看软件详细信息" << endl;
    cout << "  help/?           - 查看使用帮助" << endl;
    cout << "  egg/easter       - 触发隐藏彩蛋" << endl;
    cout << "  debug/system     - 查看调试信息和系统信息" << endl;
    cout << "操作提示:Ctrl+C 可复制选中的文本(不会退出程序)" << endl;
    cout << "====================\n" << endl;
}

// 彩蛋功能(仅输出纯文本,无提示、无时间获取)
void showEgg() {
    setConsoleStyle(true, FOREGROUND_BLUE | FOREGROUND_INTENSITY);
    cout << "今夕是何年";  // 无额外换行、装饰,纯文本输出
    resetConsoleStyle();
}

// 获取CPU核心数
DWORD getCpuCoreCount() {
    SYSTEM_INFO sysInfo;
    GetSystemInfo(&sysInfo);
    return sysInfo.dwNumberOfProcessors;
}

// 获取物理内存总量(GB)
double getTotalMemoryGB() {
    MEMORYSTATUSEX memStatus;
    memStatus.dwLength = sizeof(memStatus);
    if (!GlobalMemoryStatusEx(&memStatus)) {
        return 0.0;
    }
    return static_cast<double>(memStatus.ullTotalPhys) / (1024.0 * 1024.0 * 1024.0);
}

// 获取用户名
string getUserName() {
    char userName[UNLEN + 1] = {0};
    DWORD userNameLen = UNLEN + 1;
    if (GetUserNameA(userName, &userNameLen)) {
        return string(userName);
    }
    return "未知用户";
}

// 获取Windows版本
string getWindowsVersion() {
    OSVERSIONINFOEX osvi;
    ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
    osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
    
    if (!GetVersionEx((OSVERSIONINFO*)&osvi)) {
        return "Windows 10/11(版本检测失败)";
    }

    if (osvi.dwMajorVersion == 10) {
        if (osvi.dwMinorVersion == 0) {
            string edition = (osvi.wProductType == VER_NT_WORKSTATION) ? "工作站版" : "服务器版";
            if (osvi.dwBuildNumber >= 22000) {
                return "Windows 11 " + edition;
            }
            return "Windows 10 " + edition;
        }
        return "Windows 10(未知子版本)";
    } else if (osvi.dwMajorVersion == 6) {
        if (osvi.dwMinorVersion == 1) return "Windows 7";
        if (osvi.dwMinorVersion == 2) return "Windows 8";
        if (osvi.dwMinorVersion == 3) return "Windows 8.1";
    }
    return "Windows " + to_string(osvi.dwMajorVersion) + "." + to_string(osvi.dwMinorVersion);
}

// Debug信息
void showDebugInfo(const vector<string>& names, const string& filePath) {
    cout << "\n===== DEBUG 信息 =====\n" << endl;
    cout << "【程序调试信息】" << endl;
    cout << "名单文件路径:" << filePath << endl;
    cout << "读取名单数量:" << names.size() << endl;
    cout << "编码转换模式:UTF-8 → GBK(CP_ACP)" << endl;
    cout << "控制台输入编码:" << GetConsoleCP() << "(期望CP_ACP)" << endl;
    cout << "控制台输出编码:" << GetConsoleOutputCP() << "(期望CP_ACP)" << endl;
    cout << "当前选中名字:" << (g_selectedName.empty() ? "无" : g_selectedName) << endl;

    cout << "\n【系统信息】" << endl;
    cout << "操作系统:" << getWindowsVersion() << endl;
    cout << "登录用户名:" << getUserName() << endl;
    cout << "CPU核心数:" << getCpuCoreCount() << " 核" << endl;
    cout << "物理内存总量:" << fixed << setprecision(2) << getTotalMemoryGB() << " GB" << endl;
    cout << "====================\n" << endl;
}

int main() {
    SetConsoleOutputCP(CP_ACP);
    SetConsoleCP(CP_ACP);
    SetConsoleTitleA("韶关市中等职业技术学校信建2502班专用随机点名软件 v0.0.1");

    string filePath = "C:\\Program Files\\2502sjdm\\sjdm.txt";
    vector<string> nameList;

    if (!readUTF8NameList(filePath, nameList)) {
        cout << "\n按任意键退出程序...";
        system("pause>nul");
        return 1;
    }

    system("cls");
    cout << "===== 韶关市中等职业技术学校信建2502班专用随机点名软件 v0.0.1 =====\n" << endl;
    showHelp();

    string input;
    while (true) {
        g_selectedName = randomSelect(nameList);

        cout << "\n========================" << endl;
        setConsoleStyle(true, FOREGROUND_RED | FOREGROUND_INTENSITY);
        cout << "★★★ 本次被选中的是:【 " << g_selectedName << " 】 ★★★" << endl;
        resetConsoleStyle();
        cout << "========================\n" << endl;

        cout << "请输入操作(输入help查看指令):";
        getline(cin, input);
        string cmd = toLower(trim(input));

        if (cmd == "q" || cmd == "quit" || cmd == "exit") {
            cout << "\n感谢使用,程序即将退出..." << endl;
            Sleep(1000);
            break;
        } else if (cmd == "w" || cmd == "save") {
            saveResult(g_selectedName);
            cout << "\n按任意键继续...";
            system("pause>nul");
            system("cls");
            cout << "===== 韶关市中等职业技术学校信建2502班专用随机点名软件 v0.0.1 =====\n" << endl;
        } else if (cmd == ":wq" || cmd == "save&exit") {
            cout << "\n正在保存当前结果...";
            saveResult(g_selectedName);
            cout << "\n保存完成,程序即将退出..." << endl;
            Sleep(1500);
            break;
        } else if (cmd == "info" || cmd == "about") {
            system("cls");
            cout << "===== 韶关市中等职业技术学校信建2502班专用随机点名软件 v0.0.1 =====\n" << endl;
            showInfo();
        } else if (cmd == "help" || cmd == "?") {
            system("cls");
            cout << "===== 韶关市中等职业技术学校信建2502班专用随机点名软件 v0.0.1 =====\n" << endl;
            showHelp();
        } else if (cmd == "egg" || cmd == "easter") {
            // 触发彩蛋:不清理控制台,直接输出纯文本,无额外提示
            showEgg();
            cout << "\n" << endl;  // 仅添加换行保证界面整洁,无其他提示
        } else if (cmd == "debug" || cmd == "system") {
            system("cls");
            cout << "===== 韶关市中等职业技术学校信建2502班专用随机点名软件 v0.0.1 =====\n" << endl;
            showDebugInfo(nameList, filePath);
        } else if (cmd.empty() || cmd == "next") {
            system("cls");
            cout << "===== 韶关市中等职业技术学校信建2502班专用随机点名软件 v0.0.1 =====\n" << endl;
        } else {
            system("cls");
            cout << "===== 韶关市中等职业技术学校信建2502班专用随机点名软件 v0.0.1 =====\n" << endl;
            cout << "??  输入无效!输入help或?查看支持的指令\n" << endl;
        }
    }

    return 0;
}

突然想起来,老师如果要只抽男女怎么办?(
其次手动动画太丑了啊!

于是

#include <iostream>
#include <vector>
#include <string>
#include <fstream>
#include <algorithm>
#include <thread>
#include <chrono>
#include <random>
#include <iomanip>
#include <limits>
#include <ctime>
#include <cctype>
#include <cstdlib>
#include <Windows.h>

using namespace std;

const string APP_NAME = "韶关市中等职业技术学校信建2502班专用随机点名软件";
const string APP_VERSION = "v0.0.2";
const string MALE_FILE_PATH = "D:\\2502sjdm\\n.txt";
const string FEMALE_FILE_PATH = "D:\\2502sjdm\\s.txt";
const string SAVE_FILE_NAME = "点名结果.txt";
const int ANIMATION_COUNT = 20;
const int ANIMATION_DELAY_MS = 50;
const int NAME_MAX_LENGTH = 8;

// #6cf(浅青色)加粗颜色定义(兼容所有Windows编译器)
const WORD NAME_COLOR = FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY;

class Utils {
public:
    static string trim(const string& str) {
        size_t start = str.find_first_not_of(" \t\n\r");
        size_t end = str.find_last_not_of(" \t\n\r");
        return (start == string::npos || end == string::npos) ? "" : str.substr(start, end - start + 1);
    }

    static string toLower(const string& str) {
        string res = str;
        transform(res.begin(), res.end(), res.begin(), ::tolower);
        return res;
    }

    static void printError(const string& msg) {
        cerr << "? " << msg << "!" << endl;
    }

    static bool exists(const string& path) {
        ifstream file(path);
        return file.good();
    }

    static bool checkDirectory(const string& dirPath) {
        string testPath = dirPath + "\\.test_exists";
        ofstream testFile(testPath);
        bool isDir = testFile.good();
        testFile.close();
        if (isDir) remove(testPath.c_str());
        if (!isDir) cerr << "403 目录不存在或无权限:" << dirPath << endl;
        return isDir;
    }

    static string centerAlign(const string& str, int width) {
        if (str.length() >= width) return str;
        int leftPad = (width - str.length()) / 2;
        int rightPad = width - str.length() - leftPad;
        return string(leftPad, ' ') + str + string(rightPad, ' ');
    }

    // 设置控制台文本颜色
    static void setConsoleColor(WORD color) {
        HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
        SetConsoleTextAttribute(hConsole, color);
    }

    // 恢复控制台默认颜色
    static void resetConsoleColor() {
        HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
        SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
    }
};

class NameListManager {
private:
    vector<string> maleNames;
    vector<string> femaleNames;
    vector<string> allNames;

    bool readNameFile(const string& filePath, vector<string>& names) {
        ifstream file(filePath);
        if (!file.is_open()) {
            Utils::printError("无法打开 " + filePath.substr(filePath.find_last_of("\\") + 1) + " 文件");
            return false;
        }

        string line;
        while (getline(file, line)) {
            string trimmed = Utils::trim(line);
            if (!trimmed.empty()) {
                names.push_back(trimmed);
            }
        }
        file.close();
        return true;
    }

public:
    NameListManager() {}

    bool loadNameList() {
        string dirPath = MALE_FILE_PATH.substr(0, MALE_FILE_PATH.find_last_of("\\"));
        if (!Utils::checkDirectory(dirPath)) return false;

        bool maleOk = readNameFile(MALE_FILE_PATH, maleNames);
        bool femaleOk = readNameFile(FEMALE_FILE_PATH, femaleNames);

        allNames.insert(allNames.end(), maleNames.begin(), maleNames.end());
        allNames.insert(allNames.end(), femaleNames.begin(), femaleNames.end());
        
        if (allNames.empty()) {
            cerr << "404 无有效名字" << endl;
            return false;
        }

        cout << "? 成功读取名单:" << endl;
        cout << "  男生:" << maleNames.size() << " 人" << endl;
        cout << "  女生:" << femaleNames.size() << " 人" << endl;
        cout << "  总计:" << allNames.size() << " 人" << endl;
        return true;
    }

    const vector<string>& getMaleNames() const { return maleNames; }
    const vector<string>& getFemaleNames() const { return femaleNames; }
    const vector<string>& getAllNames() const { return allNames; }
};

class RollCallApp {
public:
    enum class SelectRange { ALL, MALE, FEMALE };

private:
    NameListManager nameManager;
    string selectedName;
    SelectRange currentRange;
    bool hasAnimation;
    static mt19937& getRandomGenerator() {
        static random_device rd;
        static mt19937 gen(rd());
        return gen;
    }

public:
    RollCallApp() : currentRange(SelectRange::ALL), hasAnimation(true) {}

    bool init() {
        if (!nameManager.loadNameList()) return false;
        clearScreen();
        cout << "===== " << APP_NAME << " " << APP_VERSION << " =====\n" << endl;
        showRangeSelection();
        showHelpBrief();
        return true;
    }

    void run() {
        string input;
        while (true) {
            cout << "\n请输入操作:";
            getline(cin, input);
            handleCommand(Utils::trim(input));
        }
    }

private:
    void handleCommand(const string& cmd) {
        if (cmd == "1" || cmd == "2" || cmd == "3") {
            currentRange = (cmd == "1") ? SelectRange::ALL : (cmd == "2") ? SelectRange::MALE : SelectRange::FEMALE;
            showRangeUpdate();
            return;
        }

        if (cmd == "f" || cmd == "F") {
            hasAnimation = !hasAnimation;
            clearScreen();
            showAppHeader();
            showRangeSelection();
            cout << "\n√ 动画状态已切换为:" << (hasAnimation ? "启用" : "关闭") << endl;
            return;
        }

        if (isNonNegativeInt(cmd)) {
            batchSelect(stoi(cmd));
            return;
        }

        if (cmd.empty() || Utils::toLower(cmd) == "next") {
            singleSelect();
            return;
        }

        string lowerCmd = Utils::toLower(cmd);
        if (lowerCmd == "q" || lowerCmd == "quit" || lowerCmd == "exit") exitApp();
        else if (lowerCmd == "w" || lowerCmd == "save") saveResult();
        else if (lowerCmd == ":wq" || lowerCmd == "save&exit") saveResult(true);
        else if (lowerCmd == "info" || lowerCmd == "about") showInfo();
        else if (lowerCmd == "help" || lowerCmd == "?") showHelpDetail();
        else if (lowerCmd == "egg" || lowerCmd == "easter") showEgg();
        else invalidCommand();
    }

    void singleSelect() {
        clearScreen();
        showAppHeader();

        // 显示固定框架(默认颜色)
        cout << "\n========================" << endl;
        cout << "★★★ 本次被选中的是:【 ";
        
        if (hasAnimation) {
            playSelectionAnimation();
        } else {
            // 无动画:直接显示彩色加粗名字
            selectedName = randomSelectSingle();
            Utils::setConsoleColor(NAME_COLOR);
            cout << Utils::centerAlign(selectedName, NAME_MAX_LENGTH);
            Utils::resetConsoleColor();
            cout << " 】 ★★★" << endl;
        }

        cout << "========================\n" << endl;
        cout << "当前抽取范围:" << getRangeName(currentRange) << " | 动画:" << (hasAnimation ? "启用" : "关闭") << endl;
    }

    void batchSelect(int count) {
        if (count <= 0) {
            cout << "\n??  抽取数量必须为正整数!" << endl;
            return;
        }

        clearScreen();
        showAppHeader();
        const vector<string>& targetNames = getCurrentRangeNames();
        int maxCount = static_cast<int>(targetNames.size());
        int actualCount = min(count, maxCount);

        cout << "\n===== 批量抽取 " << actualCount << " 人(" << getRangeName(currentRange) << ")=====\n" << endl;
        if (targetNames.empty()) cout << "??  当前选择范围无有效名字!" << endl;
        else {
            vector<string> shuffledNames = targetNames;
            shuffle(shuffledNames.begin(), shuffledNames.end(), getRandomGenerator());
            for (int i = 0; i < actualCount; ++i) {
                cout << setw(2) << i + 1 << ". ";
                Utils::setConsoleColor(NAME_COLOR);
                cout << shuffledNames[i];
                Utils::resetConsoleColor();
                cout << endl;
            }
        }
        cout << "\n====================\n" << endl;
    }

    void playSelectionAnimation() {
        const vector<string>& targetNames = getCurrentRangeNames();
        if (targetNames.empty()) {
            selectedName = "当前范围无有效名字!";
            Utils::setConsoleColor(NAME_COLOR);
            cout << Utils::centerAlign(selectedName, NAME_MAX_LENGTH);
            Utils::resetConsoleColor();
            cout << " 】 ★★★" << endl;
            return;
        }

        // 简化动画逻辑:使用\r回车符覆盖名字部分,避免复杂光标操作
        for (int i = 0; i < ANIMATION_COUNT; ++i) {
            string randomName = randomSelectSingle(targetNames);
            
            // 移动到名字起始位置并覆盖显示
            cout << "\r★★★ 本次被选中的是:【 ";
            Utils::setConsoleColor(NAME_COLOR);
            cout << setw(NAME_MAX_LENGTH) << left << Utils::centerAlign(randomName, NAME_MAX_LENGTH);
            Utils::resetConsoleColor();
            cout << " 】";
            cout.flush();
            
            this_thread::sleep_for(chrono::milliseconds(ANIMATION_DELAY_MS));
        }

        // 显示最终选中的名字
        selectedName = randomSelectSingle(targetNames);
        cout << "\r★★★ 本次被选中的是:【 ";
        Utils::setConsoleColor(NAME_COLOR);
        cout << setw(NAME_MAX_LENGTH) << left << Utils::centerAlign(selectedName, NAME_MAX_LENGTH);
        Utils::resetConsoleColor();
        cout << " 】 ★★★" << endl;
    }

    string randomSelectSingle() const {
        const vector<string>& targetNames = getCurrentRangeNames();
        return targetNames.empty() ? "当前范围无有效名字!" : randomSelectSingle(targetNames);
    }

    string randomSelectSingle(const vector<string>& names) const {
        uniform_int_distribution<> dis(0, names.size() - 1);
        return names[dis(getRandomGenerator())];
    }

    const vector<string>& getCurrentRangeNames() const {
        switch (currentRange) {
            case SelectRange::MALE: return nameManager.getMaleNames();
            case SelectRange::FEMALE: return nameManager.getFemaleNames();
            default: return nameManager.getAllNames();
        }
    }

    void saveResult(bool exitAfterSave = false) {
        if (selectedName.empty()) {
            cout << "\n??  暂无抽取结果可保存!" << endl;
            pauseAndContinue();
            return;
        }

        cout << "\n请输入保存路径(回车使用默认路径:" << SAVE_FILE_NAME << "):";
        string savePath = Utils::trim(getInput());
        if (savePath.empty()) {
            savePath = SAVE_FILE_NAME;
            cout << "??  使用默认路径:" << savePath << endl;
        }

        time_t now = time(nullptr);
        tm* localTime = localtime(&now);
        char timeBuf[64];
        strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", localTime);

        ofstream outFile(savePath, ios::app);
        if (!outFile.is_open()) {
            Utils::printError("无法创建/打开保存文件");
            cerr << "路径:" << savePath << endl;
            pauseAndContinue();
            return;
        }

        outFile << "[" << timeBuf << "] 【" << getRangeName(currentRange) << "】 " << selectedName << endl;
        outFile.close();
        cout << "? 保存成功!文件路径:" << savePath << endl;

        exitAfterSave ? exitApp("保存完成,程序即将退出...", 1500) : pauseAndContinue();
    }

    void showRangeSelection() const {
        cout << "===== 选择抽取范围 =====" << endl;
        cout << "  1. 全部(" << nameManager.getAllNames().size() << " 人)" << endl;
        cout << "  2. 男生(" << nameManager.getMaleNames().size() << " 人)" << endl;
        cout << "  3. 女生(" << nameManager.getFemaleNames().size() << " 人)" << endl;
        cout << "====================\n" << endl;
        cout << "当前抽取范围:" << getRangeName(currentRange) << endl;
        cout << "动画状态:" << (hasAnimation ? "启用(输入f关闭)" : "关闭(输入f启用)") << endl;
    }

    void showRangeUpdate() const {
        clearScreen();
        showAppHeader();
        showRangeSelection();
        cout << "\n√ 已切换到抽取范围:" << getRangeName(currentRange) << endl;
    }

    void showHelpBrief() const {
        cout << "\n===== 快速操作 =====\n" << endl;
        cout << "  回车/next - 单次抽取(带动画)" << endl;
        cout << "  数字(如5)- 批量抽取对应人数" << endl;
        cout << "  f - 切换动画开关" << endl;
        cout << "  help/? - 查看详细帮助" << endl;
        cout << "====================\n" << endl;
    }

    void showHelpDetail() const {
        clearScreen();
        showAppHeader();
        cout << "\n===== 详细帮助 =====\n" << endl;
        cout << "【抽取控制】" << endl;
        cout << "  1/2/3            - 选择抽取范围(1=全部,2=男生,3=女生)" << endl;
        cout << "  回车/next        - 单次抽取1人(默认带动画)" << endl;
        cout << "  数字(如10)     - 批量抽取对应人数" << endl;
        cout << "  f                - 切换动画开关(启用/关闭)" << endl;
        cout << "\n【文件操作】" << endl;
        cout << "  w/save           - 保存当前单次抽取结果" << endl;
        cout << "  :wq/save&exit    - 保存结果并退出程序" << endl;
        cout << "\n【其他指令】" << endl;
        cout << "  q/quit/exit      - 直接退出程序" << endl;
        cout << "  info/about       - 查看软件信息" << endl;
        cout << "  help/?           - 查看详细帮助" << endl;
        cout << "====================\n" << endl;
    }

    void showInfo() const {
        clearScreen();
        showAppHeader();
        cout << "\n===== 软件信息 =====\n" << endl;
        cout << "软件名称:" << APP_NAME << endl;
        cout << "版本号:" << APP_VERSION << endl;
        cout << "适用系统:Windows 10" << endl;
        cout << "名单文件位置:" << endl;
        cout << "  男生名单:" << MALE_FILE_PATH << endl;
        cout << "  女生名单:" << FEMALE_FILE_PATH << endl;
        cout << "名单填写:每行写1个姓名即可" << endl;
        cout << "主要功能:" << endl;
        cout << "  - 可选择抽取全部、男生或女生" << endl;
        cout << "  - 支持单次抽取(带动画)和批量抽取" << endl;
        cout << "  - 可保存抽取结果" << endl;
        cout << "====================\n" << endl;
    }

    void showEgg() const {
        clearScreen();
        showAppHeader();
        cout << "\n今夕是何年" << endl;
        cout << "\n====================\n" << endl;
        showRangeSelection();
    }

    void invalidCommand() const {
        clearScreen();
        showAppHeader();
        showRangeSelection();
        cout << "\n??  输入无效!输入help或?查看支持的指令" << endl;
    }

    void exitApp(const string& msg = "感谢使用,程序即将退出...", int delay_ms = 1000) const {
        cout << "\n" << msg << endl;
        this_thread::sleep_for(chrono::milliseconds(delay_ms));
        exit(EXIT_SUCCESS);
    }

    bool isNonNegativeInt(const string& str) const {
        if (str.empty()) return false;
        for (char c : str) if (!isdigit(c)) return false;
        return true;
    }

    string getRangeName(SelectRange range) const {
        return (range == SelectRange::MALE) ? "男生" : (range == SelectRange::FEMALE) ? "女生" : "全部";
    }

    void clearScreen() const {
        system("cls");
    }

    void showAppHeader() const {
        cout << "===== " << APP_NAME << " " << APP_VERSION << " =====\n" << endl;
    }

    string getInput() const {
        string input;
        getline(cin, input);
        return input;
    }

    void pauseAndContinue() const {
        cin.ignore(std::numeric_limits<streamsize>::max(), '\n');
        clearScreen();
        showAppHeader();
        showRangeSelection();
    }
};

int main() {
    RollCallApp app;
    if (!app.init()) {
        return EXIT_FAILURE;
    } else {
        app.run();
    }
    return EXIT_SUCCESS;
}

分析维度

版本 0.0.1 (第一个代码段)

版本 0.0.2 (第二个代码段)

主要升级点分析

1. 架构与设计

面向过程。功能集中在一个主文件,通过全局变量和函数实现。

面向对象。引入了 RollCallAppNameListManagerUtils 等类,职责分离清晰。

提升了代码的模块化、可维护性和可扩展性。新架构更易于理解和后续开发。

2. 核心功能

单一列表随机抽取。从一个名单文件读取所有名字。

分类与批量抽取
1. 性别分类:从 n.txt(男)和 s.txt(女)分别读取名单。
2. 范围选择:可指定抽取“全部”、“男生”或“女生”。
3. 批量抽取:输入数字可一次性抽取多人。

功能显著增强。支持按性别筛选和批量操作,实用性更强。

3. 文件处理

复杂路径与编码处理
- 尝试从“Program Files”和“我的文档”读取。
- 内置 UTF-8 到 GBK 的编码转换。

路径简化,编码隐式处理
- 固定从 D:\2502sjdm\ 读取。
- 依赖系统控制台编码,无显式转换。

去除了复杂的编码转换逻辑,降低了出错风险,但牺牲了路径灵活性。

4. 代码质量与特性

基础功能。包含随机、保存、彩蛋、调试信息。

优化与增强
- 动画效果:抽取时有名字快速切换的动画。
- 常量与配置:用常量定义路径、颜色等。
- 错误处理:有更明确的错误提示(如“403 目录不存在”)。
- 用户界面:提示信息更友好,如显示人数统计。

用户体验和代码健壮性得到优化。动画和更好的提示提升了交互体验。

一编译,能跑,但有编译报错,我看不得任何报错,必须改

#include <iostream>
#include <vector>
#include <string>
#include <fstream>
#include <algorithm>
#include <thread>
#include <chrono>
#include <random>
#include <iomanip>
#include <limits>
#include <ctime>
#include <cctype>
#include <cstdlib>
#include <Windows.h>

using namespace std;

const string APP_NAME = "韶关市中等职业技术学校信建2502班专用随机点名软件";
const string APP_VERSION = "v0.0.3";
const string MALE_FILE_PATH = "D:\\2502sjdm\\n.txt";
const string FEMALE_FILE_PATH = "D:\\2502sjdm\\s.txt";
const string SAVE_FILE_NAME = "点名结果.txt";
const int ANIMATION_COUNT = 20;
const int ANIMATION_DELAY_MS = 50;
const size_t NAME_MAX_LENGTH = 8;  // 改为 size_t 类型

// #6cf(浅青色)加粗颜色定义
const WORD NAME_COLOR = FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY;

class Utils {
public:
    static string trim(const string& str) {
        size_t start = str.find_first_not_of(" \t\n\r");
        size_t end = str.find_last_not_of(" \t\n\r");
        return (start == string::npos || end == string::npos) ? "" : str.substr(start, end - start + 1);
    }

    static string toLower(const string& str) {
        string res = str;
        transform(res.begin(), res.end(), res.begin(), ::tolower);
        return res;
    }

    static void printError(const string& msg) {
        cerr << "? " << msg << "!" << endl;
    }

    static bool exists(const string& path) {
        ifstream file(path);
        return file.good();
    }

    static bool checkDirectory(const string& dirPath) {
        string testPath = dirPath + "\\.test_exists";
        ofstream testFile(testPath);
        bool isDir = testFile.good();
        testFile.close();
        if (isDir) remove(testPath.c_str());
        if (!isDir) cerr << "403 目录不存在或无权限:" << dirPath << endl;
        return isDir;
    }

    static string centerAlign(const string& str, size_t width) {  // 参数改为 size_t
        size_t strLength = str.length();
        if (strLength >= width) return str;
        size_t leftPad = (width - strLength) / 2;
        size_t rightPad = width - strLength - leftPad;
        return string(leftPad, ' ') + str + string(rightPad, ' ');
    }

    // 设置控制台文本颜色
    static void setConsoleColor(WORD color) {
        HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
        SetConsoleTextAttribute(hConsole, color);
    }

    // 恢复控制台默认颜色
    static void resetConsoleColor() {
        HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
        SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
    }
};

class NameListManager {
private:
    vector<string> maleNames;
    vector<string> femaleNames;
    vector<string> allNames;

    bool readNameFile(const string& filePath, vector<string>& names) {
        ifstream file(filePath);
        if (!file.is_open()) {
            Utils::printError("无法打开 " + filePath.substr(filePath.find_last_of("\\") + 1) + " 文件");
            return false;
        }

        string line;
        while (getline(file, line)) {
            string trimmed = Utils::trim(line);
            if (!trimmed.empty()) {
                names.push_back(trimmed);
            }
        }
        file.close();
        return true;
    }

public:
    NameListManager() {}

    bool loadNameList() {
        string dirPath = MALE_FILE_PATH.substr(0, MALE_FILE_PATH.find_last_of("\\"));
        if (!Utils::checkDirectory(dirPath)) return false;

        bool maleOk = readNameFile(MALE_FILE_PATH, maleNames);
        bool femaleOk = readNameFile(FEMALE_FILE_PATH, femaleNames);

        allNames.insert(allNames.end(), maleNames.begin(), maleNames.end());
        allNames.insert(allNames.end(), femaleNames.begin(), femaleNames.end());
        
        if (allNames.empty()) {
            cerr << "404 无有效名字" << endl;
            return false;
        }

        cout << "? 成功读取名单:" << endl;
        cout << "  男生:" << maleNames.size() << " 人" << endl;
        cout << "  女生:" << femaleNames.size() << " 人" << endl;
        cout << "  总计:" << allNames.size() << " 人" << endl;
        return true;
    }

    const vector<string>& getMaleNames() const { return maleNames; }
    const vector<string>& getFemaleNames() const { return femaleNames; }
    const vector<string>& getAllNames() const { return allNames; }
};

class RollCallApp {
public:
    enum class SelectRange { ALL, MALE, FEMALE };

private:
    NameListManager nameManager;
    string selectedName;
    SelectRange currentRange;
    bool hasAnimation;
    static mt19937& getRandomGenerator() {
        static random_device rd;
        static mt19937 gen(rd());
        return gen;
    }

public:
    RollCallApp() : currentRange(SelectRange::ALL), hasAnimation(true) {}

    bool init() {
        if (!nameManager.loadNameList()) return false;
        clearScreen();
        cout << "===== " << APP_NAME << " " << APP_VERSION << " =====\n" << endl;
        showRangeSelection();
        showHelpBrief();
        return true;
    }

    void run() {
        string input;
        while (true) {
            cout << "\n请输入操作:";
            getline(cin, input);
            handleCommand(Utils::trim(input));
        }
    }

private:
    void handleCommand(const string& cmd) {
        if (cmd == "1" || cmd == "2" || cmd == "3") {
            currentRange = (cmd == "1") ? SelectRange::ALL : (cmd == "2") ? SelectRange::MALE : SelectRange::FEMALE;
            showRangeUpdate();
            return;
        }

        if (cmd == "f" || cmd == "F") {
            hasAnimation = !hasAnimation;
            clearScreen();
            showAppHeader();
            showRangeSelection();
            cout << "\n√ 动画状态已切换为:" << (hasAnimation ? "启用" : "关闭") << endl;
            return;
        }

        if (isNonNegativeInt(cmd)) {
            batchSelect(stoi(cmd));
            return;
        }

        if (cmd.empty() || Utils::toLower(cmd) == "next") {
            singleSelect();
            return;
        }

        string lowerCmd = Utils::toLower(cmd);
        if (lowerCmd == "q" || lowerCmd == "quit" || lowerCmd == "exit") exitApp();
        else if (lowerCmd == "w" || lowerCmd == "save") saveResult();
        else if (lowerCmd == ":wq" || lowerCmd == "save&exit") saveResult(true);
        else if (lowerCmd == "info" || lowerCmd == "about") showInfo();
        else if (lowerCmd == "help" || lowerCmd == "?") showHelpDetail();
        else invalidCommand();
    }

    void singleSelect() {
        clearScreen();
        showAppHeader();

        // 显示固定框架(默认颜色)
        cout << "\n========================" << endl;
        cout << "★★★ 本次被选中的是:【 ";
        
        if (hasAnimation) {
            playSelectionAnimation();
        } else {
            // 无动画:直接显示彩色加粗名字
            selectedName = randomSelectSingle();
            Utils::setConsoleColor(NAME_COLOR);
            cout << Utils::centerAlign(selectedName, NAME_MAX_LENGTH);
            Utils::resetConsoleColor();
            cout << " 】 ★★★" << endl;
        }

        cout << "========================\n" << endl;
        cout << "当前抽取范围:" << getRangeName(currentRange) << " | 动画:" << (hasAnimation ? "启用" : "关闭") << endl;
    }

    void batchSelect(int count) {
        if (count <= 0) {
            cout << "\n??  抽取数量必须为正整数!" << endl;
            return;
        }

        clearScreen();
        showAppHeader();
        const vector<string>& targetNames = getCurrentRangeNames();
        int maxCount = static_cast<int>(targetNames.size());
        int actualCount = min(count, maxCount);

        cout << "\n===== 批量抽取 " << actualCount << " 人(" << getRangeName(currentRange) << ")=====\n" << endl;
        if (targetNames.empty()) cout << "??  当前选择范围无有效名字!" << endl;
        else {
            vector<string> shuffledNames = targetNames;
            shuffle(shuffledNames.begin(), shuffledNames.end(), getRandomGenerator());
            for (int i = 0; i < actualCount; ++i) {
                cout << setw(2) << i + 1 << ". ";
                Utils::setConsoleColor(NAME_COLOR);
                cout << shuffledNames[i];
                Utils::resetConsoleColor();
                cout << endl;
            }
        }
        cout << "\n====================\n" << endl;
    }

    void playSelectionAnimation() {
        const vector<string>& targetNames = getCurrentRangeNames();
        if (targetNames.empty()) {
            selectedName = "当前范围无有效名字!";
            Utils::setConsoleColor(NAME_COLOR);
            cout << Utils::centerAlign(selectedName, NAME_MAX_LENGTH);
            Utils::resetConsoleColor();
            cout << " 】 ★★★" << endl;
            return;
        }

        // 简化动画逻辑:使用\r回车符覆盖名字部分
        for (int i = 0; i < ANIMATION_COUNT; ++i) {
            string randomName = randomSelectSingle(targetNames);
            
            // 移动到名字起始位置并覆盖显示
            cout << "\r★★★ 本次被选中的是:【 ";
            Utils::setConsoleColor(NAME_COLOR);
            cout << setw(static_cast<int>(NAME_MAX_LENGTH)) << left << Utils::centerAlign(randomName, NAME_MAX_LENGTH);
            Utils::resetConsoleColor();
            cout << " 】";
            cout.flush();
            
            this_thread::sleep_for(chrono::milliseconds(ANIMATION_DELAY_MS));
        }

        // 显示最终选中的名字
        selectedName = randomSelectSingle(targetNames);
        cout << "\r★★★ 本次被选中的是:【 ";
        Utils::setConsoleColor(NAME_COLOR);
        cout << setw(static_cast<int>(NAME_MAX_LENGTH)) << left << Utils::centerAlign(selectedName, NAME_MAX_LENGTH);
        Utils::resetConsoleColor();
        cout << " 】 ★★★" << endl;
    }

    string randomSelectSingle() const {
        const vector<string>& targetNames = getCurrentRangeNames();
        return targetNames.empty() ? "当前范围无有效名字!" : randomSelectSingle(targetNames);
    }

    string randomSelectSingle(const vector<string>& names) const {
        uniform_int_distribution<> dis(0, static_cast<int>(names.size()) - 1);
        return names[dis(getRandomGenerator())];
    }

    const vector<string>& getCurrentRangeNames() const {
        switch (currentRange) {
            case SelectRange::MALE: return nameManager.getMaleNames();
            case SelectRange::FEMALE: return nameManager.getFemaleNames();
            default: return nameManager.getAllNames();
        }
    }

    void saveResult(bool exitAfterSave = false) {
        if (selectedName.empty()) {
            cout << "\n??  暂无抽取结果可保存!" << endl;
            pauseAndContinue();
            return;
        }

        cout << "\n请输入保存路径(回车使用默认路径:" << SAVE_FILE_NAME << "):";
        string savePath = Utils::trim(getInput());
        if (savePath.empty()) {
            savePath = SAVE_FILE_NAME;
            cout << "??  使用默认路径:" << savePath << endl;
        }

        time_t now = time(nullptr);
        tm* localTime = localtime(&now);
        char timeBuf[64];
        strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", localTime);

        ofstream outFile(savePath, ios::app);
        if (!outFile.is_open()) {
            Utils::printError("无法创建/打开保存文件");
            cerr << "路径:" << savePath << endl;
            pauseAndContinue();
            return;
        }

        outFile << "[" << timeBuf << "] 【" << getRangeName(currentRange) << "】 " << selectedName << endl;
        outFile.close();
        cout << "? 保存成功!文件路径:" << savePath << endl;

        if (exitAfterSave) {
            exitApp("保存完成,程序即将退出...", 1500);
        } else {
            pauseAndContinue();
        }
    }

    void showRangeSelection() const {
        cout << "===== 选择抽取范围 =====" << endl;
        cout << "  1. 全部(" << nameManager.getAllNames().size() << " 人)" << endl;
        cout << "  2. 男生(" << nameManager.getMaleNames().size() << " 人)" << endl;
        cout << "  3. 女生(" << nameManager.getFemaleNames().size() << " 人)" << endl;
        cout << "====================\n" << endl;
        cout << "当前抽取范围:" << getRangeName(currentRange) << endl;
        cout << "动画状态:" << (hasAnimation ? "启用(输入f关闭)" : "关闭(输入f启用)") << endl;
    }

    void showRangeUpdate() const {
        clearScreen();
        showAppHeader();
        showRangeSelection();
        cout << "\n√ 已切换到抽取范围:" << getRangeName(currentRange) << endl;
    }

    void showHelpBrief() const {
        cout << "\n===== 快速操作 =====\n" << endl;
        cout << "  回车/next - 单次抽取(带动画)" << endl;
        cout << "  数字(如5)- 批量抽取对应人数" << endl;
        cout << "  f - 切换动画开关" << endl;
        cout << "  help/? - 查看详细帮助" << endl;
        cout << "====================\n" << endl;
    }

    void showHelpDetail() const {
        clearScreen();
        showAppHeader();
        cout << "\n===== 详细帮助 =====\n" << endl;
        cout << "【抽取控制】" << endl;
        cout << "  1/2/3            - 选择抽取范围(1=全部,2=男生,3=女生)" << endl;
        cout << "  回车/next        - 单次抽取1人(默认带动画)" << endl;
        cout << "  数字(如10)     - 批量抽取对应人数" << endl;
        cout << "  f                - 切换动画开关(启用/关闭)" << endl;
        cout << "\n【文件操作】" << endl;
        cout << "  w/save           - 保存当前单次抽取结果" << endl;
        cout << "  :wq/save&exit    - 保存结果并退出程序" << endl;
        cout << "\n【其他指令】" << endl;
        cout << "  q/quit/exit      - 直接退出程序" << endl;
        cout << "  info/about       - 查看软件信息" << endl;
        cout << "  help/?           - 查看详细帮助" << endl;
        cout << "====================\n" << endl;
    }

    void showInfo() const {
        clearScreen();
        showAppHeader();
        cout << "\n===== 软件信息 =====\n" << endl;
        cout << "软件名称:" << APP_NAME << endl;
        cout << "版本号:" << APP_VERSION << endl;
        cout << "适用系统:Windows 10" << endl;
        cout << "名单文件位置:" << endl;
        cout << "  男生名单:" << MALE_FILE_PATH << endl;
        cout << "  女生名单:" << FEMALE_FILE_PATH << endl;
        cout << "名单填写:每行写1个姓名即可" << endl;
        cout << "主要功能:" << endl;
        cout << "  - 可选择抽取全部、男生或女生" << endl;
        cout << "  - 支持单次抽取(带动画)和批量抽取" << endl;
        cout << "  - 可保存抽取结果" << endl;
        cout << "====================\n" << endl;
    }

    void invalidCommand() const {
        clearScreen();
        showAppHeader();
        showRangeSelection();
        cout << "\n??  输入无效!输入help或?查看支持的指令" << endl;
    }

    void exitApp(const string& msg = "感谢使用,程序即将退出...", int delay_ms = 1000) const {
        cout << "\n" << msg << endl;
        this_thread::sleep_for(chrono::milliseconds(delay_ms));
        exit(EXIT_SUCCESS);
    }

    bool isNonNegativeInt(const string& str) const {
        if (str.empty()) return false;
        for (char c : str) if (!isdigit(c)) return false;
        return true;
    }

    string getRangeName(SelectRange range) const {
        return (range == SelectRange::MALE) ? "男生" : (range == SelectRange::FEMALE) ? "女生" : "全部";
    }

    void clearScreen() const {
        system("cls");
    }

    void showAppHeader() const {
        cout << "===== " << APP_NAME << " " << APP_VERSION << " =====\n" << endl;
    }

    string getInput() const {
        string input;
        getline(cin, input);
        return input;
    }

    void pauseAndContinue() const {
        cin.ignore(std::numeric_limits<streamsize>::max(), '\n');
        clearScreen();
        showAppHeader();
        showRangeSelection();
    }
};

int main() {
    RollCallApp app;
    if (!app.init()) {
        return EXIT_FAILURE;
    } else {
        app.run();
    }
    return EXIT_SUCCESS;
}

变更类别

变更位置

v0.0.2 版本

v0.0.3 版本

升级点分析

1. 版本标识

常量 APP_VERSION

“v0.0.2”

“v0.0.3”

更新了软件版本号。

2. 常量类型优化

常量 NAME_MAX_LENGTH

const int NAME_MAX_LENGTH = 8;

const size_t NAME_MAX_LENGTH = 8;

将与字符串长度相关的常量类型改为 size_t,与标准库函数(如string::length())返回值类型保持一致,提高了类型安全性和代码一致性

3. 函数参数匹配

Utils::centerAlign 方法参数

int width

size_t width

配合上述常量类型的修改,消除了潜在的隐式类型转换警告

4. 显式类型转换

RollCallApp::playSelectionAnimation 方法

setw(NAME_MAX_LENGTH)

setw(static_cast<int>(NAME_MAX_LENGTH))

setw 期望 int 类型参数。将 size_t 类型的常量显式转换为 int避免了编译器的类型不匹配警告

5. 显式类型转换

RollCallApp::randomSelectSingle 方法

dis(0, names.size() - 1)

dis(0, static_cast<int>(names.size()) - 1)

uniform_int_distribution<> 默认使用 int 类型。将 size_t 显式转换为 int,同样是为了消除编译器警告,确保随机数生成的类型安全

6. 功能移除

RollCallApp::handleCommand 方法

包含对 “egg”“easter” 命令的处理分支。

已删除 彩蛋命令的处理分支。

移除了彩蛋功能

这个时候是老师用到的的一个版本,说要给他们班上的电脑也搞一个

得~0.0.4横空出世

#include <iostream>
#include <vector>
#include <string>
#include <fstream>
#include <algorithm>
#include <thread>
#include <chrono>
#include <random>
#include <iomanip>
#include <limits>
#include <ctime>
#include <cctype>
#include <cstdlib>
#include <Windows.h>

using namespace std;

const string APP_NAME = "韶关市中等职业技术学校信建2503班专用随机点名软件";
const string APP_VERSION = "v0.0.4";
const string MALE_FILE_PATH = "D:\\2503sjdm\\n.txt";
const string FEMALE_FILE_PATH = "D:\\2503sjdm\\s.txt";
const string SAVE_FILE_NAME = "点名结果.txt";
const int ANIMATION_COUNT = 20;
const int ANIMATION_DELAY_MS = 50;
const size_t NAME_MAX_LENGTH = 4;
const int BATCH_ITEM_WIDTH = 8;  // 每个名字显示宽度(含空格)
const int MAX_COLUMNS = 6;  // 每行最多显示6个名字

// #6cf(浅青色)加粗颜色定义
const WORD NAME_COLOR = FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY;

class Utils {
public:
    static string trim(const string& str) {
        size_t start = str.find_first_not_of(" \t\n\r");
        size_t end = str.find_last_not_of(" \t\n\r");
        return (start == string::npos || end == string::npos) ? "" : str.substr(start, end - start + 1);
    }

    static string toLower(const string& str) {
        string res = str;
        transform(res.begin(), res.end(), res.begin(), ::tolower);
        return res;
    }

    static bool exists(const string& path) {
        ifstream file(path);
        return file.good();
    }

    static bool checkDirectory(const string& dirPath) {
        string testPath = dirPath + "\\.test_exists";
        ofstream testFile(testPath);
        bool isDir = testFile.good();
        testFile.close();
        if (isDir) remove(testPath.c_str());
        return isDir;
    }

    static string centerAlign(const string& str, size_t width) {
        size_t strLength = str.length();
        if (strLength >= width) return str;
        size_t leftPad = (width - strLength) / 2;
        size_t rightPad = width - strLength - leftPad;
        return string(leftPad, ' ') + str + string(rightPad, ' ');
    }

    // 设置控制台文本颜色
    static void setConsoleColor(WORD color) {
        HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
        SetConsoleTextAttribute(hConsole, color);
    }

    // 恢复控制台默认颜色
    static void resetConsoleColor() {
        HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
        SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
    }

    // 生成指定长度的分隔线
    static string generateLine(size_t length, char ch = '=') {
        return string(length, ch);
    }
};

class NameListManager {
private:
    vector<string> maleNames;
    vector<string> femaleNames;
    vector<string> allNames;

    bool readNameFile(const string& filePath, vector<string>& names) {
        ifstream file(filePath);
        if (!file.is_open()) {
            return false;
        }

        string line;
        while (getline(file, line)) {
            string trimmed = Utils::trim(line);
            if (!trimmed.empty()) {
                names.push_back(trimmed);
            }
        }
        file.close();
        return true;
    }

public:
    NameListManager() {}

    bool loadNameList() {
        string dirPath = MALE_FILE_PATH.substr(0, MALE_FILE_PATH.find_last_of("\\"));
        if (!Utils::checkDirectory(dirPath)) return false;

        bool maleOk = readNameFile(MALE_FILE_PATH, maleNames);
        bool femaleOk = readNameFile(FEMALE_FILE_PATH, femaleNames);

        allNames.insert(allNames.end(), maleNames.begin(), maleNames.end());
        allNames.insert(allNames.end(), femaleNames.begin(), femaleNames.end());
        
        if (allNames.empty()) {
            return false;
        }
        cout << "  男生:" << maleNames.size() << " 人" << endl;
        cout << "  女生:" << femaleNames.size() << " 人" << endl;
        cout << "  总计:" << allNames.size() << " 人" << endl;
        return true;
    }

    const vector<string>& getMaleNames() const { return maleNames; }
    const vector<string>& getFemaleNames() const { return femaleNames; }
    const vector<string>& getAllNames() const { return allNames; }
};

class RollCallApp {
public:
    enum class SelectRange { ALL, MALE, FEMALE };

private:
    NameListManager nameManager;
    string selectedName;
    vector<string> selectedBatchNames;
    SelectRange currentRange;
    bool hasAnimation;
    static mt19937& getRandomGenerator() {
        static random_device rd;
        static mt19937 gen(rd());
        return gen;
    }

public:
    RollCallApp() : currentRange(SelectRange::ALL), hasAnimation(true) {}

    bool init() {
        clearScreen();
        bool loadSuccess = nameManager.loadNameList();
        cout << "===== " << APP_NAME << " " << APP_VERSION << " =====\n" << endl;
        showRangeSelection();
        showHelpBrief();
        return true;
    }

    void run() {
        string input;
        while (true) {
            getline(cin, input);
            handleCommand(Utils::trim(input));
        }
    }

private:
    void handleCommand(const string& cmd) {
        if (cmd == "1" || cmd == "2" || cmd == "3") {
            currentRange = (cmd == "1") ? SelectRange::ALL : (cmd == "2") ? SelectRange::MALE : SelectRange::FEMALE;
            showRangeUpdate();
            return;
        }

        if (cmd == "f" || cmd == "F") {
            hasAnimation = !hasAnimation;
            clearScreen();
            showAppHeader();
            showRangeSelection();
            cout << "\n√ 动画状态已切换为:" << (hasAnimation ? "启用" : "关闭") << endl;
            return;
        }

        // 处理批量抽取:p+数字 格式
        if (cmd.size() >= 2 && (cmd[0] == 'p' || cmd[0] == 'P')) {
            string numStr = cmd.substr(1);
            if (isNonNegativeInt(numStr)) {
                int count = stoi(numStr);
                if (count > 0) {
                    batchSelect(count);
                } else {
                    cout << "\n??  批量抽取数量必须为正整数!" << endl;
                }
                return;
            }
        }

        if (cmd.empty() || Utils::toLower(cmd) == "next") {
            singleSelect();
            return;
        }

        string lowerCmd = Utils::toLower(cmd);
        if (lowerCmd == "q" || lowerCmd == "quit" || lowerCmd == "exit") exit(0);
        else if (lowerCmd == "w" || lowerCmd == "save") saveResult();
        else if (lowerCmd == ":wq" || lowerCmd == "save&exit") saveResult(true);
        else if (lowerCmd == "info" || lowerCmd == "about") showInfo();
        else if (lowerCmd == "help" || lowerCmd == "?") showHelpDetail();
        else invalidCommand();
    }

    void singleSelect() {
        clearScreen();
        showAppHeader();

        // 显示固定框架(默认颜色)
        cout << "\n" << Utils::generateLine(32) << endl;
        cout << "★★★ 本次被选中的是:【 ";
        
        if (hasAnimation) {
            playSingleAnimation();
        } else {
            // 无动画:直接显示彩色加粗名字
            selectedName = randomSelectSingle();
            Utils::setConsoleColor(NAME_COLOR);
            cout << Utils::centerAlign(selectedName, NAME_MAX_LENGTH);
            Utils::resetConsoleColor();
            cout << " 】 ★★★" << endl;
        }

        cout << Utils::generateLine(32) << "\n" << endl;
        cout << "当前抽取范围:" << getRangeName(currentRange) << " | 动画:" << (hasAnimation ? "启用" : "关闭") << endl;
    }

    void batchSelect(int count) {
        clearScreen();
        showAppHeader();
        const vector<string>& targetNames = getCurrentRangeNames();
        int maxCount = static_cast<int>(targetNames.size());
        int actualCount = min(count, maxCount);

        cout << "\n===== 批量抽取 " << actualCount << " 人(" << getRangeName(currentRange) << ")=====" << endl;
        
        if (targetNames.empty()) {
            cout << "\n??  当前选择范围无有效名字!" << endl;
            cout << "\n" << Utils::generateLine(40) << "\n" << endl;
            cin.ignore(numeric_limits<streamsize>::max(), '\n');
            return;
        }

        // 批量动画展示
        if (hasAnimation) {
            playBatchAnimation(targetNames, actualCount);
        } else {
            // 无动画:直接获取去重结果
            selectedBatchNames = randomSelectBatchUnique(targetNames, actualCount);
            showBatchResult();
        }

        cout << "\n" << Utils::generateLine(40) << "\n" << endl;
        cin.ignore(numeric_limits<streamsize>::max(), '\n');  // 清除换行符
    }

    // 单次抽取动画
    void playSingleAnimation() {
        const vector<string>& targetNames = getCurrentRangeNames();
        if (targetNames.empty()) {
            selectedName = "无有效名字";
            Utils::setConsoleColor(NAME_COLOR);
            cout << Utils::centerAlign(selectedName, NAME_MAX_LENGTH);
            Utils::resetConsoleColor();
            cout << " 】 ★★★" << endl;
            return;
        }

        // 动画效果:快速切换显示随机名字
        for (int i = 0; i < ANIMATION_COUNT; ++i) {
            string randomName = randomSelectSingle(targetNames);
            
            // 移动到名字起始位置并覆盖显示
            cout << "\r★★★ 本次被选中的是:【 ";
            Utils::setConsoleColor(NAME_COLOR);
            cout << setw(static_cast<int>(NAME_MAX_LENGTH)) << left << Utils::centerAlign(randomName, NAME_MAX_LENGTH);
            Utils::resetConsoleColor();
            cout << "  】 ★★★";
            cout.flush();
            
            this_thread::sleep_for(chrono::milliseconds(ANIMATION_DELAY_MS));
        }

        // 显示最终选中的名字
        selectedName = randomSelectSingle(targetNames);
        cout << "\r★★★ 本次被选中的是:【 ";
        Utils::setConsoleColor(NAME_COLOR);
        cout << setw(static_cast<int>(NAME_MAX_LENGTH)) << left << Utils::centerAlign(selectedName, NAME_MAX_LENGTH);
        Utils::resetConsoleColor();
        cout << " 】 ★★★" << endl;
    }

    // 批量抽取动画(移除光标控制,改用清屏重绘)
    void playBatchAnimation(const vector<string>& targetNames, int count) {
        // 先获取去重后的最终结果
        vector<string> candidateNames = targetNames;
        shuffle(candidateNames.begin(), candidateNames.end(), getRandomGenerator());
        selectedBatchNames.clear();
        for (int i = 0; i < count && i < candidateNames.size(); ++i) {
            selectedBatchNames.push_back(candidateNames[i]);
        }

        int lines = (count + MAX_COLUMNS - 1) / MAX_COLUMNS;  // 计算行数
        int lineWidth = MAX_COLUMNS * BATCH_ITEM_WIDTH;  // 每行宽度

        // 动画过程
        for (int frame = 0; frame < ANIMATION_COUNT; ++frame) {
            clearScreen();
            showAppHeader();
            cout << "\n===== 批量抽取 " << count << " 人(" << getRangeName(currentRange) << ")=====" << endl;
            cout << "\n【抽取中...】" << endl;
            cout << "┌" << Utils::generateLine(lineWidth, '-') << "┐" << endl;

            // 逐行绘制当前帧
            for (int line = 0; line < lines; ++line) {
                cout << "│";
                Utils::setConsoleColor(NAME_COLOR);

                for (int col = 0; col < MAX_COLUMNS; ++col) {
                    int index = line * MAX_COLUMNS + col;
                    if (index >= count) {
                        cout << string(BATCH_ITEM_WIDTH, ' ');
                        continue;
                    }

                    string name;
                    // 动画后期逐步锁定
                    if (frame >= ANIMATION_COUNT - 8) {
                        name = selectedBatchNames[index];
                    } else {
                        // 随机滚动
                        uniform_int_distribution<> dis(0, static_cast<int>(targetNames.size()) - 1);
                        name = targetNames[dis(getRandomGenerator())];
                    }

                    cout << Utils::centerAlign(name, BATCH_ITEM_WIDTH);
                }

                Utils::resetConsoleColor();
                cout << "│" << endl;
            }

            cout << "└" << Utils::generateLine(lineWidth, '-') << "┘" << endl;
            cout.flush();
            this_thread::sleep_for(chrono::milliseconds(ANIMATION_DELAY_MS));
        }

        // 显示最终结果
        clearScreen();
        showAppHeader();
        cout << "\n===== 批量抽取 " << count << " 人(" << getRangeName(currentRange) << ")=====" << endl;
        cout << "\n【抽取完成】" << endl;
        cout << "┌" << Utils::generateLine(lineWidth, '-') << "┐" << endl;

        for (int line = 0; line < lines; ++line) {
            cout << "│";
            Utils::setConsoleColor(NAME_COLOR);
            for (int col = 0; col < MAX_COLUMNS; ++col) {
                int index = line * MAX_COLUMNS + col;
                if (index >= count) {
                    cout << string(BATCH_ITEM_WIDTH, ' ');
                    continue;
                }
                cout << Utils::centerAlign(selectedBatchNames[index], BATCH_ITEM_WIDTH);
            }
            Utils::resetConsoleColor();
            cout << "│" << endl;
        }

        cout << "└" << Utils::generateLine(lineWidth, '-') << "┘" << endl;
        showBatchResult();
    }

    // 显示批量抽取结果
    void showBatchResult() {
        cout << "\n抽取结果:" << endl;
        for (int i = 0; i < selectedBatchNames.size(); ++i) {
            cout << setw(2) << right << i + 1 << ". ";
            Utils::setConsoleColor(NAME_COLOR);
            cout << setw(static_cast<int>(NAME_MAX_LENGTH)) << left << selectedBatchNames[i];
            Utils::resetConsoleColor();
            cout << endl;
        }
    }

    // 随机选择单个名字
    string randomSelectSingle() const {
        const vector<string>& targetNames = getCurrentRangeNames();
        return targetNames.empty() ? "无有效名字" : randomSelectSingle(targetNames);
    }

    string randomSelectSingle(const vector<string>& names) const {
        uniform_int_distribution<> dis(0, static_cast<int>(names.size()) - 1);
        return names[dis(getRandomGenerator())];
    }

    // 随机选择批量名字(强制去重)
    vector<string> randomSelectBatchUnique(const vector<string>& names, int count) const {
        vector<string> result;
        vector<string> tempNames = names;
        
        shuffle(tempNames.begin(), tempNames.end(), getRandomGenerator());
        
        for (int i = 0; i < count && i < tempNames.size(); ++i) {
            result.push_back(tempNames[i]);
        }
        
        return result;
    }

    const vector<string>& getCurrentRangeNames() const {
        switch (currentRange) {
            case SelectRange::MALE: return nameManager.getMaleNames();
            case SelectRange::FEMALE: return nameManager.getFemaleNames();
            default: return nameManager.getAllNames();
        }
    }

    void saveResult(bool exitAfterSave = false) {
        bool hasResult = false;
        string saveContent;
        
        time_t now = time(nullptr);
        tm* localTime = localtime(&now);
        char timeBuf[64];
        strftime(timeBuf, sizeof(timeBuf), "%Y-%m-%d %H:%M:%S", localTime);

        if (!selectedName.empty()) {
            saveContent = "[" + string(timeBuf) + "] 【单次抽取】【" + getRangeName(currentRange) + "】 " + selectedName + "\n";
            hasResult = true;
        } else if (!selectedBatchNames.empty()) {
            saveContent = "[" + string(timeBuf) + "] 【批量抽取-" + to_string(selectedBatchNames.size()) + "人】【" + getRangeName(currentRange) + "】 ";
            for (size_t i = 0; i < selectedBatchNames.size(); ++i) {
                if (i > 0) saveContent += "、";
                saveContent += selectedBatchNames[i];
            }
            saveContent += "\n";
            hasResult = true;
        }

        if (!hasResult) {
            clearScreen();
            showAppHeader();
            showRangeSelection();
            cout << "\n??  暂无抽取结果可保存!" << endl;
            return;
        }

        clearScreen();
        showAppHeader();
        cout << "\n===== 保存结果 =====" << endl;
        cout << "请输入保存路径(回车使用默认路径:" << SAVE_FILE_NAME << "):";
        string savePath = Utils::trim(getInput());
        if (savePath.empty()) {
            savePath = SAVE_FILE_NAME;
            cout << "??  使用默认路径:" << savePath << endl;
        }

        ofstream outFile(savePath, ios::app);
        if (!outFile.is_open()) {
            cerr << "\n× 保存失败!无法打开文件:" << savePath << endl;
            clearScreen();
            showAppHeader();
            showRangeSelection();
            return;
        }

        outFile << saveContent;
        outFile.close();
        cout << "\n√ 保存成功!文件路径:" << savePath << endl;

        if (exitAfterSave) {
            exit(0);
        } else {
            // 保存成功后自动返回主界面,无额外提示
            this_thread::sleep_for(chrono::milliseconds(800));  // 短暂停留让用户看到成功提示
            clearScreen();
            showAppHeader();
            showRangeSelection();
        }
    }

    void showRangeSelection() const {
        cout << "===== 选择抽取范围 =====" << endl;
        cout << "  1. 全部(" << nameManager.getAllNames().size() << " 人)" << endl;
        cout << "  2. 男生(" << nameManager.getMaleNames().size() << " 人)" << endl;
        cout << "  3. 女生(" << nameManager.getFemaleNames().size() << " 人)" << endl;
        cout << "====================\n" << endl;
        cout << "当前抽取范围:" << getRangeName(currentRange) << endl;
        cout << "动画状态:" << (hasAnimation ? "启用(输入f关闭)" : "关闭(输入f启用)") << endl;
    }

    void showRangeUpdate() const {
        clearScreen();
        showAppHeader();
        showRangeSelection();
        cout << "\n√ 已切换到抽取范围:" << getRangeName(currentRange) << endl;
    }

    void showHelpBrief() const {
        cout << "\n===== 快速操作 =====\n" << endl;
        cout << "  回车/next - 单次抽取(带动画)" << endl;
        cout << "  p+数字(如p5)- 批量抽取对应人数(去重+动画)" << endl;
        cout << "  f - 切换动画开关" << endl;
        cout << "  help/? - 查看详细帮助" << endl;
        cout << "====================\n" << endl;
    }

    void showHelpDetail() const {
        clearScreen();
        showAppHeader();
        cout << "\n===== 详细帮助 =====\n" << endl;
        cout << "【抽取控制】" << endl;
        cout << "  1/2/3            - 选择抽取范围(1=全部,2=男生,3=女生)" << endl;
        cout << "  回车/next        - 单次抽取1人(默认带动画)" << endl;
        cout << "  p+数字(如p10)  - 批量抽取对应人数(自动去重,默认带动画)" << endl;
        cout << "  f                - 切换动画开关(启用/关闭)" << endl;
        cout << "\n【文件操作】" << endl;
        cout << "  w/save           - 保存当前抽取结果(单次/批量)" << endl;
        cout << "  :wq/save&exit    - 保存结果并退出程序" << endl;
        cout << "\n【其他指令】" << endl;
        cout << "  q/quit/exit      - 直接退出程序" << endl;
        cout << "  info/about       - 查看软件信息" << endl;
        cout << "  help/?           - 查看详细帮助" << endl;
        cout << "====================\n" << endl;
    }

    void showInfo() const {
        clearScreen();
        showAppHeader();
        cout << "\n===== 软件信息 =====\n" << endl;
        cout << "软件名称:" << APP_NAME << endl;
        cout << "版本号:" << APP_VERSION << endl;
        cout << "适用系统:Windows 10" << endl;
        cout << "主要功能:" << endl;
        cout << "  - 可选择抽取全部、男生或女生" << endl;
        cout << "  - 支持单次抽取(带动画)和批量抽取(p+数字,去重+动画)" << endl;
        cout << "  - 可保存抽取结果" << endl;
        cout << "copyright 2025 信建2502小原酱(任君明)" << endl;
        cout << "====================\n" << endl;
    }

    void invalidCommand() const {
        clearScreen();
        showAppHeader();
        showRangeSelection();
        cout << "\n??  输入无效!输入help或?查看支持的指令" << endl;
        cout << "   批量抽取请使用 p+数字 格式(如p3、P5)" << endl;
    }
    bool isNonNegativeInt(const string& str) const {
        if (str.empty()) return false;
        for (char c : str) if (!isdigit(c)) return false;
        return true;
    }

    string getRangeName(SelectRange range) const {
        return (range == SelectRange::MALE) ? "男生" : (range == SelectRange::FEMALE) ? "女生" : "全部";
    }

    void clearScreen() const {
        system("cls");
    }

    void showAppHeader() const {
        cout << "===== " << APP_NAME << " " << APP_VERSION << " =====\n" << endl;
    }

    string getInput() const {
        string input;
        getline(cin, input);
        return input;
    }
};

int main() {
    RollCallApp app;
    if (!app.init()) {
        return EXIT_FAILURE;
    } else {
        app.run();
    }
    return EXIT_SUCCESS;
}

此次升级的核心是完全重构了批量抽取功能,并增强了可视化效果。下表总结了主要变化:

升级维度

v0.0.3 (上一个版本)

v0.0.4 (当前版本)

升级点分析

1. 应用与配置

面向“信建2502班”

面向“信建2503班”,路径同步更新。

软件适配到了新班级。这表明代码具备了基本的可复用性。

2. 批量抽取功能

交互:直接输入数字(如 5)。
展示:简单纵向列表。
逻辑:简单抽取,可能重复

交互:新命令 p+数字 (如 p5)。
展示可视化网格动画与结果框
逻辑强制去重抽取。

功能全面增强。新命令更清晰;动画和网格显示效果炫酷;去重逻辑更符合实际场景需求。

3. 可视化效果

基础文本界面。

增加了动态网格动画ASCII框线 (┌─┐│└─┘) 和分隔线工具 (generateLine)。

用户体验显著提升。批量抽取过程更具仪式感和观赏性,更符合“课堂演示”的场景。

4. 结果保存

只能保存单次抽取结果。

可保存单次批量抽取结果,并在日志中明确区分类型和人数。

功能更完整。记录更详细,便于后续回顾。

5. 错误处理与提示

有基本提示。

提示更友好。例如,在输入无效时,会专门提示“请使用 p+数字 格式”。

交互更人性化,降低了用户的困惑感。

评论