【编码艺术:掌握String类函数接口的妙用指南】(二):https://developer.aliyun.com/article/1425650
5.6.clear()只是将string中有效字符清空,不改变底层空间大小。
6. string类对象的增删查改操作
6.1.增加
在string尾部追加字符时,s.push_back(c) / s.append(1, c) / s += 'c'三种的实现方式差不多,一般情况下string类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可以连接字符串。
这里有一个问题:string s1 = "hello world";//这个可行吗?可行,这里是一个单参数的构造函数,它会隐式类型转换,它先去构造,然后再拷贝构造,不过这里编译器可能会优化成直接构造。
我们来看一下append的使用。
int main() { std::string str; std::string str2 = "Writing "; std::string str3 = "print 10 and then 5 more"; // used in the same order as described above: str.append(str2); // "Writing " str.append(str3, 6, 3); // "10 " str.append("here: "); // "here: " str.append("dots are cool", 5); // "dots " str.append(10u, '.'); // ".........." str.append(str3.begin() + 8, str3.end()); // " and then 5 more" std::cout << str << '\n'; return 0; }
我们上面有了尾插,那我们有没有头插呢?或者在中间插入呢?
// inserting into a string #include <iostream> #include <string> int main () { std::string str="to be question"; std::string str2="the "; std::string str3="or not to be"; std::string::iterator it; // used in the same order as described above: str.insert(6,str2); // to be (the )question str.insert(6,str3,3,4); // to be (not )the question str.insert(10,"that is cool",8); // to be not (that is )the question str.insert(10,"to be "); // to be not (to be )that is the question str.insert(15,1,':'); // to be not to be(:) that is the question it = str.insert(str.begin()+5,','); // to be(,) not to be: that is the question str.insert (str.end(),3,'.'); // to be, not to be: that is the question(...) str.insert (it+2,str3.begin(),str3.begin()+3); // (or ) std::cout << str << '\n'; return 0; }
实际中头插使用较少,因为要挪动数据,效率不高。那删除字符呢?
6.2.删除
// string::erase #include <iostream> #include <string> int main () { std::string str ("This is an example sentence."); std::cout << str << '\n'; // "This is an example sentence." str.erase (10,8); // ^^^^^^^^ std::cout << str << '\n'; // "This is an sentence." str.erase (str.begin()+9); // ^ std::cout << str << '\n'; // "This is a sentence." str.erase (str.begin()+5, str.end()-9); // ^^^^^ std::cout << str << '\n'; // "This sentence." return 0; }
6.3.查找
注意:
- pos含义:搜索字符串中要查找的第一个字符的位置。
- 函数返回值:第一个匹配项的第一个字符的位置。 如果未找到任何匹配项,则该函数返回
string::npos
。
// string::find #include <iostream> // std::cout #include <string> // std::string int main () { std::string str ("There are two needles in this haystack with needles."); std::string str2 ("needle"); // different member versions of find in the same order as above: std::size_t found = str.find(str2); if (found!=std::string::npos) std::cout << "first 'needle' found at: " << found << '\n'; found=str.find("needles are small",found+1,6); if (found!=std::string::npos) std::cout << "second 'needle' found at: " << found << '\n'; found=str.find("haystack"); if (found!=std::string::npos) std::cout << "'haystack' also found at: " << found << '\n'; found=str.find('.'); if (found!=std::string::npos) std::cout << "Period found at: " << found << '\n'; // let's replace the first needle: str.replace(str.find(str2),str2.length(),"preposition"); std::cout << str << '\n'; return 0; }
现在我们来使用一下find,查找字符串的后缀是什么?这里我们会使用到substr。
int main() { string s1("Test.cpp"); string s2("Test.tar.zip"); size_t pos1 = s1.find('.'); if(pos1 != string::npos) { string suff1 = s1.substr(pos1, s1.size() - pos1); cout << suff1 << endl;//.cpp string suff2 = s1.substr(pos1);//默认取到结尾 cout << suff2 << endl;//.cpp } size_t pos2 = s2.find('.'); if (pos2 != string::npos) { string suff1 = s2.substr(pos2, s2.size() - pos2); cout << suff1 << endl;//.tar.zip string suff2 = s2.substr(pos2);//默认取到结尾 cout << suff2 << endl;//.tar.zip } //搜索字符串中由参数指定的序列的最后一次出现 size_t pos3 = s2.rfind('.'); if (pos2 != string::npos) { string suff1 = s2.substr(pos3, s2.size() - pos3); cout << suff1 << endl;//.zip string suff2 = s2.substr(pos3);//默认取到结尾 cout << suff2 << endl;//.zip } return 0; }
运行结果:
如何将一个网址分开呢?
#include <iostream> #include <string> using namespace std; int main() { string str("https://cplusplus.com/reference/string/string/substr/"); string sub1, sub2, sub3; size_t pos1 = str.find(':'); //左闭右开区间 sub1 = str.substr(0, pos1 - 0); cout << sub1 << endl; size_t pos2 = str.find('/', pos1 + 3); sub2 = str.substr(pos1 + 3, pos2 - (pos1 + 3)); cout << sub2 << endl; sub3 = str.substr(pos2 + 1); cout << sub3 << endl; return 0; }
运行结果:
6.4.修改
// replacing in a string #include <iostream> #include <string> int main () { std::string base="this is a test string."; std::string str2="n example"; std::string str3="sample phrase"; std::string str4="useful."; // replace signatures used in the same order as described above: // Using positions: 0123456789*123456789*12345 std::string str=base; // "this is a test string." str.replace(9,5,str2); // "this is an example string." (1) str.replace(19,6,str3,7,6); // "this is an example phrase." (2) str.replace(8,10,"just a"); // "this is just a phrase." (3) str.replace(8,6,"a shorty",7); // "this is a short phrase." (4) str.replace(22,1,3,'!'); // "this is a short phrase!!!" (5) // Using iterators: 0123456789*123456789* str.replace(str.begin(),str.end()-3,str3); // "sample phrase!!!" (1) str.replace(str.begin(),str.begin()+6,"replace"); // "replace phrase!!!" (3) str.replace(str.begin()+8,str.begin()+14,"is coolness",7); // "replace is cool!!!" (4) str.replace(str.begin()+12,str.end()-4,4,'o'); // "replace is cooool!!!" (5) str.replace(str.begin()+11,str.end(),str4.begin(),str4.end());// "replace is useful." (6) std::cout << str << '\n'; return 0; }
根据上面的查找和修改可以轻松解决一个我们曾经遇到的问题,将日期"2023-11-23"中的'-'修改为'/'。
int main() { string str("2023-11-23"); cout << str << endl; size_t pos = str.find('-'); // '-'修改为'/' while (pos != string::npos) { str.replace(pos, 1, 1, '/'); pos = str.find('-'); } cout << str << endl; return 0; }
我们看一下我们的代码有什么问题没?我们发现我们每次找'-'都是从字符串的其实位置开始找,那么这也效率比较低,其实我们第一找到'-'后,得到第一次出现'-'的位置,后面再找'-'就可以从上次找的位置+1即可,这样效率就提高很多,但是replace的效率比较低,我们下面替换一个字符还好,但是如果替换成'///'时,此时就要往后挪动数据才能插入,这样效率比较低,所以replace我们能少用尽量就少用。
int main() { string str("2023-11-23"); cout << str << endl; size_t pos = str.find('-', 0); // '-'修改为'/' if (pos != string::npos) { str.replace(pos, 1, 1, '/'); pos = str.find('-', pos + 1); } cout << str << endl; return 0; }
所以这里我们有更好的方法,使用范围for+赋值。
int main() { string str("2023-11-23"); cout << str << endl; string str1; for (auto ch : str) { if (ch == '-') str1 += '/'; else str1 += ch; } cout << str1 << endl; str.swap(str1); return 0; }
运行结果:
这里有一个细节问题:我们上面使用的是C++ 标准库中 std::string
类的成员函数。
string::swap
是 C++ 标准库中 std::string
类的成员函数,用于交换两个字符串的内容。它是在字符串对象上调用的函数,例如:
std::string str1 = "Hello"; std::string str2 = "World"; str1.swap(str2); // 交换 str1 和 str2 的内容
而 swap
是一个通用的 C++ 函数,用于交换两个对象的值。对于字符串来说,可以使用 std::swap
或直接使用 swap
来交换两个字符串的内容,例如:
std::string str1 = "Hello"; std::string str2 = "World"; std::swap(str1, str2); // 交换 str1 和 str2 的内容 // 或者直接使用 swap swap(str1, str2);
主要区别在于调用方式和命名空间。string::swap 是 std::string 类的成员函数,而 swap 是一个通用的函数,可以在合适的作用域下直接使用或通过 std::swap 来调用。两者都可以用于交换字符串的内容。同时std::swap 参数是函数模板,使用的时候实例化,需要拷贝,消耗较大,而string::swap 是直接交换两个字符串的地址,后面实现string类的时候可以看到。
【编码艺术:掌握String类函数接口的妙用指南】(四):https://developer.aliyun.com/article/1425656