从零开始的C++第五天:让程序听懂自然语言
前四天我们搭建了一个功能完整的计算器但有一个小瑕疵用户必须严格按照数字 运算符 数字的格式输入比如3 5中间必须带空格。如果用户想输入35不带空格或者3 5多个空格程序就会出错。今天我们就来解决这个问题让程序变得更智能、更友好。 回顾与目标第四天结束时我们获取用户输入的方式是cpp复制下载cin x oper y;cin 默认用空格、制表符、换行作为分隔符。所以35会被当成一个整体35试图存入double变量x时就会失败。今天我们要做两件事学习字符串处理——C如何操作文本改造计算器——让用户输入35、3 5、3 5都能正确识别 第一部分认识C中的字符串在C中有两种方式表示字符串1. C风格字符串C-Style Stringcpp复制下载char name[] Hello; // 字符数组末尾有隐藏的 \0这是从C语言继承来的方式操作起来比较麻烦需要手动管理内存、使用strcpy、strlen等函数新手容易出错。2. C标准库字符串std::stringcpp复制下载#include string using namespace std; string name Hello;这是C推荐的字符串类型使用起来非常方便支持拼接支持比较支持.length()获取长度支持[]索引访问自动管理内存无需担心溢出std::string常用操作速查cpp复制下载#include iostream #include string using namespace std; int main() { string s1 Hello; string s2 World; // 拼接 string s3 s1 s2; // Hello World // 长度 int len s3.length(); // 11 // 索引访问从0开始 char first s3[0]; // H char last s3[s3.length() - 1]; // d // 查找子串 int pos s3.find(World); // 6World从第6个字符开始 if (pos ! string::npos) { cout 找到了位置在 pos endl; } // 截取子串 string sub s3.substr(0, 5); // Hello从位置0开始取5个字符 // 判断是否为空 if (s3.empty()) { cout 字符串为空 endl; } // 清空 s3.clear(); return 0; } 第二部分解析用户输入——从35中提取数字和运算符核心思路是把用户输入的整个字符串读进来然后逐个字符分析。步骤1用getline读取整行输入cin 会忽略空格无法读取整行。我们需要用getlinecpp复制下载#include iostream #include string using namespace std; int main() { string input; cout 请输入算式; getline(cin, input); // 读取一整行包括空格 cout 你输入了 input endl; return 0; }步骤2设计解析算法我们要从字符串中提取出第一个数字、运算符、第二个数字。以35为例解析思路如下text复制下载遍历字符串中的每个字符 如果当前字符是数字或小数点 → 拼接到当前数字中 如果当前字符是运算符 - * /→ 记录运算符 如果当前字符是空格 → 跳过步骤3实现解析函数cpp复制下载#include iostream #include string using namespace std; bool ParseExpression(const string input, double x, char oper, double y) { string num1 ; // 第一个数字的字符串形式 string num2 ; // 第二个数字的字符串形式 oper \0; // 运算符初始化为空 bool foundOper false; for (char ch : input) { // C11 范围for循环遍历每个字符 if (ch ) { continue; // 遇到空格直接跳过 } if (ch || ch - || ch * || ch /) { if (foundOper) { cout 错误只能有一个运算符 endl; return false; } oper ch; foundOper true; } else if ( (ch 0 ch 9) || ch . ) { // 数字或小数点 if (!foundOper) { num1 ch; // 运算符还没出现属于第一个数字 } else { num2 ch; // 运算符已出现属于第二个数字 } } else { cout 错误包含非法字符 ch endl; return false; } } // 检查是否都找到了 if (num1.empty() || num2.empty() || oper \0) { cout 错误格式不正确请确保输入格式为 ab 或 a-b 等 endl; return false; } // 将字符串转换为数字 x stod(num1); // string to double y stod(num2); return true; } 新知识点详解1. 范围for循环Range-based for loopcpp复制下载for (char ch : input) { // 遍历 input 中的每一个字符依次赋值给 ch }这是C11引入的语法比传统的for (int i 0; i input.length(); i)更简洁。2.stod(string to double)cpp复制下载double num stod(3.14); // num 3.14这是C标准库提供的函数将字符串转换为浮点数。如果转换失败会抛出异常后续学习内容这里我们假设用户输入的是合法数字。3.string::empty()cpp复制下载if (num1.empty()) { ... } // 判断字符串是否为空 第三部分测试我们的解析器先写一个小程序单独测试解析功能cpp复制下载#include iostream #include string using namespace std; // 把上面的 ParseExpression 函数复制到这里 int main() { string input; double x, y; char oper; cout 测试解析器输入 q 退出 endl; while (true) { cout 请输入算式; getline(cin, input); if (input q || input Q) { break; } if (ParseExpression(input, x, oper, y)) { cout 解析成功 endl; cout x x , oper oper , y y endl; } } return 0; }测试用例35→ x3, oper, y5 ✅3 5→ x3, oper, y5 ✅3 5→ x3, oper, y5 ✅3.14*2→ x3.14, oper*, y2 ✅35→ 报错 ✅多个运算符abc→ 报错 ✅非法字符3→ 报错 ✅缺少第二个数字️ 第四部分整合到计算器主程序现在我们把解析功能整合到之前的计算器程序中。修改PerformCalculation函数cpp复制下载#include iostream #include string #include Calculator.h using namespace std; // 将 ParseExpression 声明放在这里 bool ParseExpression(const string input, double x, char oper, double y); void PerformCalculation(Calculator calc) { string input; double x 0.0, y 0.0; char oper ; cout 请输入算式例如35 或 3.14*2; getline(cin, input); // 读取整行 // 如果用户直接按回车重新提示 if (input.empty()) { cout 输入不能为空请重新输入 endl; return; } if (!ParseExpression(input, x, oper, y)) { return; // 解析失败直接返回错误信息已在ParseExpression中输出 } double result calc.Calculate(x, oper, y); cout x oper y result endl; }⚠️注意在main函数中如果前面使用了cin choice来读取菜单选项之后再用getline读取算式会遇到一个经典问题getline会读取到cin 留在输入缓冲区中的换行符\n导致第一次getline读到空字符串。解决方案在cin choice之后调用cin.ignore()清除缓冲区中的换行符cpp复制下载cin choice; cin.ignore(); // 忽略缓冲区中剩余的换行符 第五部分完整的第五天代码CalculatorTutorial.cppcpp复制下载#include iostream #include string #include Calculator.h using namespace std; // 函数声明 bool ParseExpression(const string input, double x, char oper, double y); void PerformCalculation(Calculator calc); void ShowMenu(); // 主函数 int main() { Calculator c; int choice 0; do { ShowMenu(); cin choice; cin.ignore(); // 清空缓冲区中的换行符为后面的getline做准备 if (choice 1) { PerformCalculation(c); } else if (choice 2) { cout 感谢使用再见 endl; break; } else { cout 无效输入请重新选择 endl; } } while (true); return 0; } // 函数定义 void ShowMenu() { cout \n 计算器 endl; cout 1. 进行计算 endl; cout 2. 退出程序 endl; cout 请选择输入数字; } bool ParseExpression(const string input, double x, char oper, double y) { string num1 ; string num2 ; oper \0; bool foundOper false; for (char ch : input) { if (ch ) { continue; } if (ch || ch - || ch * || ch /) { if (foundOper) { cout ❌ 错误只能有一个运算符 endl; return false; } oper ch; foundOper true; } else if ((ch 0 ch 9) || ch .) { if (!foundOper) { num1 ch; } else { num2 ch; } } else { cout ❌ 错误包含非法字符 ch endl; return false; } } if (num1.empty() || num2.empty() || oper \0) { cout ❌ 错误格式不正确请确保输入格式为 ab 或 a-b 等 endl; return false; } try { x stod(num1); y stod(num2); } catch (...) { cout ❌ 错误数字格式无效 endl; return false; } return true; } void PerformCalculation(Calculator calc) { string input; double x 0.0, y 0.0; char oper ; cout 请输入算式例如35 或 3.14*2; getline(cin, input); if (input.empty()) { cout 输入不能为空请重新输入 endl; return; } if (!ParseExpression(input, x, oper, y)) { return; } double result calc.Calculate(x, oper, y); cout x oper y result endl; } 第五天前四天我们搭建了一个功能完整的计算器但有一个小瑕疵用户必须严格按照数字 运算符 数字的格式输入比如3 5中间必须带空格。如果用户想输入35不带空格或者3 5多个空格程序就会出错。今天我们就来解决这个问题让程序变得更智能、更友好。 回顾与目标第四天结束时我们获取用户输入的方式是cpp复制下载cin x oper y;cin 默认用空格、制表符、换行作为分隔符。所以35会被当成一个整体35试图存入double变量x时就会失败。今天我们要做两件事学习字符串处理——C如何操作文本改造计算器——让用户输入35、3 5、3 5都能正确识别 第一部分认识C中的字符串在C中有两种方式表示字符串1. C风格字符串C-Style Stringcpp复制下载char name[] Hello; // 字符数组末尾有隐藏的 \0这是从C语言继承来的方式操作起来比较麻烦需要手动管理内存、使用strcpy、strlen等函数新手容易出错。2. C标准库字符串std::stringcpp复制下载#include string using namespace std; string name Hello;这是C推荐的字符串类型使用起来非常方便支持拼接支持比较支持.length()获取长度支持[]索引访问自动管理内存无需担心溢出std::string常用操作速查cpp复制下载#include iostream #include string using namespace std; int main() { string s1 Hello; string s2 World; // 拼接 string s3 s1 s2; // Hello World // 长度 int len s3.length(); // 11 // 索引访问从0开始 char first s3[0]; // H char last s3[s3.length() - 1]; // d // 查找子串 int pos s3.find(World); // 6World从第6个字符开始 if (pos ! string::npos) { cout 找到了位置在 pos endl; } // 截取子串 string sub s3.substr(0, 5); // Hello从位置0开始取5个字符 // 判断是否为空 if (s3.empty()) { cout 字符串为空 endl; } // 清空 s3.clear(); return 0; } 第二部分解析用户输入——从35中提取数字和运算符核心思路是把用户输入的整个字符串读进来然后逐个字符分析。步骤1用getline读取整行输入cin 会忽略空格无法读取整行。我们需要用getlinecpp复制下载#include iostream #include string using namespace std; int main() { string input; cout 请输入算式; getline(cin, input); // 读取一整行包括空格 cout 你输入了 input endl; return 0; }步骤2设计解析算法我们要从字符串中提取出第一个数字、运算符、第二个数字。以35为例解析思路如下text复制下载遍历字符串中的每个字符 如果当前字符是数字或小数点 → 拼接到当前数字中 如果当前字符是运算符 - * /→ 记录运算符 如果当前字符是空格 → 跳过步骤3实现解析函数cpp复制下载#include iostream #include string using namespace std; bool ParseExpression(const string input, double x, char oper, double y) { string num1 ; // 第一个数字的字符串形式 string num2 ; // 第二个数字的字符串形式 oper \0; // 运算符初始化为空 bool foundOper false; for (char ch : input) { // C11 范围for循环遍历每个字符 if (ch ) { continue; // 遇到空格直接跳过 } if (ch || ch - || ch * || ch /) { if (foundOper) { cout 错误只能有一个运算符 endl; return false; } oper ch; foundOper true; } else if ( (ch 0 ch 9) || ch . ) { // 数字或小数点 if (!foundOper) { num1 ch; // 运算符还没出现属于第一个数字 } else { num2 ch; // 运算符已出现属于第二个数字 } } else { cout 错误包含非法字符 ch endl; return false; } } // 检查是否都找到了 if (num1.empty() || num2.empty() || oper \0) { cout 错误格式不正确请确保输入格式为 ab 或 a-b 等 endl; return false; } // 将字符串转换为数字 x stod(num1); // string to double y stod(num2); return true; } 新知识点详解1. 范围for循环Range-based for loopcpp复制下载for (char ch : input) { // 遍历 input 中的每一个字符依次赋值给 ch }这是C11引入的语法比传统的for (int i 0; i input.length(); i)更简洁。2.stod(string to double)cpp复制下载double num stod(3.14); // num 3.14这是C标准库提供的函数将字符串转换为浮点数。如果转换失败会抛出异常后续学习内容这里我们假设用户输入的是合法数字。3.string::empty()cpp复制下载if (num1.empty()) { ... } // 判断字符串是否为空 第三部分测试我们的解析器先写一个小程序单独测试解析功能cpp复制下载#include iostream #include string using namespace std; // 把上面的 ParseExpression 函数复制到这里 int main() { string input; double x, y; char oper; cout 测试解析器输入 q 退出 endl; while (true) { cout 请输入算式; getline(cin, input); if (input q || input Q) { break; } if (ParseExpression(input, x, oper, y)) { cout 解析成功 endl; cout x x , oper oper , y y endl; } } return 0; }测试用例35→ x3, oper, y5 ✅3 5→ x3, oper, y5 ✅3 5→ x3, oper, y5 ✅3.14*2→ x3.14, oper*, y2 ✅35→ 报错 ✅多个运算符abc→ 报错 ✅非法字符3→ 报错 ✅缺少第二个数字️ 第四部分整合到计算器主程序现在我们把解析功能整合到之前的计算器程序中。修改PerformCalculation函数cpp复制下载#include iostream #include string #include Calculator.h using namespace std; // 将 ParseExpression 声明放在这里 bool ParseExpression(const string input, double x, char oper, double y); void PerformCalculation(Calculator calc) { string input; double x 0.0, y 0.0; char oper ; cout 请输入算式例如35 或 3.14*2; getline(cin, input); // 读取整行 // 如果用户直接按回车重新提示 if (input.empty()) { cout 输入不能为空请重新输入 endl; return; } if (!ParseExpression(input, x, oper, y)) { return; // 解析失败直接返回错误信息已在ParseExpression中输出 } double result calc.Calculate(x, oper, y); cout x oper y result endl; }⚠️注意在main函数中如果前面使用了cin choice来读取菜单选项之后再用getline读取算式会遇到一个经典问题getline会读取到cin 留在输入缓冲区中的换行符\n导致第一次getline读到空字符串。解决方案在cin choice之后调用cin.ignore()清除缓冲区中的换行符cpp复制下载cin choice; cin.ignore(); // 忽略缓冲区中剩余的换行符 第五部分完整的第五天代码CalculatorTutorial.cppcpp复制下载#include iostream #include string #include Calculator.h using namespace std; // 函数声明 bool ParseExpression(const string input, double x, char oper, double y); void PerformCalculation(Calculator calc); void ShowMenu(); // 主函数 int main() { Calculator c; int choice 0; do { ShowMenu(); cin choice; cin.ignore(); // 清空缓冲区中的换行符为后面的getline做准备 if (choice 1) { PerformCalculation(c); } else if (choice 2) { cout 感谢使用再见 endl; break; } else { cout 无效输入请重新选择 endl; } } while (true); return 0; } // 函数定义 void ShowMenu() { cout \n 计算器 endl; cout 1. 进行计算 endl; cout 2. 退出程序 endl; cout 请选择输入数字; } bool ParseExpression(const string input, double x, char oper, double y) { string num1 ; string num2 ; oper \0; bool foundOper false; for (char ch : input) { if (ch ) { continue; } if (ch || ch - || ch * || ch /) { if (foundOper) { cout ❌ 错误只能有一个运算符 endl; return false; } oper ch; foundOper true; } else if ((ch 0 ch 9) || ch .) { if (!foundOper) { num1 ch; } else { num2 ch; } } else { cout ❌ 错误包含非法字符 ch endl; return false; } } if (num1.empty() || num2.empty() || oper \0) { cout ❌ 错误格式不正确请确保输入格式为 ab 或 a-b 等 endl; return false; } try { x stod(num1); y stod(num2); } catch (...) { cout ❌ 错误数字格式无效 endl; return false; } return true; } void PerformCalculation(Calculator calc) { string input; double x 0.0, y 0.0; char oper ; cout 请输入算式例如35 或 3.14*2; getline(cin, input); if (input.empty()) { cout 输入不能为空请重新输入 endl; return; } if (!ParseExpression(input, x, oper, y)) { return; } double result calc.Calculate(x, oper, y); cout x oper y result endl; } 第五天知识点地图知识点作用关键代码std::string现代化字符串处理#include string字符串拼接合并字符串s1 s2字符串长度获取字符数s.length()字符串索引访问单个字符s[0]getline读取整行输入含空格getline(cin, input)范围for循环遍历字符串每个字符for (char ch : input)stod字符串转浮点数double num stod(3.14);cin.ignore()清除输入缓冲区cin.ignore();try-catch捕获异常简单入门try { ... } catch (...) { ... }知识点地图知识点作用关键代码std::string现代化字符串处理#include string字符串拼接合并字符串s1 s2字符串长度获取字符数s.length()字符串索引访问单个字符s[0]getline读取整行输入含空格getline(cin, input)范围for循环遍历字符串每个字符for (char ch : input)stod字符串转浮点数double num stod(3.14);cin.ignore()清除输入缓冲区cin.ignore();try-catch捕获异常简单入门try { ... } catch (...) { ... }

相关新闻

SkillEvolver:让 Agent 学会“自己写技能”的元技能框架

SkillEvolver:让 Agent 学会“自己写技能”的元技能框架

一、一句话概述 SkillEvolver 的核心想法很直接:与其让人手写 Agent skill,或者让模型一次性“凭记忆”生成 skill,不如给 Agent 一个 meta-skill,让它在少量训练任务试错中,自动探索、编写、部署、审计并迭代出一个可…

2026/7/24 11:20:49 阅读更多 →
ADS8588S高精度同步ADC:多通道数据采集系统设计实战指南

ADS8588S高精度同步ADC:多通道数据采集系统设计实战指南

1. 项目概述:为什么我们需要ADS8588S这样的高精度同步ADC?在工业自动化、电力监控或者多相电机控制的项目里,我们工程师常常会遇到一个头疼的问题:如何同时、准确地捕捉多路快速变化的模拟信号?比如,你想监…

2026/7/24 11:20:49 阅读更多 →
AI驱动决策:企业智能体系统的架构与演进

AI驱动决策:企业智能体系统的架构与演进

1. 为什么企业决策正在从"经验主义"转向"AI驱动"去年参加行业峰会时,我注意到一个有趣现象:当讨论到市场策略时,传统企业高管们还在说"根据我20年经验...",而新兴企业的年轻CEO们已经在展示"我…

2026/7/24 11:20:49 阅读更多 →

最新新闻

昇腾NPU与MindSpore框架的AI训练优化实践

昇腾NPU与MindSpore框架的AI训练优化实践

1. 项目背景与核心价值 在AI模型训练领域,框架与硬件的协同优化一直是提升效率的关键路径。华为昇腾NPU(Neural Processing Unit)作为专为深度学习设计的处理器,与MindSpore框架的深度结合,为开发者提供了从算法设计到…

2026/7/24 11:27:51 阅读更多 →
LMK61E0M DCXO模式实战:从PLL原理到70.656MHz时钟的无毛刺调频

LMK61E0M DCXO模式实战:从PLL原理到70.656MHz时钟的无毛刺调频

1. 项目概述与核心价值在高速数字系统、通信设备乃至精密测量仪器中,一个稳定、纯净且可编程的时钟源是系统正常工作的基石。无论是FPGA的逻辑同步、ADC/DAC的采样时钟,还是网络设备的时钟恢复,对时钟信号频率精度、相位噪声和抖动性能的要求…

2026/7/24 11:27:51 阅读更多 →
C++高并发数据流水线五大核心法则:从无锁设计到性能调优实战

C++高并发数据流水线五大核心法则:从无锁设计到性能调优实战

1. 项目概述:为什么我们需要重新审视C高并发数据流水线?如果你正在用C处理海量数据,比如实时日志分析、高频交易撮合,或者视频流处理,你大概率已经感受到了单线程的力不从心。数据像潮水一样涌来,传统的串行…

2026/7/24 11:27:51 阅读更多 →
小熊猫Dev-C++实战指南:从零配置到多文件项目开发

小熊猫Dev-C++实战指南:从零配置到多文件项目开发

1. 项目概述:为什么今天还要聊Dev-C? 如果你是一位刚接触编程的C/C新手,或者是一位需要给学生准备教学环境的老师,那么“Dev-C”这个名字你一定不陌生。它可能不是你听说过的第一个IDE,但很可能是你最早接触、也最容易…

2026/7/24 11:27:51 阅读更多 →
非侵入、纳秒级、跨芯片——ISDT 让电控系统的实时性问题不用再靠猜

非侵入、纳秒级、跨芯片——ISDT 让电控系统的实时性问题不用再靠猜

嵌入式软件,尤其是电控系统,有个共同的难点:让代码跑起来不难,难的是搞清楚它到底跑得好不好——快在哪、慢在哪、稳不稳。系统偶尔卡顿、响应变慢,到底是哪个任务、哪段代码多花了时间,靠日志和经验很难查…

2026/7/24 11:27:51 阅读更多 →
Transformer自注意力机制原理与工程实践

Transformer自注意力机制原理与工程实践

1. Transformer架构中的自注意力机制解析 2017年那篇《Attention Is All You Need》论文扔进NLP领域就像往池塘里丢了块巨石。当时我在做机器翻译项目,第一次看到完全基于注意力机制的模型架构时,整个人都是懵的。传统RNN那套序列处理方式突然被颠覆&…

2026/7/24 11:26:50 阅读更多 →

日新闻

用Highcharts 创建可拖拽三维散点立方体3D图表

用Highcharts 创建可拖拽三维散点立方体3D图表

该案例基于Highcharts scatter3d 三维散点图实现空间立方体散点可视化,核心特色:三维 X/Y/Z 三轴空间,所有散点分布在 0~10 立方体空间内;散点使用径向渐变实现立体 3D 圆球质感;支持鼠标 / 触屏拖拽画布,…

2026/7/24 0:00:29 阅读更多 →
AppCertDlls:进程创建路径上的 DLL 入口

AppCertDlls:进程创建路径上的 DLL 入口

AppCertDlls:进程创建路径上的 DLL 入口 AppCertDlls 位于 HKLM\System\CurrentControlSet\Control\Session Manager\AppCertDlls。本文的程序功能是只读列出这个键在 64 位和 32 位注册表视图中的全部值,并显示每条值的来源、名称、类型和可安全显示的数…

2026/7/24 0:00:29 阅读更多 →
我的编程之路:第一篇博客

我的编程之路:第一篇博客

大家好,我是一名编程初学者,同时这也是我编程学习之路上的第一篇博客。在这里,我想要向大家介绍我的一些想法和规划。a.自我介绍我是一个刚刚接触编程的新手,目前在学习c语言,我对编程世界充满了强烈的好奇。当然&…

2026/7/24 0:00:29 阅读更多 →

周新闻

Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/24 3:59:20 阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/24 1:23:39 阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/23 17:49:47 阅读更多 →

月新闻