【C++实战技巧】substr函数高效截取字符串的5种场景解析 1. substr函数基础从零开始的字符串手术刀第一次接触C字符串操作时我就被substr函数的简洁高效惊艳到了。这个看似简单的函数就像外科医生的手术刀能精准地切割字符串的任意部分。先来看个最基础的例子string phone 13812345678; string prefix phone.substr(0, 3); // 获取手机号前三位 cout prefix; // 输出138这里我们截取了手机号的前三位运营商代码。注意两个关键参数第一个0表示从索引0开始C字符串索引从0开始第二个3表示截取3个字符。这个基础用法看似简单但实际开发中我见过不少新手容易犯的两个错误把第二个参数误认为结束索引比如误以为substr(0,3)是截取到索引3忘记检查字符串长度直接截取可能导致越界提示substr的参数n是截取长度而非结束位置这点与Python等语言的切片操作不同当你不确定字符串长度时安全做法是先检查if (phone.length() 3) { string prefix phone.substr(0, 3); }2. 文件路径处理自动提取文件名和扩展名上周处理一个文件管理系统时我需要从完整路径中提取文件名。substr配合find方法完美解决了这个问题string path /home/user/docs/report.pdf; size_t lastSlash path.find_last_of(/); string filename (lastSlash string::npos) ? path : path.substr(lastSlash 1); // filename report.pdf这里用find_last_of找到最后一个斜杠位置然后用substr截取斜杠之后的部分。更复杂的情况是分离文件名和扩展名size_t dotPos filename.find_last_of(.); if (dotPos ! string::npos) { string name filename.substr(0, dotPos); // report string ext filename.substr(dotPos 1); // pdf }实际项目中我遇到过Windows路径的反斜杠问题这时需要先统一替换路径分隔符replace(path.begin(), path.end(), \\, /);3. 日志分析精准提取关键信息处理服务器日志时经常需要提取特定时间段或错误代码。比如这样的日志格式[2023-08-15 14:30:45] ERROR 500: Internal server error用substr可以这样提取关键信息string log [2023-08-15 14:30:45] ERROR 500: Internal server error; string timestamp log.substr(1, 19); // 2023-08-15 14:30:45 string errorCode log.substr(32, 3); // 500但固定位置截取在日志格式变化时很脆弱。更健壮的做法是结合findsize_t bracketEnd log.find(]); if (bracketEnd ! string::npos) { string timestamp log.substr(1, bracketEnd - 1); } size_t errorStart log.find(ERROR); if (errorStart ! string::npos) { string errorMsg log.substr(errorStart 6); // 截取ERROR之后的内容 }4. 数据清洗处理不规则文本从网页抓取数据时经常遇到需要清理的文本。比如去除HTML标签string html divHello bWorld/b/div; string cleanText; size_t start 0, end; while ((start html.find(, start)) ! string::npos) { cleanText html.substr(end, start - end); // 添加标签间文本 end html.find(, start) 1; start end; } cleanText html.substr(end); // 添加最后一段文本 // cleanText Hello World另一个常见场景是处理CSV文件中的引号包裹字段string csv 123,\Apple, Inc\,456; vectorstring fields; size_t start 0, quotePos; while (start csv.length()) { if (csv[start] ) { quotePos csv.find(, start 1); fields.push_back(csv.substr(start 1, quotePos - start - 1)); start csv.find(,, quotePos) 1; } else { size_t end csv.find(,, start); fields.push_back(csv.substr(start, end - start)); start end 1; } } // fields {123, Apple, Inc, 456}5. 性能优化避免不必要的字符串拷贝substr虽然方便但不当使用会导致性能问题。关键点在于它每次都会创建新字符串对象。比如这个看似无害的代码for (int i 0; i largeStr.length(); i 10) { process(largeStr.substr(i, 10)); // 每次循环都创建临时字符串 }对于大字符串这会频繁分配内存。优化方法是用string_view(C17)或直接传索引// 方法1使用string_view for (int i 0; i largeStr.length(); i 10) { string_view view(largeStr.c_str() i, min(10, (int)largeStr.length() - i)); process(view); } // 方法2传索引范围 void processSubstring(const string str, size_t start, size_t len) { // 直接操作原字符串的指定区间 }另一个技巧是预分配内存string result; result.reserve(totalLength); // 预分配足够空间 while (...) { string part getNextPart(); result part.substr(...); // 减少重新分配 }在最近的一个文本处理项目中通过这类优化处理时间从2.3秒降到了0.8秒。记住在性能敏感的场景每个substr调用都值得仔细考量。