本文最后更新于 2025-12-20,距今已 300 天,部分内容可能已经过时。
目录
老师想在班里的电脑上随机点名,我们先试了现成的点名软件,结果它要求登录;手机又不在手边,最后就变成了一个很适合练手的小项目:自己写一个能离线运行的点名器。
原稿里保留了很多从 0.0.1 到 0.0.4 的中间版本,代码越堆越长,反而看不清最后到底需要什么。这里只留下最后一版的核心功能:
- 从
n.txt和s.txt读取名单; - 选择全部、男生或女生;
- 一次抽取 1 人或多个人;
- 同一轮抽取不重复;
- 使用
mt19937,不再用time % 4充当随机数。

准备名单文件
把两个文本文件放在程序同一个目录下,每行写一个名字:
# n.txt
张三
李四
王五
# s.txt
小红
小兰
小雨
文件使用 UTF-8 保存。Windows 控制台如果显示中文乱码,可以先执行 chcp 65001,或者让编译器和终端统一使用 UTF-8。
完整代码
#include <algorithm>
#include <fstream>
#include <iostream>
#include <random>
#include <string>
#include <vector>
#ifdef _WIN32
#include <windows.h>
#endif
std::string trim(const std::string& value) {
const auto first = value.find_first_not_of(" \t\r\n");
if (first == std::string::npos) return {};
const auto last = value.find_last_not_of(" \t\r\n");
return value.substr(first, last - first + 1);
}
std::vector<std::string> loadNames(const std::string& fileName) {
std::ifstream file(fileName);
if (!file) {
std::cerr << "无法打开名单文件:" << fileName << "\n";
return {};
}
std::vector<std::string> names;
std::string line;
while (std::getline(file, line)) {
line = trim(line);
if (!line.empty()) names.push_back(line);
}
return names;
}
std::vector<std::string> chooseGroup(
const std::vector<std::string>& male,
const std::vector<std::string>& female,
int group
) {
if (group == 1) return male;
if (group == 2) return female;
std::vector<std::string> all = male;
all.insert(all.end(), female.begin(), female.end());
return all;
}
int main() {
#ifdef _WIN32
SetConsoleOutputCP(CP_UTF8);
SetConsoleCP(CP_UTF8);
#endif
const auto male = loadNames("n.txt");
const auto female = loadNames("s.txt");
if (male.empty() && female.empty()) {
std::cerr << "两个名单文件都没有可用名字。\n";
return 1;
}
std::cout << "选择范围:0. 全部 1. 男生 2. 女生\n> ";
int group = 0;
if (!(std::cin >> group) || group < 0 || group > 2) {
std::cerr << "范围选项无效。\n";
return 1;
}
auto candidates = chooseGroup(male, female, group);
if (candidates.empty()) {
std::cerr << "这个范围没有名单。\n";
return 1;
}
std::cout << "抽取人数(1 - " << candidates.size() << "):\n> ";
std::size_t count = 0;
if (!(std::cin >> count) || count == 0 || count > candidates.size()) {
std::cerr << "人数无效。\n";
return 1;
}
std::random_device seed;
std::mt19937 engine(seed());
std::shuffle(candidates.begin(), candidates.end(), engine);
std::cout << "\n本次抽到:\n";
for (std::size_t i = 0; i < count; ++i) {
std::cout << i + 1 << ". " << candidates[i] << "\n";
}
}
编译和运行
使用 MinGW-w64 或其他支持 C++17 的编译器:
chcp 65001
g++ -std=c++17 -O2 -Wall -Wextra main.cpp -o picker.exe
picker.exe
一个运行过程大概是:
选择范围:0. 全部 1. 男生 2. 女生
> 0
抽取人数(1 - 6):
> 2
本次抽到:
1. 小兰
2. 张三
std::shuffle 会先把候选名单打乱,再取前几个名字,所以一轮抽取中不会重复。下一次运行会重新使用随机种子,结果也不会固定。
这版和最初版本差在哪里
最开始的代码为了增加“功能感”,堆了动画、彩蛋、颜色、多个输入路径和几版重复实现,结果出现了 ifstream 使用不一致、帮助文本和实际参数不匹配、重复函数等问题。最后保留下来的版本反而简单很多:文件读取、范围选择、数量检查、随机打乱,四件事各自独立。
如果要继续做,可以再加:
- 抽取历史记录和导出 CSV;
- 用
std::filesystem检查名单文件; - 给名单加唯一 ID,避免同名同学被误判;
- 为“空文件、超出人数、重复名字、文件不存在”补自动化测试。
做这种班级小工具,先把输入格式和最终行为定清楚,比一开始就加一堆界面效果更重要。
C++ 实践:写一个班级随机点名器 作者:小原酱
· 发布于 2025-11-21
许可协议:CC BY-NC-SA 4.0
,转载请注明出处。
评论
评论区滚动到这里时加载(基于 GitHub Discussions)