string的概念
string是标准库的字符序列容器,使用string需要包含头文件<string>。
在使用迭代器时需要额外添加头文件:<functional>,<algorithm>。
string常用的迭代器:
string的用法示例
'string 变量可以直接通过赋值操作符 = 进行赋值'
#include <iostream>
#include <string>
using namespace std;
int main(){
string s1;
string s2 = "c plus plus";
string s3 = s2;
string s4 (5, 's');
return 0;
}
变量 s1 只是定义但没有初始化,编译器会将默认值赋给 s1,默认值是"",也即空字符串。
变量 s2 在定义的同时被初始化为"c plus plus"。与C风格的字符串不同,string 的结尾没有结束标志'\0'。
变量 s3 在定义的时候直接用 s2 进行初始化,因此 s3 的内容也是"c plus plus"。
变量 s4 被初始化为由 5 个's'字符组成的字符串,也就是"sssss"。
获取字符串长度
string s = "studying in mayi";
int len = s.length();
cout << len << endl;
//输出:16,因为string的末尾没有“\0”字符,所有返回的是真实长度,没有+1.(空格也算长度)
转换为C风格的字符串
//打开文件时的路径,需要用到C风格的字符串,
//string 类为我们提供了一个转换函数c_str(),
//该函数能够将 string 字符串转换为C风格的字符串,并返回该字符串的 const 指针(const char*)。
string path = "D:\\demo.txt";
FILE *fp = fopen(path.c_str(), "rt");
//为了使用C语言中的 fopen() 函数打开文件,必须将 string 字符串转换为C风格的字符串。
字符串的拼接
string类对常用的运算符进行了重载,在对字符进行操作时,就会异常方便;
#include <string>
using namespace std;
int main(){
string s1 = "first ";
string s2 = "second ";
char *s3 = "third ";
char s4[] = "fourth ";
char ch = '@';
string s5 = s1 + s2;
string s6 = s1 + s3;
string s7 = s1 + s4;
string s8 = s1 + ch;
cout << s5 << endl
<< s6 << endl
<< s7 << endl
<< s8 << endl;
return 0;
}
输出:first second
first third
first fourth
first @
这里有一个通用的截取字符串并保存或者输出的方法,这里用到了序列式容器vector,之后会继续更新它的用法;
//分割字符串,通用法
void split(const string & str,const string & sp, vector<string> &arr) {
size_t left = 0;
size_t pos = 0;
while ((pos = str.find(sp, left)) != string::npos)//sp为标记字符串,left为开始下标
{
arr.push_back(str.substr(left, pos - left));//left为起始下标,pos-left为提取长度
left = pos + sp.size();
}
if (left < str.size()) {//还未结束,要将剩余的读完
arr.push_back(str.substr(left));
}
}
排序函数
#include <algorithm> //需要头文件
int main4() {
string s1 = "sdfASDMKvgp";
string s4 = "878463129";
sort(s4.begin(), s4.end());
cout << s4 << endl;
sort(s1.rbegin(), s1.rend());
cout << s1 << endl;
sort(s4.begin(), s4.end(), greater<char>()); //指定排序方向从大到小
cout << s4 << endl;
sort(s1.begin(), s1.end(), [](char a, char b) //用lambda表达式进行排序,俗称匿名函数
{
return a < b;
});
cout << s1 << endl;
return 0;
}
手撕string类的实现
string类还是比较好实现的,底层就是用char*型指针进行频繁的动态申请内存空间并释放;
头文件:String.h
//String.h 头文件
#include <iostream>
#include <exception>
using namespace std;
class String
{
public:
String();//无参构造函数
String(const char *str);//有参构造函数
~String();//析构函数
String(const String &other);//深拷贝构造函数
char * c_Str() const;//转为C风格字符串
char At(const int index) const;//下标访问函数
char Front() const;//访问首字符函数
char Back() const;//访问尾字符函数
int Size() const;//获取长度
bool Empty() const;//判空函数
void Erase(const int index);//删除指定下标元素
void Clear();//清空函数
void pop_Back();//删除尾元素
String Substr(const int index, int count) const;//截断字符串
String operator = (const String &other);//重载=
String &operator += (const char *str);//重载+=字符串
String &operator += (const String &obj);//重载+=对象
String operator + (const char *str) const;//重载+字符串
String operator + (const String &obj) const;//重载+对象
String operator * (const int num) const;//重载*
bool operator != (const String &obj) const;//重载!=
bool operator == (const String &obj) const;//重载==
char operator [] (int index);//重载[],可修改
const char operator [] (const int index) const;重载[],均不可修改
friend istream &operator >> (istream &input, String &obj);//重载流输入>>
friend ostream &operator << (ostream &output, const String &obj);//重载流输出<<
private:
char *m_str;
};
源文件:String.cpp
//String.cpp 源文件
#include "String.h"
String::String()
{
m_str = nullptr;
}
String::String(const char *str)
{
if (str) {
int length = strlen(str) + 1;
m_str = new char[length];
strcpy_s(m_str, length, str);
//strcpy_s拷贝函数,参数是目标字符串地址,目标字符串长度,源字符串地址
}
else
{
m_str = nullptr;
}
}
String::~String()
{
if (m_str) {
delete[] m_str;
}
}
String::String(const String & other) : String(other.m_str)
{
}
//根据需求,添加const关键字,使之成为常成员函数,防止函数内部修改对象
char * String::c_Str() const
{
char *str = new char[strlen(m_str) + 1];
strcpy_s(str, strlen(m_str) + 1, m_str);
return str;
}
//根据需求,在适当的地方进行异常处理
char String::At(const int index) const
{
if (index < 0 || index > Size()) {
throw logic_error("下标异常!");
}
return m_str[index];
}
char String::Front() const
{
if (Empty()) {
throw logic_error("字符串为空!");
}
return m_str[0];
}
char String::Back() const
{
if (Empty()) {
throw logic_error("字符串为空!");
}
return m_str[strlen(m_str) - 1];
}
int String::Size() const
{
return strlen(m_str);
}
bool String::Empty() const
{
return m_str == nullptr;
}
void String::Erase(const int index)
{
if (index < 0 || index > Size()) {
throw logic_error("下标异常!");
}
memmove(m_str + index, m_str + index + 1, strlen(m_str) - index);
//此函数允许有重叠的部分
//m_str + index表示目标内存的起始地址,m_str + index + 1是源内存起始地址,strlen(m_str) - index要复制的字节数
}
void String::Clear()
{
if (m_str) {
delete[] m_str;
m_str = nullptr;
}
else
{
throw logic_error("清空失败!");
}
}
void String::pop_Back()
{
if (m_str == nullptr) {
return;
}
int length = strlen(m_str);
m_str[length - 1] = '\0';
}
String String::Substr(const int index, int count) const
{
String temp;
int length = Size();
if (index >= length) {
return temp;
}
if (index + count > length) {
count = length - index;
}
temp.m_str = new char[count + 1];
strncpy_s(temp.m_str, count + 1, m_str + index, count);
//m_str + index表示偏移后的地址,count表示要拷贝的字符数
return temp;
}
String String::operator=(const String & other)
{
if (m_str) {
delete[] m_str;
}
m_str = nullptr;
if (other.m_str) {
int length = strlen(m_str) + 1;
m_str = new char[length];
strcpy_s(m_str, length, other.m_str);
}
return *this;
}
//对象和字符串都要考虑到
//思考何时需要返回引用
String & String::operator+=(const char * str)
{
if (str == nullptr) {
return *this;
}
int newlen = strlen(str) + 1;
if (m_str) {
newlen += strlen(m_str);
}
char *temp = new char[newlen];
temp[0] = '\0';
if (m_str) {
strcpy_s(temp, newlen, m_str);
delete[] m_str;
}
strcat_s(temp, newlen, str);
m_str = temp;
return *this;
}
String & String::operator+=(const String & obj)
{
return (*this) += obj.m_str;
}
String String::operator+(const char * str) const
{
String temp = *this;
temp += str;
return temp;
}
String String::operator+(const String & obj) const
{
String temp = *this;
temp += obj.m_str;
return temp;
}
String String::operator*(const int num) const
{
String temp;
for (int i = 0; i < num; ++i) {
temp += m_str;
}
return temp;
}
bool String::operator!=(const String & obj) const
{
if (strlen(obj.m_str) != strlen(this->m_str)) {
return true;
}
for (size_t i = 0; i < strlen(m_str); ++i) {
if (this->m_str[i] != obj.m_str[i]) {
return true;
}
}
return false;
}
bool String::operator==(const String & obj) const
{
if (strlen(obj.m_str) != strlen(this->m_str)) {
return false;
}
for (size_t i = 0; i < strlen(m_str); ++i) {
if (this->m_str[i] != obj.m_str[i]) {
return false;
}
}
return true;
}
//根据需求,添加const关键字,使返回值、参数、对象皆不可修改。(下面俩保留一个即可)
char String::operator[](int index)
{
if (index < 0 || index > Size()) {
throw logic_error("下标异常!");
}
return m_str[index];
}
const char String::operator[](const int index) const
{
if (index < 0 || index > Size()) {
throw logic_error("下标异常!");
}
return m_str[index];
}
istream & operator >> (istream & input, String & obj)
{
cout << "请输入字符串:";
char buffer[1024] = { 0 };
input >> buffer;
if (obj.m_str) {
delete[]obj.m_str;
obj.m_str = nullptr;
}
obj.m_str = new char[strlen(buffer) + 1];
strcpy_s(obj.m_str, strlen(buffer) + 1, buffer);
return input;
}
ostream & operator << (ostream & output, const String & obj)
{
if (obj.m_str) {
output << obj.m_str;
return output;
}
return output;
}
主函数:main.cpp
//主函数接口
#include <iostream>
using namespace std;
#include "String.h"
int main() {
//无参构造
String str1;
cout << "str1:" << str1 << endl;
//有参构造
String str2("hello");
cout << "str2:" << str2 << endl;
//拷贝构造函数
String str3(str2);
cout << "str3:" << str3 << endl;
//c_Str()函数
char *buffer = str2.c_Str();
cout << "c风格字符串buffer:" << buffer << endl;
//At()函数
try
{
cout << "At()函数:" << str2.At(4) << endl;
}
catch (const std::exception & e)
{
cout << "At()函数:" << e.what() << endl;
}
//Front()函数
try
{
cout << "Front()函数:" << str2.Front() << endl;
}
catch (const std::exception & e)
{
cout << "Front()函数:" << e.what() << endl;
}
//Back()函数
try
{
cout << "Back()函数:" << str2.Back() << endl;
}
catch (const std::exception & e)
{
cout << "Back()函数:" << e.what() << endl;
}
//Size()函数
cout << "Size()函数:" << str2.Size() << endl;
//Empty()函数
//默认情况下,bool类型会以1或0的形式输出,使用boolalpha()操纵器设置输出格式为true或false
cout << "Empty()函数:" << boolalpha << str1.Empty() << endl;
cout << "Empty()函数:" << boolalpha << str2.Empty() << endl;
//重载=
String str4 = str2;
cout << "str4:" << str4 << endl;
//重载+=,分别是 +=字符串 和 +=对象
cout << "+= 字符串:" << (str2 += " world!") << endl;
cout << "+= 对象:" << (str3 += str4) << endl;
//重载[]
cout << "下标[]:" << str2[0] << endl;
//重载!= 和 ==
cout << "!= 吗?:" << boolalpha << (str2 != str3) << endl;
cout << "== 吗?:" << boolalpha << (str2 == str3) << endl;
//重载+,分别是 +字符串 和 +对象
cout << "+ 字符串:" << (str2 + ",c++") << endl;
cout << "+ 对象:" << (str3 + str4) << endl;
//重载*
cout << "对象 * 个数:" << (str4 * 3) << endl;
String str5 = "abandon_c++";
//Substr()函数
String temp = str5.Substr(8, 3);
cout << "Substr()函数:" << temp << endl;
//Erase()函数
str5.Erase(7);
cout << "Erase()函数:" << str5 << endl;
//pop_Back()函数
str5.pop_Back();
str5.pop_Back();
str5.pop_Back();
cout << "pop_Back()函数:" << str5 << endl;
//Clear()函数
try
{
str1.Clear();
cout << "Clear()函数:" << str5;
cout << "清空成功!" << endl;
}
catch (const std::exception & e)
{
cout << "Clear()函数:" << e.what() << endl;
}
//重载 >> 和 <<
cin >> str1;
cout << "输出:" << str1 << endl;
return 0;
}
输出:
输出:
str1:
str2:hello
str3:hello
c风格字符串buffer:hello
At()函数:o
Front()函数:h
Back()函数:o
Size()函数:5
Empty()函数:true
Empty()函数:false
str4:hello
+= 字符串:hello world!
+= 对象:hellohello
下标[]:h
!= 吗?:true
== 吗?:false
+ 字符串:hello world!,c++
+ 对象:hellohellohello
对象 * 个数:hellohellohello
Substr()函数:c++
Erase()函数:abandonc++
pop_Back()函数:abandon
Clear()函数:清空失败!
请输入字符串:这里是CSDN博客!
输出:这里是CSDN博客!
STL是C++的灵魂,这也正是C++的伟大之处,本文为基础知识分享,如有错误和补充,请在评论区指正,感谢!