长度与下标
text.size() / text.at(i)
下标从零开始。at 越界时会报告错误,适合初学阶段发现位置问题。
Easy explanation
一句话在我们眼中有含义,在程序眼中却是一串按顺序排列的字符。每个字符有位置,可以读取、比较、替换或删除。理解这一点后,“统计数字个数”就变成逐个检查字符,“找出用户名”就变成寻找分隔符再截取一段。
文本解析最重要的不是某个函数名,而是先约定格式。例如 add 数学作业 3 到底有几个字段?任务名能不能含空格?缺少数字怎么办?格式约定越明确,解析越可靠。面对不可信的用户文字,程序要验证长度、字符范围和字段数量,不能假设输入永远完美。
Core concepts
text.size() / text.at(i)
下标从零开始。at 越界时会报告错误,适合初学阶段发现位置问题。
size_t pos = text.find(":");
找不到时返回 string::npos,使用位置前必须检查,不能把 npos 当普通下标。
text.substr(start, length)
从 start 开始取指定长度。先确认起点合法,并分清第二个参数是长度而不是结束位置。
stringstream ss(line); ss >> word;
字符串流像 cin 一样从一行文字中读取单词和数字,适合空格分隔的简单格式。
for (char ch : text)范围循环适合逐字符统计,不需要手动管理下标。
if (isdigit(static_cast<unsigned char>(ch)))字符分类函数前转换为 unsigned char 更稳妥。
transform(text.begin(), text.end(), text.begin(), ::tolower);统一大小写后再比较,搜索更符合用户预期。
getline(ss, field, ","[0]);指定分隔符可以逐项读取简单 CSV,但仍要考虑引号等复杂情况。
Complete examples
Example 1
第一遍逐字符统计字母、数字和空格;第二遍用 stringstream 读取单词并寻找最长单词。两个任务分开后逻辑更清楚。
最长单词初始为空,每遇到更长单词就替换。标点会附在单词上,进阶版本可以先清理标点。
string line;
getline(cin, line);
int letters = 0, digits = 0, spaces = 0;
for (char ch : line)
{
unsigned char safe = static_cast<unsigned char>(ch);
if (isalpha(safe)) ++letters;
else if (isdigit(safe)) ++digits;
else if (isspace(safe)) ++spaces;
}
stringstream ss(line);
string word, longest;
int words = 0;
while (ss >> word)
{
++words;
if (word.size() > longest.size()) longest = word;
}
cout << letters << ' ' << digits << ' ' << words << ' ' << longest;
Example 2
约定命令格式为 add 名称|优先级,竖线让名称可以包含空格。程序先分离动作,再寻找竖线并转换数字。
每一步都可能失败,因此先检查空格、分隔符和数字范围。解析成功后再创建任务,避免半成品数据进入列表。
string line = "add 数学作业|3";
size_t firstSpace = line.find(' ');
if (firstSpace == string::npos) return 1;
string action = line.substr(0, firstSpace);
string rest = line.substr(firstSpace + 1);
size_t bar = rest.rfind('|');
if (bar == string::npos) return 1;
string name = rest.substr(0, bar);
int priority = stoi(rest.substr(bar + 1));
if (action == "add" && !name.empty() && priority >= 1 && priority <= 5)
cout << "已添加:" << name << ",优先级 " << priority;
Algorithm thinking
不要同时寻找所有字段。先确认整行是否为空,再分离最稳定的外层结构,然后逐个验证内部字段。每一步只接受已经满足前一步条件的文本,错误信息也能准确指出缺少什么。
文本算法经常涉及位置和长度。画出字符下标,手算 find 返回的位置以及 substr 取出的范围。对空字符串、没有分隔符、分隔符在开头或结尾、连续多个空格都要测试。
明确字段顺序、分隔符、是否允许空格和缺省值。
用 find 定位分隔符,找不到时立即返回清楚错误。
用 substr 取字段,去掉多余空格并统一大小写。
数字转换可能失败,字段长度与取值范围也要检查。
Common mistakes
循环条件写成 i <= text.size() 会多访问一次,应使用小于。
find 失败后继续 substr,会得到异常或错误结果。
substr 的第二个参数是字符数量,不是结束下标。
用户输入 abc 时转换会抛异常,调用前检查格式或捕获异常。
先准备一个最小输入,只保留能够重现问题的几行数据;再在关键步骤输出变量值,确认程序究竟在哪一步偏离预期。编译错误从第一条开始处理,运行错误则比较“实际结果”和“期望结果”。修复后别只重跑原来的例子,还要增加空输入、边界值和错误输入,防止问题换一个形式再次出现。
Homework
建议按顺序完成。前四题帮助巩固语法和基本操作,第五到第七题要求把多个知识点组合起来,第八题是小项目。每道题都要先写输入、处理、输出三行计划,再开始敲代码;程序运行后至少测试正常情况、边界情况和错误情况。
统计一行中的大写、小写、数字、空格和其他字符,并验证空行。
忽略大小写和空格判断一句话是否回文,先生成清理后的字符串再比较。
检查是否恰好有一个 @、两边非空且后半部分包含点,输出具体失败原因。
把句子中所有完整目标单词替换为新单词,不能误改较长单词的一部分。
解析姓名、年龄、城市三列,检查列数和年龄格式,再输出表格。
统一大小写并清理标点,统计每个单词出现次数,为第 16 课映射做准备。
支持 add、remove、show 和 quit,验证每种命令需要的参数数量。
读取多行 LEVEL|TIME|MESSAGE 格式日志,统计级别并查找包含关键词的记录。
本课验收标准:能安全处理空字符串和找不到分隔符;不发生下标越界;至少使用 find、substr、getline 和 stringstream;解析失败时能指出具体字段问题。
提交内容应包括源代码、三组测试输入与输出、一个曾经出现的错误及修复方法。最后请用自己的话解释本课最重要的概念;如果只能照着代码念,还需要再独立重写一次核心例子。