好久没更新了,今天有点能写的就来写一下吧~
起因:曹老师让我们在班上的电脑上搞一个随机点名,我们几个都对这个比较兴奋(有正经的理由能中午在班里玩电脑了),胡、李、余觉得应该用班级优化大师,于是我们下了班级优化大师,然后发现居然要登录,而我们手机都交了(啊啊啊气死我了)
于是我提议用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;
}
一编译,能跑,但有编译报错,我看不得任何报错,必须改
#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;
}
这个时候是老师用到的的一个版本,说要给他们班上的电脑也搞一个
得~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;
}
此次升级的核心是完全重构了批量抽取功能,并增强了可视化效果。下表总结了主要变化: