在C++编程中,字符串处理是最基础也是最重要的技能之一。作为一门系统级编程语言,C++提供了两种截然不同的字符串处理方式:传统的C风格字符数组和现代的C++标准库string类。这两种方式各有特点,适用于不同的场景。
C风格字符数组直接继承自C语言,使用字符数组和指针来操作字符串,以空字符'\0'作为字符串的结束标志。这种方式虽然效率高,但需要程序员手动管理内存,容易出现缓冲区溢出等安全问题。而C++的string类则封装了字符数组的操作,提供了自动内存管理、丰富的成员函数和运算符重载,大大简化了字符串处理。
在信息学竞赛和日常开发中,string类因其安全性和便捷性成为首选。它不仅能自动处理内存分配和释放,还提供了查找、替换、拼接等常用操作,让程序员可以更专注于算法逻辑而非底层细节。
C风格字符数组本质上是一个连续的字符序列,在内存中以'\0'作为结束标志。例如:
cpp复制char cstr[] = "Hello"; // 实际存储:'H','e','l','l','o','\0'
而C++的string类是一个封装类,内部维护了一个动态分配的字符数组,并自动记录字符串长度。它不需要依赖'\0'来判断字符串结束,这使得string类可以包含'\0'字符本身。
在性能方面,C风格字符数组通常有轻微优势,因为它没有额外的类开销。但在大多数应用场景中,这种差异可以忽略不计。相比之下,string类提供了更好的安全性:
在以下情况下可以考虑使用C风格字符数组:
其他情况下,特别是信息学竞赛中,强烈建议使用string类。它不仅代码更简洁,还能减少常见错误。
使用string类需要包含
cpp复制#include <string>
using namespace std;
string类提供了多种初始化方式:
cpp复制string s1; // 空字符串
string s2 = "Hello"; // 拷贝初始化
string s3("World"); // 直接初始化
string s4(5, 'a'); // 包含5个'a'的字符串
string s5(s2); // 拷贝构造
string s6 = s2 + s3; // 通过表达式初始化
cpp复制string s;
cin >> s; // 读取一个单词(遇到空白停止)
cout << s; // 输出字符串
使用getline读取整行内容:
cpp复制string line;
getline(cin, line); // 读取整行,包括空格
注意:当混合使用cin和getline时,需要清除输入缓冲区:
cpp复制int n;
cin >> n;
cin.ignore(); // 忽略换行符
string s;
getline(cin, s);
可以通过下标或at()方法访问单个字符:
cpp复制string s = "Hello";
char c1 = s[0]; // 'H'
char c2 = s.at(1); // 'e'
区别在于at()会进行边界检查,越界时会抛出异常。
获取字符串长度的两种方法:
cpp复制int len1 = s.size();
int len2 = s.length(); // 与size()完全相同
注意:返回值是size_t类型,与int比较时可能需要类型转换。
cpp复制string s1 = "Hello";
string s2 = "World";
s1 += " "; // 追加字符串
s1 += s2; // 追加另一个string
s1.append("!"); // 使用append方法
cpp复制s.insert(5, " "); // 在位置5插入空格
s.erase(5, 1); // 从位置5删除1个字符
s.replace(6, 5, "There"); // 替换子串
cpp复制size_t pos = s.find("World"); // 返回首次出现的位置
if(pos != string::npos) {
// 找到子串
}
其他查找方法:
cpp复制string sub = s.substr(6, 5); // 从位置6开始,取5个字符
cpp复制for(int i = 0; i < s.size(); ++i) {
cout << s[i];
}
cpp复制for(auto it = s.begin(); it != s.end(); ++it) {
cout << *it;
}
cpp复制for(char c : s) {
cout << c;
}
如需修改字符,使用引用:
cpp复制for(char &c : s) {
c = toupper(c);
}
cpp复制string numStr = "123";
int num = stoi(numStr); // string to int
double d = stod("3.14"); // string to double
cpp复制int num = 456;
string s = to_string(num);
cpp复制reverse(s.begin(), s.end());
cpp复制sort(s.begin(), s.end());
cpp复制bool isPalindrome = equal(s.begin(), s.begin()+s.size()/2, s.rbegin());
cpp复制string s;
s.reserve(100); // 预分配100字节
cpp复制void process(const string& s); // 避免拷贝
cpp复制string createString() {
string s(1000000, 'a');
return s; // 触发移动语义
}
cpp复制string s = "Hello";
// cout << s[5]; // 未定义行为,可能崩溃
cout << s.at(5); // 抛出std::out_of_range异常
cpp复制int n;
cin >> n;
cin.ignore(); // 必须清除换行符
string s;
getline(cin, s);
直接使用比较运算符:
cpp复制if(s1 == s2) {...}
if(s1 < s2) {...} // 字典序比较
cpp复制map<string, int> wordCount;
string word;
while(cin >> word) {
++wordCount[word];
}
cpp复制string line;
while(getline(cin, line)) {
stringstream ss(line);
string field;
while(getline(ss, field, ',')) {
// 处理每个字段
}
}
cpp复制vector<string> split(const string& s, char delimiter) {
vector<string> tokens;
string token;
istringstream tokenStream(s);
while(getline(tokenStream, token, delimiter)) {
tokens.push_back(token);
}
return tokens;
}
string_view提供对字符串的非拥有视图,避免不必要的拷贝:
cpp复制string s = "Hello World";
string_view sv(s.c_str(), 5); // "Hello"
cpp复制#include <regex>
regex pattern(R"(\d+)"); // 匹配数字
string s = "abc123def";
smatch matches;
if(regex_search(s, matches, pattern)) {
cout << matches[0]; // "123"
}
对于UTF-8等编码,可以使用专门的库:
cpp复制string utf8 = "你好";
// 需要使用专门的库处理多字节字符
在实际开发中,根据具体需求选择最合适的字符串处理方法,平衡性能、安全性和开发效率。对于大多数应用场景,C++的string类提供了最佳的综合解决方案。