C++: string讲解 前序

目录

STL简介:

stl容器的头文件和std标准库的关系:

string的介绍:

<1>string类和char*转换:—————补充————————

(1)string转char*

(2)char*转string

<2>char*转int

(1)atoi:

(2)stoi

(3)strtol

<3>int转char*

———————————————————————————————

string类对象大小

string类对象的输入——getline(cin, a); 新增!

一.string类对象的常见构造函数

(2)单参数的构造函数支持隐式类型转换

二.string类对象的常见析构函数

三.string类对象的赋值 std::string::operator=

四.std::string::operator[]

1.有两个函数重载:

2.使用

【1】题目1:遍历对象的每一个字符,三种做法:

(1)用 std::string::operator[](解决“遍历对象的每一个字符”①)

(2)迭代器(解决“遍历对象的每一个字符”②)

(3)范围for—语法糖(解决“遍历对象的每一个字符”③)

【2】相关练习题目:917. 仅仅反转字母

(1)做法一:利用下标+[]   (已知物理空间是连续的,如果是链表就只能用迭代器)

(2)迭代器做法

3.std::string::operator[] 和 std::string::at

五.迭代器(共四种迭代器)

1.正向迭代器

(1)正向迭代器

2.反向迭代器

(1)反向迭代器

3.const正向迭代器 和 4. const反向迭代器

 六.string类中的其他用法

string类可以用==判断相等

1.几个小用法

2.std::string::reserve

3.std::string::resize

(1)例一

(2)例二

4.push_back; append  operator+=

5.插入数据: insert() 

题目:415. 字符串相加

6.删除数据: erase() 

7.交换对象:swap()

8.返回C格式字符串 c_str

9.std::string::find 和 rfind

(1)find

(2)rfind 从后往前取

10. std::string::substr

11.std::getline (string)

题目:HJ1 字符串最后一个单词的长度

12.std::string::find_first_of

题目:387. 字符串中的第一个唯一字符


STL简介:

1. 什么是 STL
STL(standard template libaray- 标准模板库 ) C++ 标准库的重要组成部分 ,不仅是一个可复用的组件库,而且 是一个包罗数据结构与算法的软件框架
2. STL 的版本
原始版本
Alexander Stepanov Meng Lee 在惠普实验室完成的原始版本,本着开源精神,他们声明允许任何人任意
运用、拷贝、修改、传播、商业使用这些代码,无需付费。唯一的条件就是也需要向原始版本一样做开源使
用。 HP 版本 -- 所有 STL 实现版本的始祖。
P. J. 版本
P. J. Plauger 开发,继承自 HP 版本,被 Windows Visual C++ 采用,不能公开或修改,缺陷:可读性比较低,
符号命名比较怪异。
RW 版本
Rouge Wage 公司开发,继承自 HP 版本,被 C+ + Builder 采用,不能公开或修改,可读性一般。
SGI 版本
Silicon Graphics Computer Systems Inc 公司开发,继承自 HP 版 本。被 GCC(Linux) 采用,可移植性好,
可公开、修改甚至贩卖,从命名风格和编程 风格上看,阅读性非常高。 我们后面学习 STL 要阅读部分源代码, 主要参考的就是这个版本。
3. STL 的六大组件

stl容器的头文件和std标准库的关系:

头文件:#include<string>   (不加.h,C语言中有string.h,防止和C语言的重复)<iostream>头文件除输入输入函数以外也包含<utility>和stl的所有容器的定义部分容器的声明部分容器的声明解释:有的容器在std标准库中有声明,有的stl容器在std标准库中没声明。例如string在std标准库中有声明,unordered_map在std标准库中无声明,即:<iostream>头文件包含了string的声明,但不包含unordered_map的声明,所以不加#include<string>,不加using namespace std;不放开标准库,直接用#include<iostream> ,只需要加上std这个域:std::string就可以正常使用,因为<iostream>头文件有string的声明;但是对于unordered_map,因为<iostream>不包含unordered_map的声明,所以你必须加上#include<unordered_map>里面有声明,才能正常使用unordered_map:

在加上<iostream>前提下,如果加#include<unordered_map>声明,但不加using namespace std; 不把标准库打开,就需要std::限定才能用否则不能用;如果加using namespace std; 打开了标准库,则std::加不加都可以。

若不加#include<unordered_map>,则没有声明,即使加上 std::限定 也用不了。

string的介绍:

typedef basic_string<char> string;  (string是一个类模板)

1.解释:Strings are objects that represent sequences of characters. (strings 是管理字符串的一个类)

2.作用:管理动态增长字符数组,这个字符串以\0结尾

3.底层:类似于下面这样:底层是new一个新空间,再拷贝常量字符串进去,为什么不能直接

_str=str  ? 这样做不行,他们指向同一块空间,都是指向常量字符串了,常量字符串无法实现增删查改

namespace bit
{
	template<class T>
	class basic_string
	{
	public:
		basic_string(const T* str)
		{
			// 开空间存储字符串,方便增删查改
			size_t len = strlen(str);
			_str = new T[len + 1];
			strcpy(_str, str);
		}

	private:
		const T* _str;
		size_t _size;
		size_t _capacity;
	};
}

 typedef basic_string<char> string;
 int main()
 {
	 string s("hello world");
 }

<1>string类和char*转换:—————补充————————

(1)string转char*

std::string str = "string";
const char *cstr = str.c_str();

(2)char*转string

可以直接赋值。

char *cstr = “hello”;

string sss;

sss = cstr;

<2>char*转int

(1)atoi:

(2)stoi

#include <cstdlib>
int main()
{
  //int a=atoi("123");
  int a=stoi("123");
  printf("%d\n",a);
  return 0;
}

 

(3)strtol

<3>int转char*

(1)to_string

———————————————————————————————

string类对象大小

 

string类对象的输入——getline(cin, a); 新增!

cin和cout写输入输出非常方便,但是用cin输入string字符串时,默认遇到空格回车制表符等空白字符即字符串输入结束。

#include <iostream>
#include <string>
using namespace std;

int main() {
	string a, b;
	cin >> a >> b;
	cout << "a:" << a << endl;
	cout << "b:" << b << endl;

	return 0;
}

/*
输入:
abc 123
输出:
a:abc
b:123
*/

但是遇到带空格字符的字符串就不是很友好了,则需要用到getline函数,getline()是遇回车符输入结束。

#include <iostream>
#include <string>
using namespace std;

int main() {
	string a, b;
	getline(cin, a);
	getline(cin, b);
	cout << "a:" << a << endl;
	cout << "b:" << b << endl;

	return 0;
}

/*
输入:
abc 123
123 abc
输出:
a:abc 123
b:123 abc
*/

一.string类对象的常见构造函数

(1)

1.string(); 是无参的构造函数。

2.string (const char* s); 是有参构造函数。

3.string (const string& str); 是拷贝构造函数。

4.string (const char* s, size_t n); 是用字符串s的前n个字符初始化

5.string(size_t n, char c);初始化成n个字符c

6.string (const string& str, size_t pos,size_ t len = npos);用string类型对象str的第pos个位置后的len个字符初始化(pos是position位置的缩写,缺省参数npos是 static const size_t npos = -1; 也就是正数里面的最大值,是4294967295:如果pos后面不够取,str太短了,那就取完为止,npos意思是把pos位置后面的取完)

#include<iostream>
#include<string>
using namespace std;
int main()
{
	string s1;                  // 1.string();    相当于string s1(""); 
	string s2("hello world");   // 2.string (const char* s); 
	string s3(s2);              // 3.string (const string& str); 
	string s4 = s2;             // 3.string (const string& str); 
	string s5("aaabbbcccddd",3);// 4.string (const char* s, size_t n);
    string s6(10, 'x');         // 5.string(size_t n, char c);
// 6.string (const string& str, size_t pos,size_ t len = npos);
    string s7(s2, 6, 3);       
	string s8(s2, 6, 100);

	cout << s1 << endl;
	cout << s2 << endl;
	cout << s3 << endl;
	cout << s4 << endl;
	cout << s5 << endl;
	cout << s6 << endl;
	cout << s7 << endl;
	cout << s8 << endl;
}

(2)单参数的构造函数支持隐式类型转换

string s="hello"; 这句,字符串"hello"会先进行隐式类型转换,直接构造一个string类型的"hello",然后这个string类的"hello"拷贝构造给s,但会被编译器优化成直接构造,先构造再拷贝构造——>通过编译器优化就成了直接构造

string s("hello");
string s="hello";

详情见:此文章的大标题三——>小标题2
(88条消息) C++:1.流插入与流提取的运算符重载,2.初始化列表,3.explicit关键字,4.静态成员变量与非静态成员变量用法 详解_beyond.myself的博客-CSDN博客_流插入流提取

.string类对象的常见析构函数

用途是清理动态资源,不用管,自动调用

三.string类对象的赋值 std::string::operator=

string& operator= (const string& str); 把string类对象赋值成另一个string类对象str(这个是最常用的)
string& operator= (const char* s); 把string类对象赋值成字符串s
string& operator= (char c);  把string类对象赋值成字符c

void test_string2()
{
	string s1("hello");
	string s2("xxx");

	s1 = s2;        //string& operator= (const string& str);
	s1 = "yyy";     //string& operator= (const char* s);
	s1 = "y";       //string& operator= (char c); 
}
int main()
{
	test_string2();
	return 0;
}

四.std::string::operator[]

s1[i] 就相当于 s1.operator[](i); string类对象s1的第i个字符

1.有两个函数重载:

char& operator[](size_t pos)        可读可写

const char& operator[](size_t pos) const          只读

底层逻辑:

​
namespace bit
{
	class string
	{
	public:
		// 值修改返回对象
		char& operator[](size_t pos)
		{
			assert(pos <= _size);    //检查是否越界

			return _str[pos];
		}

		const char& operator[](size_t pos) const
		{
			assert(pos <= _size);

			return _str[pos];
		}
	private:
		char* _str;
		size_t _size;
		size_t _capacity;
	};
}
void test_string4()
{    
	string s1("hello");          //调用可读可写
	const string s2("hello");    //调用只读

}
int main()
{
	test_string4();
	return 0;
}

2.使用

以前的内置类型[]:

    const char* s2 = "world";
    s2[i]; // *(s2+i)

string中的自定义类型用 std::string::operator[]:

【1】题目1:遍历对象的每一个字符,三种做法:

第一种方式,下标+[]

第二种方式,迭代器

第二种方式,范围for

( s1.size()是计算对象中的字符个数,不包含\0)

(1)用 std::string::operator[](解决“遍历对象的每一个字符”①)

void test_string3()
{
	// 遍历string的每一个字符
	string s1("hello");

	cout << s1.size() << endl;

	// 第一种方式,下标+[]
	for (size_t i = 0; i < s1.size(); i++)
	{
		// s1.operator[](i);
		cout << s1[i] << " ";
	}
	cout << endl;
}
int main()
{
	test_string3();
	return 0;
}

(2)迭代器(解决“遍历对象的每一个字符”②)

例1:遍历对象的每一个字符:第二种方式,迭代器

介绍:迭代器是什么? -像指针一样的东西或者就是指针(像指针一样的访问数据结构的东西)

各个类都有独自的迭代器 iterator ,所以要指定是哪个类的,string类中的迭代器写作

string: : iterator

s1.begin()返回第一个位置的指针(或者说第一个位置的迭代器)

s1.end()返回最后一个数据的下一个位置,下面这里的 s1.end() 就是\0的位置

void test_string3()
{
	// 遍历string的每一个字符
	string s1("hello");
	cout << s1.size() << endl;

	// 迭代器
	string::iterator it = s1.begin();    //it是s1的首元素地址
	while (it != s1.end())               //遍历打印,直到it为 \0 停止
	{            
		cout << *it << " ";
		++it;
	}
	cout << endl;
}
int main()
{
	test_string3();
	return 0;
}

小疑问:这里 while (it != s1.end()) 可以写成 while (it < s1.end())吗?

答:这里是可以,因为是顺序表,存储空间连续,如果像后面的链表形式存储空间不连续就只能用标准的 while (it != s1.end()) 。

例2:vector顺序表

#include<iostream>
#include<vector>
#include<string>
using namespace std;

void test_string3()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);

	vector<int>::iterator vit = v.begin();
	while (vit != v.end())
	{
		cout << *vit << " ";
		++vit;
	}
	cout << endl;
}
int main()
{
	test_string3();
	return 0;
}

(3)范围for—语法糖(解决“遍历对象的每一个字符”③)

范围for -- 底层:编译器替换成了迭代器 (这里简答了解一下)

void test_string3()
{
	// 遍历string的每一个字符
	string s1("hello");
	cout << s1.size() << endl;

	// 范围for -- 原理:替换成迭代器
	for (auto ch : s1)        //自动取s1中的数据赋值给ch,自动判断结束
	{
		cout << ch << " ";
	}
	cout << endl;
}
int main()
{
	test_string3();
	return 0;
}

【2】相关练习题目:917. 仅仅反转字母

给你一个字符串 s ,根据下述规则反转字符串:

所有非英文字母保留在原有位置。
所有英文字母(小写或大写)位置反转。
返回反转后的 s 。

示例 1:

输入:s = "ab-cd"
输出:"dc-ba"
示例 2:

输入:s = "a-bC-dEf-ghIj"
输出:"j-Ih-gfE-dCba"
示例 3:

输入:s = "Test1ng-Leet=code-Q!"
输出:"Qedo1ct-eeLg=ntse-T!"

(1)做法一:利用下标+[]   (已知物理空间是连续的,如果是链表就只能用迭代器)

class Solution {
public:
    bool isletter(char ch)
    {
        if(ch>='a'&&ch<='z')
            return true;
        else if(ch>='A'&&ch<='Z')
            return true;
        else
            return false;
    }
    string reverseOnlyLetters(string s) {
        int left=0,right=s.size()-1;
        while(left<right)
        {
           while(left<right && !isletter(s[left])) 
            left++;
           while(left<right && !isletter(s[right]))
            right--;
           swap(s[left],s[right]);
           left++;
           right--;
        }    
        return s;   
    }
};

(2)迭代器做法

class Solution {
public:
    bool isletter(char ch)
    {
        if(ch>='a'&&ch<='z')
            return true;
        else if(ch>='A'&&ch<='Z')
            return true;
        else
            return false;
    }
    string reverseOnlyLetters(string s) {
        string::iterator leftIt=s.begin();
        string::iterator rightIt=s.end()-1;    //end()是\0, s.end()-1 是最后一个字符
        while(leftIt<rightIt)
        {
           while(leftIt<rightIt && !isletter(*leftIt)) 
            leftIt++;
           while(leftIt<rightIt && !isletter(*rightIt))
            rightIt--;
           swap(*leftIt,*rightIt);
           leftIt++;
           rightIt--;
        }    
        return s;   
    }
};

3.std::string::operator[] 和 std::string::at

用法一样,处理方式不一样,越界时 operator[]通过断言抛异常,at抛越界异常

void test_string4()
{
	string s1("hello");
	const string s2("hello");
	s1[0];
	s1.at(0);

	s1[0] = 'x';
	//s2[0] = 'x';
	s1.at(0) = 'y';
}
int main()
{
	test_string4();
	return 0;
}

五.迭代器(共四种迭代器)

介绍:迭代器是什么? -像指针一样的东西或者就是指针

各个类都有独自的迭代器 iterator ,所以要指定是哪个类的,string类中的迭代器写作

string: : iterator

s1.begin()返回第一个位置的指针(或者说第一个位置的迭代器)

s1.end()返回最后一个数据的下一个位置

1.正向迭代器

(1)正向迭代器

还是以 题目1:遍历对象的每一个字 为例

void test_string5()
{
	// 遍历string的每一个字符
	string s1("hello");
	cout << s1.size() << endl;

	// 迭代器
	string::iterator it = s1.begin();    //it是s1的首元素地址
	while (it != s1.end())               //遍历打印,直到it为 \0 停止
	{            
		cout << *it << " ";
		++it;
	}
	cout << endl;
}
int main()
{
	test_string5();
	return 0;
}

2.反向迭代器

(1)反向迭代器

void test_string5()
{
	// 正向迭代器
	string s("hello world");
	string::iterator it = s.begin();
	while (it != s.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;

	// 反向迭代器
	string::reverse_iterator rit = s.rbegin();    // s.rbegin() 表示最后一个字母d的指针
	while (rit != s.rend())                       // s.rend() 表示首字母h的指针
	{
		cout << *rit << " ";
		++rit;                    //反向迭代器也是++
	}
	cout << endl;
}

int main()
{
	test_string5();
	return 0;
}

 

3.const正向迭代器 和 4. const反向迭代器

常规的正向迭代器和反向迭代器是可以修改string类对象的内容的:

void test_string5()
{
	// 正向迭代器
	string s("hello world");
	string::iterator it = s.begin();
	while (it != s.end())
	{
		(*it) += 1;            //修改string类对象的内容
		cout << *it << " ";
		++it;
	}
	cout << endl;
	cout << s << endl;

	// 反向迭代器
	string::reverse_iterator rit = s.rbegin();
	while (rit != s.rend())
	{
		(*rit) -= 1;            //把string类对象的内容改回去
		cout << *rit << " ";
		++rit;
	}
	cout << endl;
	cout << s << endl;
}

int main()
{
	test_string5();
	return 0;
}

const正向迭代器和const反向迭代器:

const正向迭代器:

此处调用只读类型的 const_iterator begin() const; 

只读类型的  const_iterator end() const;

const反向迭代器:

调用只读类型的 const_reverse_iterator begin() const; 

只读类型的  const_reverse_iterator rend() const;

void Func(const string& rs)        //传参时转为const类型
{
	string::const_iterator it = rs.begin(); 
	while (it != rs.end())
	{
		//*it += 1;		× 不可以修改
		cout << *it << " ";
		++it;
	}
	cout << endl;

	//string::const_reverse_iterator rit = rs.rbegin(); 前面类型太长可以考虑用auto
	auto rit = rs.rbegin();
	while (rit != rs.rend())
	{
		//(*rit) -= 1;	× 不可以修改
		cout << *rit << " ";
		++rit;
	}
	cout << endl;
}

void test_string6()
{
	// 正向迭代器
	string s("hello world");

	Func(s);
}

 六.string类中的其他用法

string类可以用==判断相等

#include<iostream>
using namespace std;
#include<string>
int main()
{
	string s1("hello");
	string s2("hello");
	if (s1 == s2)
	{
		cout << "相等";
	}
	return 0;
}

1.几个小用法

length() 和 size() 一样,都是显示对象中字符串长度,size常用(length是早起写法,不常用)

max_ size() 显示字符串最大能开多长,没什么用

capacity() 显示对象的总容量

 

2.std::string::reserve

 reserve:提前开好n字节空间,不一定是正好n个字节,为了对齐还要多开几个

利用s.push_back( ); 向对象中添加字符,每次容量满了就会扩容1.5倍,很麻烦,已知要添加1000个字符,所以用 reserve:提前开好1000字节空间

void test_string6()
{
	string s;

	size_t sz = s.capacity();
	cout << "making s grow:\n";
	cout << "capacity changed: " << sz << '\n';
	for (int i = 0; i < 1000; ++i)
	{
		s.push_back('c');
		if (sz != s.capacity())
		{
			sz = s.capacity();
			cout << "capacity changed: " << sz << '\n';
		}
	}

}
int main()
{
	test_string6();
	return 0;
}

 

 用 reserve:提前开好1000字节空间

void test_string6()
{
	string s;

	size_t sz1 = s.capacity();
	cout << "capacity changed: " << sz1 << '\n';  //查看初始容量
	s.reserve(1000);                //提前开好1000字节空间
	size_t sz = s.capacity();
	cout << "making s grow:\n";
	cout << "capacity changed: " << sz << '\n';    //查看reserve后的容量
	for (int i = 0; i < 1000; ++i)
	{
		s.push_back('c');
		if (sz != s.capacity())
		{
			sz = s.capacity();
			cout << "capacity changed: " << sz << '\n';
		}
	}

}
int main()
{
	test_string6();
	return 0;
}

3.std::string::resize

resize是开空间并初始化

(1)例一

void test_string6()
{
	string s1;
	string s2;
	//s.reserve(1000); // 扩空间
	s1.resize(1000); // 扩空间+初始化
	s2.resize(1000, 'x'); // 扩空间+初始化

}
int main()
{
	test_string6();
	return 0;
}

(2)例二

VS下:reverse不能缩小容量;resize也不能缩小容量,但能去掉前10个后面所有的内容

void test_string6()
{
	string s1;
	string s2;
	s1.resize(100, 'c');
	s2.resize(100, 'x');
	s1.reserve(10);
	s2.resize(10);

}
int main()
{
	test_string6();
	return 0;
}

 

4.push_back; append  operator+=

插入一个字符用push_back;插入字符串用append;最好用的还是+=

void test_string6()
{
	string s;
	s.push_back('x');
	s.append("hello");
	string str("world");
	s.append(str);
	cout << s << endl;

	s += 'x';
	s += "hello";
	s += str;
	cout << s << endl;
}
int main()
{
	test_string6();
	return 0;
}

 

 

 +=:

void test_string8()
{
	string s("hello");
	s += " ";
	s += "world";
	cout << s << endl;
}
int main()
{
	test_string8();
	return 0;
}

5.插入数据: insert() 

void test_string8()
{
	string s("hello");
	s.insert(0, 1, 'a');        //在第0个位置插入一个字符a
	s.insert(s.begin(), 'b');   //在第0个位置插入一个字符a
	cout << s << endl;

	s.insert(3, 1, 'x');
	s.insert(s.begin()+3, 'y');    //里面放迭代器
	cout << s << endl;
}
int main()
{
	test_string8();
	return 0;
}

insert 如果头插的话,第二次头插数据把后面1个挪动1次,第三次头插数据把后面2个挪动1次,……,第n次头插数据把后面n-1个挪动1次,等差数列相加后时间复杂度是O(N²),不如使用+=后逆置reverse( retStr.begin(), retStr.end()); 可以使时间复杂度降低到O(N) :

题目:415. 字符串相加

class Solution {
public:
    string addStrings(string num1, string num2) {
        int end1=num1.size()-1;
        int end2=num2.size()-1;
        int carry=0;
        string retStr;
        while(end1>=0||end2>=0)
        {
            int val1= end1>=0?num1[end1]-'0':0;
            int val2= end2>=0?num2[end2]-'0':0;
            int ret=val1+val2+carry;
            if(ret>9)
            {
                carry=1;
                ret-=10;
            }
            else
                carry=0;
            retStr+=ret+'0';  //这里如果用retStr.insert(retStr.begin(),ret+'0')效率低
            end1--;
            end2--;
        }
        if(carry==1)
        {
            retStr+='1';
        }
        reverse( retStr.begin(), retStr.end());
        return retStr;
    }
};

6.删除数据: erase() 

void test_string8()
{
	string s("hello world");
	cout << s << endl;

	s.erase(s.begin());    //删除迭代器所在位置的字符,删除首字符
	cout << s << endl;

	s.erase(s.begin() + 3);    //删除第3个字符
	cout << s << endl;

	s.erase(3, 2);        //删除包括第3个字符开始后面的2个字符
	cout << s << endl;

	//s.erase(3);         //删除包括第3个字符开始后面所有值,因为缺省值npos是最大正数
	s.erase(3, 100);      //超出就有多少删多少
	cout << s << endl;
}
int main()
{
	test_string8();
	return 0;
}

当用第三种时,删除区间是左闭右开

void test_string14()
{
	string s("hello world");
	cout << s << endl;

	s.erase(s.begin(),s.begin()+2);    
	cout << s << endl;

}
int main()
{
	test_string14();
	return 0;
}

 

7.交换对象:swap()

void test_string9()
{
	string s1("hello world");
	string s2("string");

	// C++98
	s1.swap(s2);   // 效率高:直接交换指针
	swap(s1, s2);  // 效率低:库中的交换函数,是深拷贝交换
}
int main()
{
	test_string9();
	return 0;
}

STL中的swap函数底层是深拷贝交换:

8.返回C格式字符串 c_str

const char* c_str() const;

获取C字符串等效
返回指向数组的指针,该数组包含以空结尾的字符序列(即C字符串),表示字符串对象的当前值。

此数组包含构成字符串对象值的相同字符序列,最后添加一个终止空字符(“\0”)。

    string s1("hello world");
	cout << s1 << endl;
	cout << s1.c_str() << endl;

注意:他返回的是指针,例如下面的对象a和b就算值一样,但他们的返回值是指针,即a.c_str()==b.c_str()比较的是存储字符串位置的地址,不相同,a.c_str() == b.c_str()为假。


int main(int argc, char* argv[])
{
	string a = "hello world";
	string b = a;
	if (a.c_str() == b.c_str())
	{
		cout << "true" << endl;
	}
	else cout << "false" << endl;
	return 0;
}

9.std::string::find 和 rfind

(1)find

从字符串 pos 位置开始往后找字符 c ,返回该字符在字符串中的位置
pos缺省值是0:默认从0开始找字符c

 返回找的第一个字符的起始位置,找不到就返回npos(npos=42亿9千万...)

file.find('.');   对应第四种用法:size_t find (char c, size_t pos = 0) const; ,'.'对应char c,size_t pos = 0 缺省值是0说明默认从0开始找字符c

void test_string10()
{
	// 要求取出文件的后缀
	string file("string.cpp");

	size_t pos = file.find('.');    //size_t find (char c, size_t pos = 0) const;

	if (pos != string::npos)
	{
		//string suffix = file.substr(pos, file.size() - pos);    
		string suffix = file.substr(pos);    //和下面一样,都是取完后面所有的字符

		cout << file << "后缀:" << suffix << endl;
	}
	else
	{
		cout << "没有后缀" << endl;
	}
}
int main()
{
	test_string10();
	return 0;
}

(2)rfind 从后往前取

void test_string10()
{
	string file("string.c.tar.zip");
	size_t pos = file.rfind('.');
	if (pos != string::npos)
	{
		string suffix = file.substr(pos);
		//string suffix = file.substr(pos,file.size()-pos);
		cout << file << "后缀:" << suffix << endl;
	}
	else
	{
		cout << "没有后缀" << endl;
	}
}
int main()
{
	test_string10();
	return 0;
}

10. std::string::substr

string substr (size_t pos = 0, size_t len = npos) const;

从pos位置往后取(包括pos位置字符)len个字符,返回取出的这一串string类型的字符。默认缺省参数npos是最大正值。

int main()
{
	std::string str("Please, replace the vowels in this sentence by asterisks.");
	std::cout << str.substr(6,8) << '\n';
	return 0;
}

 

void test_string11()
{
	// 取出url中的域名
	string url1("http://www.cplusplus.com/reference/string/string/find/");
	string url2("https://leetcode.cn/problems/design-skiplist/solution/tiao-biao-probabilistic-alternative-to-b-0cd8/");

	string& url = url1;

	// 协议 域名 uri
	string protocol;
	size_t pos1 = url.find("://");
	if (pos1 != string::npos)
	{
		protocol = url.substr(0, pos1);
		cout << "protocol:" << protocol << endl;
	}
	else
	{
		cout << "非法url" << endl;
	}

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

	string uri = url.substr(pos2 + 1);
	cout << "uri:" << uri << endl;
}
int main()
{
	test_string11();
	return 0;
}

11.std::getline (string)

 用法是:cin遇到空格或换行就会停下,getline只认换行,遇到空格不会停

题目:HJ1 字符串最后一个单词的长度

 

#include <iostream>
#include <string>
using namespace std;

int main(){
    string str;
    getline(cin,str);            //getline用法
    size_t pos = str.rfind(' ');
    cout << str.size()-pos-1;
    return 0;
}

12.std::string::find_first_of

在string类对象中找到第一个出现的字符c,返回此字符的下标值。

int main()
{
	std::string str("Please, replace the vowels in this sentence by asterisks.");
	std::size_t found = str.find_first_of("e");
	int a = 4;
	while (a--)
	{
		str[found] = '*';
		found = str.find_first_of("e", found + 1);
	}

	std::cout << str << '\n';

	return 0;
}

如果找字符串的话,找到包含字符中出现的第一个,并返回下标值

int main()
{
	std::string str("Please, replace the vowels in this sentence by asterisks.");
	std::size_t found = str.find_first_of("aeiou");
	int a = 4;
	while (a--)
	{
		str[found] = '*';
		found = str.find_first_of("aeiou", found + 1);
	}

	std::cout << str << '\n';

	return 0;
}

题目:387. 字符串中的第一个唯一字符

class Solution {
public:
    int firstUniqChar(string s) {
       int count[26]={0};
       for(char ch:s)
       {
           count[ch-'a']++;
       }
       for(int i=0;i<s.size();i++)
       {
           if(count[s[i]-'a']==1)
           return i;
       }
       return -1;
    }
};

  • 14
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 13
    评论
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值