
前四天我们搭建了一个功能完整的计算器但有一个小瑕疵用户必须严格按照数字 运算符 数字的格式输入比如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 (...) { ... }