在C++编程中,处理文本数据是再常见不过的需求了。相比于C语言中原始的字符数组(char[]),C++标准库提供的string类型无疑是一个更加强大和便捷的选择。string本质上是一个类模板的特化版本,它封装了字符序列以及一系列操作这些字符的方法,让字符串处理变得简单而高效。
要使用string类型,首先需要包含
cpp复制#include <string>
using namespace std;
string s1; // 默认初始化,空字符串
string s2 = "abc"; // 拷贝初始化
string s3("def"); // 直接初始化
string s4(5, 'x'); // 包含5个'x'字符的字符串
这里有个重要细节:string对象管理的字符串不以'\0'作为结束标志。这与C风格字符串有本质区别,意味着string可以包含空字符('\0')作为有效内容的一部分。
注意:虽然string用起来方便,但在某些对性能要求极高的场景(如嵌入式系统),可能需要权衡是否使用,因为它的抽象会带来一定的开销。
cin是C++中最基础的输入方式,但对于string有一些特殊行为:
cpp复制string word;
cin >> word; // 读取直到遇到空白字符(空格、制表符、换行等)
这种方式的局限性很明显:无法读取包含空格的整行文本。这在处理用户输入或文本文件时经常成为问题。
getline函数完美解决了包含空格的字符串输入问题。它有两种形式:
cpp复制string line;
getline(cin, line); // 读取直到遇到换行符
cpp复制string part;
getline(cin, part, ','); // 读取直到遇到逗号
实际编程中,经常会遇到需要混合使用cin和getline的情况。这时要特别注意缓冲区中可能残留的换行符:
cpp复制int age;
string name;
cin >> age; // 读取后换行符留在缓冲区
getline(cin, name); // 会立即读取到空行
// 正确做法:
cin >> age;
cin.ignore(); // 清除缓冲区中的换行符
getline(cin, name);
string提供了多种获取长度和访问元素的方式:
cpp复制string s = "hello";
int len1 = s.size(); // 5
int len2 = s.length(); // 5 (与size()完全等价)
// 访问元素
char first = s[0]; // 'h' (不检查边界)
char last = s.at(4); // 'o' (会检查边界,越界抛出异常)
经验之谈:在循环中访问元素时,使用size()作为边界条件比硬编码数字更安全可靠。现代C++编译器会对size()调用进行优化,不会每次循环都重新计算长度。
string提供了一系列修改内容的方法:
cpp复制string s = "Hello";
s.append(" World"); // "Hello World"
s.insert(5, " dear"); // "Hello dear World"
s.replace(6, 4, "my"); // "Hello my World"
s.erase(5, 3); // "Hello World"
这些操作都返回引用,因此可以链式调用:
cpp复制s.append("!").replace(0, 1, "h").erase(5, 1);
查找子串或字符:
cpp复制string text = "The quick brown fox jumps over the lazy dog";
size_t pos1 = text.find("fox"); // 16
size_t pos2 = text.find("cat"); // string::npos
size_t pos3 = text.rfind("the"); // 从后向前查找
比较字符串:
cpp复制string a = "apple", b = "banana";
int cmp = a.compare(b); // 返回负数(a<b)、0(a==b)或正数(a>b)
C++11引入了方便的转换函数:
cpp复制// 字符串转数值
int i = stoi("42");
double d = stod("3.14");
// 数值转字符串
string s1 = to_string(123);
string s2 = to_string(3.14159);
cpp复制string bigStr;
bigStr.reserve(1000); // 预留1000字符空间
cpp复制string createLargeString(); // 返回大字符串的函数
string s = createLargeString(); // 自动使用移动构造
cpp复制void processText(string_view sv) {
// 可以像使用string一样操作sv,但不会复制数据
}
string本质上是以字节为单位操作的,处理多字节编码(如UTF-8)时需要特别注意:
cpp复制string chinese = "你好";
cout << chinese.size(); // 输出6(UTF-8下每个中文字符占3字节)
解决方案是使用专门的库(如ICU)或C++20引入的char8_t和u8string。
频繁修改大字符串可能导致内存碎片。解决方法:
不同平台对string的实现可能有细微差别,特别是在以下方面:
编写跨平台代码时,应避免依赖这些实现细节。
实现一个简单的单词统计程序:
cpp复制#include <iostream>
#include <string>
#include <map>
using namespace std;
int main() {
map<string, int> wordCount;
string word;
while (cin >> word) {
// 转换为小写
for (char &c : word) {
c = tolower(c);
}
// 移除标点
word.erase(remove_if(word.begin(), word.end(), ::ispunct), word.end());
if (!word.empty()) {
++wordCount[word];
}
}
// 输出结果
for (const auto &pair : wordCount) {
cout << pair.first << ": " << pair.second << endl;
}
return 0;
}
一个实用的字符串分割实现:
cpp复制#include <vector>
#include <string>
std::vector<std::string> split(const std::string &str, char delimiter) {
std::vector<std::string> tokens;
size_t start = 0;
size_t end = str.find(delimiter);
while (end != std::string::npos) {
tokens.push_back(str.substr(start, end - start));
start = end + 1;
end = str.find(delimiter, start);
}
tokens.push_back(str.substr(start));
return tokens;
}
这个实现高效且灵活,可以处理各种分隔情况,包括连续分隔符和空字段。
经过多年的C++开发,我发现以下string使用习惯最为可靠:
对于性能关键的部分,建议进行基准测试。string的便利性通常值得那一点性能开销,但在极端情况下,回归C风格字符串可能是必要的优化手段。