为什么使用std::stringstream遍历std::string时末尾会出现异常字符?
问题原因
这个乱码和编码没有任何关系,是std::stringstream的读取逻辑错误导致的。
具体出错点:
- 你使用的
while (s_fmt.good())判断逻辑存在漏洞:good()仅能代表上一次流操作没有发生错误,不能保证下一次get()操作能读取到有效字符。当流已经走到末尾时,good()仍然会返回true,进入循环后调用get()会返回EOF(通常值为-1),你将它强制转换为char类型就会生成一个不可打印的乱码字符,这就是末尾多余字符的来源。 - 你写的
if (c == '\0') break判断完全没有作用:std::string存储字符串时不会把末尾终止符\0计入有效长度,std::stringstream初始化时也不会将\0放入流缓冲区,你永远读不到\0,这条语句可以直接删除。 - 额外隐患:当前逻辑如果遇到
%后接未定义的格式符,会直接吞掉后面的字符且没有任何输出,你可以根据需求补充default分支处理这种情况。
修复方案
将循环逻辑修改为读取后先判断是否为EOF,再进行后续处理即可,修复后的完整代码如下:
#include <iostream> #include <sstream> #include <string> int main() { std::string fmt = "[%t]%Y-%M-%D<%H:%m:%s>:c"; std::stringstream s_res; std::stringstream s_fmt(fmt); int ch; // 先读取再判断是否为EOF,避免读取无效字符 while ((ch = s_fmt.get()) != EOF) { char c = (char)ch; if (c == '%') { int next_ch = s_fmt.peek(); if (next_ch == EOF) { // 处理%在末尾的边界情况 s_res << c; break; } switch (next_ch) { case 't': s_res << "Type"; break; case 'Y': s_res << "Year"; break; case 'M': s_res << "Month"; break; case 'D': s_res << "Day"; break; case 'H': s_res << "Hour"; break; case 'm': s_res << "min"; break; case 's': s_res << "sec"; break; case 'c': s_res << "content"; break; default: // 未定义的格式符,原样输出%和后面的字符 s_res << c << (char)next_ch; break; } s_fmt.get(); continue; } s_res << c; } std::cout << s_res.str(); return 0; }
运行修复后的代码就能得到预期输出:[Type]Year-Month-Day<Hour:min:sec>:content
内容的提问来源于stack exchange,提问作者MuxAte
相关产品推荐
相关产品推荐

