C++库函数——string类

目录

1. 简介

①什么是string类

②string类的作用

2. string类的常见接口

①string类对象的常见构造

②string类对象的容量操作

⑴size、length、 capacity

⑵reserve、resize

⑶clear、shrink_to_fit

③string类对象的访问及遍历操作

⑴使用[]

⑵使用迭代器iterator

⑶使用范围for

④string类对象的修改操作

⑴+=、append、push_back与insert

⑵assign、erase、replace与pop_back

⑶c_str、substr

⑷find、rfind、find_first_of、find_last_of、find_first_not_of、find_last_not_of

⑤string类的非成员函数

⑴operator+

⑵relational operators

⑶operator<<、operator>>

⑷getline


1. 简介

①什么是string类

源文档

译:

1. 字符串是表示字符序列的类
2. 标准的字符串类提供了对此类对象的支持,其接口类似于标准字符容器的接口,但添加了专门用于操作单字节字符字符串的设计特性。
3. string 类是使用 char( 即作为它的字符类型,使用它的默认 char_traits 和分配器类型 ( 关于模板的更多信息,请参阅basic_string)
4. string 类是 basic_string 模板类的一个实例,它使用 char 来实例化 basic_string 模板类,并用 char_traits和allocator 作为 basic_string 的默认参数 ( 根于更多的模板信息请参考 basic_string)
5. 注意,这个类独立于所使用的编码来处理字节 : 如果用来处理多字节或变长字符 ( UTF-8) 的序列,这个类的所有成员( 如长度或大小 ) 以及它的迭代器,将仍然按照字节 ( 而不是实际编码的字符 ) 来操作。

②string类的作用

string类是一个用于处理字符串的类,它可以存储任意数目的字符。它可以用来表示文本或其他字符串数据,例如:文本文件内容、网络传输的数据、数据库中的文本列、用户输入或输出等等。

string类的作用有以下几个方面:

  1. 存储和处理文本数据:string类可以存储和操作任意数目的字符,可以处理文本数据,如搜索、替换、截取子串等操作。

  2. 方便字符串操作:使用string类可以方便地进行字符串操作,如连接(concatenate)两个字符串、删除(erase)字符串中的一些字符、复制(copy)字符串等。

  3. 兼容性:string类是C++标准库的一部分,可用于在不同的计算机和操作系统之间进行可靠的代码交换。

2. string类的常见接口

①string类对象的常见构造

源文档

在这里我们只对部分作讲解 ,以如下代码为例

int main()
{
	string s1;// 创建一个没有初始值的字符串s1
	string s2("张三");// 创建一个以"张三"为初始值的字符串s2
	string s3("hello world");// 同上
	string s4(10, '*');// 创建一个s4字符串并以10个'*'初始化它
	string s5(s2);// 拷贝构造一个s5字符串

    string s6(s3, 6, 5);// 将s3中第6个位置的及之后的5个字符串拷贝构造到s6中

    string s7(s3, 6);// 将s3中第6个位置及之后的所有字符串(这里的nops为一个极大的无符号整形数,其为size_t nops = -1)拷贝构造到s7中

    string s8(s3, 6, 100);// 类似于s6,是将s3中第6个位置的及之后的100个字符串拷贝构造到s8中

    return 0;
}

分别打印它们有

②string类对象的容量操作

源文档

同样的这里只对部分函数作讲解

⑴size、length、 capacity

源文档如下

测试代码如下

int main()
{
	string s("hello, world");

	cout << s.size() << endl;// 显示字符串的有效长度
	cout << s.length() << endl;// 同上
	cout << s.capacity() << endl;// 显示字符串的容量
	cout << s << endl;
}

运行如下

⑵reserve、resize

原文档如下

测试代码如下

int main()
{
    string str = "hello world";
    cout << "Str: " << str << endl;
    cout << "Size: " << str.size() << endl;
    cout << "Capacity: " << str.capacity() << endl;
    cout << endl;

    str.resize(5);// 将str的长度改为5,超过5的部分全部被截断
    cout << "Resized: " << str << endl;
    cout << "Size: " << str.size() << endl;
    cout << "Capacity: " << str.capacity() << endl;
    cout << endl;

    str.resize(15,'*');// 将str的长度改为15,没有字符的部分全部用'*'填充
    cout << "Resized: " << str << endl;
    cout << "Size: " << str.size() << endl;
    cout << "Capacity: " << str.capacity() << endl;
    cout << endl;

    str.reserve(20);// 将capacity预置为20,不对size更改
    cout << "Reserved: " << str << endl;
    cout << "Size: " << str.size() << endl;
    cout << "Capacity: " << str.capacity() << endl;
    cout << endl;

    str.reserve(10);// 将capacity预置为小于size时,编译器可能不会对其作出更改
    cout << "Reserved: " << str << endl;
    cout << "Size: " << str.size() << endl;
    cout << "Capacity: " << str.capacity() << endl;
    cout << endl;

    return 0;
}

运行有

⑶clear、shrink_to_fit

测试代码

int main()
{
    string str = "hello world";
    cout << "Str: " << str << endl;
    cout << "Size: " << str.size() << endl;
    cout << "Capacity: " << str.capacity() << endl;
    cout << endl;

    // 清空字符串(将size置为0),但不改变容量大小
    str.clear();
    cout << "Cleared: " << str << endl;
    cout << "Size: " << str.size() << endl;
    cout << "Capacity: " << str.capacity() << endl;
    cout << endl;

    // 提出将capacity缩小到适应大小的请求,最后是否缩小由编译器确定(与resever类似)
    str.shrink_to_fit();
    cout << "Capacity after shrink: " << str.capacity() << endl;

    return 0;
}

 运行有

③string类对象的访问及遍历操作

⑴使用[]

源文档

 我们使用以下代码进行举例

int main()
{
	string s1("hello world");
	const string s2("Hello World");
	cout << s1 << " " << s2 << endl;
	cout << s1[0] << " " << s2[0] << endl;

	s1[0] = 'H';
	cout << s1 << endl;

	// s2[0] = 'h';   代码编译失败,因为const类型对象不能修改

	//使用时,可以同时改变字符串的内容
	for (size_t i = 0; i < s1.size(); ++i)
	{
		cout << s1[i] << endl;
	}

	return 0;
}

 运行有

⑵使用迭代器iterator

迭代器是什么?

  C++迭代器(Iterator)是一种对象,它允许遍历容器中的元素。迭代器提供了对容器元素的访问和操作,使程序员能够对容器进行遍历、查找、修改等操作。

  而迭代器本质上是一个泛型指针,它实现了对指向容器元素的指针的抽象,使得我们可以在不了解容器内部细节的情况下对其进行访问和操作。当我们需要遍历、查找或修改容器中的元素时,迭代器为我们提供了一种统一的接口,使得我们可以将不同类型的容器当作同一类型来处理。

迭代器源文档

我们以如下代码举例

int main()
{
	string s("hello world");
	string::iterator it = s.begin();
	while (it != s.end())
	{
		//也可以通过解引用来改变字符串内容
		(*it)++;
		cout << *it << " ";
		++it;
	}

	cout << endl;

	// 倒着遍历字符串
	string::reverse_iterator rit = s.rbegin();
	// C++11之后,直接使用auto定义迭代器,让编译器推到迭代器的类型
	// 即可以使用auto rit = s.rbegin();
	while (rit != s.rend())
	{
		(*rit)--;
		cout << *rit << " ";
	}

	return 0;
}

 运行有

对于一些只读的字符串,需要使用const修饰,对应的begin()等也要对于更换为const形式,部分源文档如下

void func(const string& s)
{
    // 对于const常量字符串
    // 只能使用const修饰的迭代器,以防造成权限的放大
	string::const_iterator it = s.begin(); 
	while (it != s.end())
	{
		// it += 2;
		cout <<*it <<" ";
		++it;
	}
	cout <<endl;

	// string::const_reverse_iterator rit = s.rbegin( );
    // 可以使用auto来自动识别
	auto rit = s.rbegin();
	while (rit != s.rend( ))
	{
		// (*rit) += 3;
		cout <<*rit <<" ";
		++rit;
	}
}

⑶使用范围for

以如下代码为例

int main()
{
	string s = "hello world";
	for (auto& ch : s)
	{
		ch += 1;
		cout << ch << " ";
	}

	return 0;
}

运行有

④string类对象的修改操作

源文档

⑴+=、append、push_back与insert

测试代码

int main()
{
    string str = "hello ";
    cout << "str:" << str << endl;
    cout << "strSize: " << str.size() << endl;
    cout << "strCapacity: " << str.capacity() << endl;

    str += "world";// 向str追加"world"
    str += '!';
    cout << "str:" << str << endl;
    cout << "strSize: " << str.size() << endl;
    cout << "strCapacity: " << str.capacity() << endl;
    cout << endl;

    string str1 = "hello ";
    cout << "str1:" << str1 << endl;
    cout << "str1Size: " << str1.size() << endl;
    cout << "str1Capacity: " << str1.capacity() << endl;

    str1.append("world", 3);// 向str1后追加"world"的前3个字符
    cout << "str1:" << str1 << endl;

    str1.append(2, '!');// 向str1后追加两个'!'
    cout << "str1:" << str1 << endl;
    cout << "str1Size: " << str1.size() << endl;
    cout << "str1Capacity: " << str1.capacity() << endl;
    cout << endl;

    string str2 = "hello ";
    cout << "str2:" << str2 << endl;
    cout << "str2Size: " << str2.size() << endl;
    cout << "str2Capacity: " << str2.capacity() << endl;

    // 遍历字符串"world",并将每个字符尾插到str2中
    for (char c : "world")
    {
        str2.push_back(c);
    }
    cout << "str2:" << str2 << endl;
    cout << "str2Size: " << str2.size() << endl;
    cout << "str2Capacity: " << str2.capacity() << endl;
    cout << endl;

    string str3 = "hello ";
    cout << "str3:" << str3 << endl;
    cout << "str3Size: " << str3.size() << endl;
    cout << "str3Capacity: " << str3.capacity() << endl;

    // 在str3的第6个位置插入字符串"world"
    str3.insert(6, "world");
    cout << "str3:" << str3 << endl;

    // 在str3的最后的位置插入"!"
    str3.insert(str3.size(), "!");
    cout << "str3:" << str3 << endl;
    cout << "str3Size: " << str3.size() << endl;
    cout << "str3Capacity: " << str3.capacity() << endl;


    return 0;
}

运行有

在插入数据时,会设计到扩容的问题, 实际上在不同的环境下扩容的方式各不相同

int main()
{
	string s1("hello world"); 
	cout << s1.size() << endl;
	cout << "初始容量:" << s1.capacity() << endl;

	size_t old = s1.capacity();
	for (size_t i = 0; i < 100; i++) 
	{
		s1 += 'x';
		if (old != s1.capacity()) 
		{
			cout << "扩容:" << s1.capacity() << endl; old = s1.capacity();
		}
	}

	return 0;
}

对于这样一段代码,在g++和vs两种环境下有着不同的结果,即

g++:

vs:

通过观察我们发现在g++中string使用的是2倍扩容,在vs中使用的是1.5倍扩容 。

⑵assign、erase、replace与pop_back

源文档

测试代码

int main()
{
    string str = "hello world";
    string str1, str2, str3, str4;

    // 将str对象的从下标为0到下标为5的字符拷贝构造到str1上
    str1.assign(str, 0, 5);
    cout << "str1:" << str1 << endl;

    str2 = str;
    // 将str2对象的从下标为6的位置往后的5个字符删除
    str2.erase(6, 5);
    cout << "str2:" << str2 << endl;

    str3 = str;
    // 将str3对象的从下标为2的位置往后的5个字符替换为"*****"
    str3.replace(2, 5, "*****");
    cout << "str3:" << str3 << endl;

    str4 = str;
    // 尾删str4
    str4.pop_back();
    cout << "str4:" << str4 << endl;

    return 0;
}

运行有

⑶c_str、substr

源文档

测试代码

int main()
{
    string str = "hello world";

    // 将string类对象转化成const char* 的C语言字符类型
    const char* cstr = str.c_str();
    cout << "cstr:" << cstr << endl;

    // 将str从下标为6向后5个字符拷贝构造到sub中
    string sub = str.substr(6, 5);
    cout << "sub:" << sub << endl;

    return 0;
}

运行有

⑷find、rfind、find_first_of、find_last_of、find_first_not_of、find_last_not_of

源文档

测试代码

int main()
{
    string str = "hello world";

    // 从头开始查找字符 l
    size_t pos1 = str.find('l');
    cout << "pos1:" << pos1 << endl;

    // 从尾开始查找字符 l
    size_t pos2 = str.rfind('l');
    cout << "pos2:" << pos2 << endl;

    // 从前往后查找"world"中任意一个字符(即'w'、'o'、'r'、'l'、'd')第一次出现的位置
    size_t pos3 = str.find_first_of("world");
    cout << "pos3:" << pos3 << endl;

    // 从后往前查找"world"中任意一个字符(即'w'、'o'、'r'、'l'、'd')第一次出现的位置
    size_t pos4 = str.find_last_of("world");
    cout << "pos4:" << pos4 << endl;

    // 从前往后查找不在"helord"中任意一个字符(即'h'、'e'、'l'、'o'、'r'、'd')第一次出现的位置
    size_t pos5 = str.find_first_not_of("helord");
    cout << "pos5:" << pos5 << endl;

    // 从后往前查找不在"helord"中任意一个字符(即'h'、'e'、'l'、'o'、'r'、'd')第一次出现的位置
    size_t pos6 = str.find_last_not_of("helord");
    cout << "pos6:" << pos6 << endl;

    return 0;
}

运行有

 在这里再举一个实际运用的例子

int main()
{
	//分别获得网络的协议、域名和资源名
	// string url = "ftp://www.baidu.com/?tn=65081411_1_oem_dg";
	// https://gitee.com/xuanxuan-qujiu-pavilion
	string url = "https://zh.wikipedia.org/wiki/Wikipedia:%E9%A6%96%E9%A1%B5";
	// 协议  域名  资源名
	size_t pos1 = url.find("://");
	string protocol;
	if (pos1 != string::npos)
	{
		protocol = url.substr(0, pos1);
	}
	cout << protocol << endl;

	string domain;
	string uri;

	size_t pos2 = url.find('/', pos1 + 3);
	if (pos2 != string::npos)
	{
		domain = url.substr(pos1 + 3, pos2 - (pos1 + 3));
		uri = url.substr(pos2 + 1);
	}
	cout << domain << endl;
	cout << uri << endl;

	return 0;
}

⑤string类的非成员函数

源文档

依旧仅对部分函数举例

⑴operator+

源文档

测试代码

int main()
{
    string str1 = "hello";
    string str2 = " world";

    // 将str1和str2拼接起来拷贝构造给str3
    string str3 = str1 + str2;
    cout << "str3:" << str3 << endl;

    // 将一个字符串和str1拼接再拷贝构造给str4
    string str4 = "I am " + str1;
    cout << "str4:" << str4 << endl;

    return 0;
}

运行有

⑵relational operators

测试代码

int main()
{
    string str1 = "abc";
    string str2 = "Abc";
    string str3 = "xyz";

    if (str1 == str2)
        cout << "str1 == str2" << endl;
    else
        cout << "str1 != str2" << endl;

    if (str1 != str3)
        cout << "str1 != str3" << endl;
    else
        cout << "str1 == str3" << endl;

    if (str1 < str3)
        cout << "str1 < str3" << endl;
    else
        cout << "str1 >= str3" << endl;

    if (str2 > str3)
        cout << "str2 > str3" << endl;
    else
        cout << "str2 <= str3" << endl;

    return 0;
}

运行有

⑶operator<<、operator>>

 测试代码

int main()
{
    string str;

    cout << "Please enter a string: ";
    cin >> str;

    cout << "You entered: " << str << endl;

    return 0;
}

运行有

我们可以发现,输出的只有hello这一个字符串,这是因为cin是读取到' '或'\0'就算作读取一次,因此就需要下面这个函数 

⑷getline

测试代码

int main()
{
    string str;

    cout << "Please enter a string: ";
    getline(cin, str);

    cout << "You entered: " << str << endl;

    return 0;
}

运行有