C++字符串分词代码陷入while循环无法退出,求排查解决
问题分析:while循环无法退出的原因及修复方案
问题描述
任务要求:找出字符数组s中所有包含数字的单词(单词以空格分隔,数组末尾始终带有一个句号)。使用测试输入qw6wqe esd9f 0gfgkj.时,代码中的while循环始终无法退出,调试发现token值有变化但循环停不下来。
原代码
#include <iostream> #include <vector> #include <cstdio> #include <cstring> #include <cctype> using namespace std; void Numbers(char s[], vector<pair<int, char>>& nums){ char *token; int zero = 0; token = strtok(s, " "); for(int i = 0; i < strlen(token); i++){ if(isdigit(token[i])){ nums.emplace_back(zero, *token); } } while(token != NULL){ zero ++; token = strtok(NULL, " "); for(int i = 0; i < strlen(token); i++){ if(isdigit(token[i])){ nums.emplace_back(zero, *token); } } } } int main() { vector<pair<int, char>> nums; char s[256]; gets(s); Numbers(s, nums); cout << nums.size(); for (int i = 0; i < nums.size(); i ++){ cout << nums[i].first << " " << nums[i].second << endl; } return 0; }
循环无法退出的核心原因
- strtok调用逻辑错误:原代码先进入
while(token != NULL)循环,再调用strtok(NULL, " ")获取下一个token。当最后一个token处理完后,strtok返回NULL,但此时循环体仍会执行strlen(token)——传入NULL指针会触发未定义行为,导致程序崩溃或进入死循环。 - 循环条件与执行顺序不匹配:正确逻辑应该是先获取下一个token,再判断是否为NULL,而非先进入循环再获取。
- 额外问题:
nums.emplace_back(zero, *token)存储的是单词首字符而非数字,不符合任务要求;gets函数存在缓冲区溢出风险。
修复后的代码
#include <iostream> #include <vector> #include <cstdio> #include <cstring> #include <cctype> #include <string> using namespace std; void Numbers(char s[], vector<pair<int, string>>& nums) { char *token; int wordIndex = 0; // 把空格和句号设为分隔符,自动处理末尾的句号 token = strtok(s, " ."); // 先获取token再判断,避免处理NULL指针 while (token != NULL) { bool hasDigit = false; string digits; // 遍历单词,收集所有数字 for (int i = 0; i < strlen(token); i++) { if (isdigit(token[i])) { hasDigit = true; digits += token[i]; } } // 若单词含数字,记录索引和对应数字 if (hasDigit) { nums.emplace_back(wordIndex, digits); } wordIndex++; token = strtok(NULL, " ."); } } int main() { vector<pair<int, string>> nums; char s[256]; // 替换gets为安全的fgets,避免缓冲区溢出 fgets(s, sizeof(s), stdin); // 移除fgets读取的换行符(若存在) size_t len = strlen(s); if (len > 0 && s[len-1] == '\n') { s[len-1] = '\0'; } Numbers(s, nums); cout << "含数字的单词数量:" << nums.size() << endl; for (auto& item : nums) { cout << "单词索引:" << item.first << ",包含的数字:" << item.second << endl; } return 0; }
修复说明
- 修正循环逻辑:调整为
先获取token→判断是否为NULL→处理token的顺序,彻底避免死循环。 - 优化分隔符:用
" ."作为分隔符,自动过滤末尾的句号,无需额外处理。 - 修正数据存储:记录单词索引和其中所有数字,符合任务要求。
- 替换不安全函数:用
fgets替代gets,并处理换行符,消除缓冲区溢出风险。
内容的提问来源于stack exchange,提问作者Михайло Горак
相关产品推荐
相关产品推荐

