string功能介绍(普及版)

目录

1。初始化(好几种方式),npos和string的使用说明

2。string的拷贝,隐式类型转换,[],size,iterator,begin,end,reverse,reverse_iterator,rbegin,rend,const_ iterator

3。算法库中sort的使用

4。push_back,append,+=,typeid

5。assign,insert,erase,replace,reserve,capacity,max_size,resize

6。find,rfind,substr,+,<,>,getline,to_string,stoi

7。全中文或者有中文的字符串大小说明

8。shrink_to_fit,at,c_str,data,copy,get_allocator,compare,find_first_of,find_first_not_of,find_last_not_of,find_last_of

9。swap

10。string类(及其部分好用的比较有意义的功能)的自我实现


本文是对STL库里面的string类及其附带的常见的函数和指令和算法库中sort的使用进行总结,std::string::函数或者指令,可以用于复习和查找。不会很详细,但是肯定非常全面。话不多说,我们现在开始

1。初始化(好几种方式),npos和string的使用说明

#include<iostream>
using namespace std;
//using std::string;//npos作为string类里的静态成员变量只会在全局找,类名 :: 静态成员变量名,这个就是类名访问静态成员的语法
#include<string>//string的头文件, 包括了就可以用里面的函数和string和这个类型
//string表示是字符数组,可以扩容,可以增删查改

int main()
{
    //常用的数组构造方式
    string s1;//构造一个名叫s1的字符数组
    string s2("abcd");//构造一个成员为abcd的字符数组s2,直接在名字后面加(内容),就是用类的构造方式
    string s3(s2);//拷贝构造s3

    // 不常用 了解
    string s4(s2, 1, 3);//将s2从下标位置1到下标位置3的字符都拷贝到s4,也是一种构造方式
    string s5(s2, 1, string::npos);//规定位置拷贝构造的最大范围的右边界为npos,及整型的最大值,也就是最后一个参数的最大值
    //npos在string这个类里,所以要包含一下这个类才可以用
    string s6(s2, 1);//如果指定拷贝的范围太大或者没有右范围,就会只拷贝可以拷贝的最大的长度到\0之前,及最后一个参数可以不写或者写很大但不超过npos
    string s7("sdsgsfgsg");
    string s8(s7, 1, 100);
    //string s9(s7, 100);//但是拷贝的范围的其实位置不可以越界
    string s10("hello word", 5);//拷贝前面那个字符串的前5个字节,然后给s10
    //string s11("hello word", 100);//如果读取的数据个数大于字符串的长度就会越界读取无效的数据,就会读取到别的东西
    //所以读取数据个数不可以大于待读取字符串的长度
    string s12(10, 'x');//创建10个x字符给s12
    //string s13(10, 3);//一定要是字符才可以被写入,不然是打印不出东西的,也不会报错的
    //cout << s2 << endl;
    //cout << s3 << endl;
    //cout << s4 << endl;
    //cout << s5 << endl;
    //cout << s7 << endl;
    //cout << s8 << endl;
    //cout << s9 << endl;
    //cout << s10 << endl;
    //cout << s11 << endl;
    cout << s12 << endl;
    //cout << s13 << endl;
    return 0;
}

2。string的拷贝,隐式类型转换,[],size,iterator,begin,end,reverse,reverse_iterator,rbegin,rend,const_ iterator

#include<iostream>
#include<string>
using namespace std;
void push_back(const string& s)
{
    cout << "push_back" << endl;
}

void test_string2()
{
    //隐式类型转换,将字符型转换成具有string字符串型的s2
    string s2 = "hello word";
    const string& s3 = "hello";
    push_back(s2);
    push_back("hhhh");
}

//class string
//{
//public:
//    // 引用返回
//    // 1、减少拷贝
//    // 2、修改返回对象
//    char& operator[](size_t i)
//    {
//        assert(i < _size);
//        return _str[i];
//    }
//private:
//    char* _str;
//    size_t _size;
//    size_t _capacity;
//};

//operator[] 符号重载,相当于char& operator[],string里面有一个指针是指向
//你初始化的空间的[],相当于在通过指针调用那快空间的字符,循环调用就是在遍历整个字符串

void test_string3()
{
    string s3("hello word");
    s3[0] = 'x';//将s3的第一个字符设置成x
    for (int i = 0; i < s3.size(); i++)
    {
        cout << s3[i];//循环通过符号重载[]打印s3
        //char ret = s3.operator[](i);//相当于上面这种写法
        //cout << ret;

    }
    //size()是string自带的函数用来计算字符串的长度,可以直接用
    //s3.length() 的作用等价于s3.size()

    cout << endl;
    cout << s3 << endl;
}
//遍历一个字符串的方法
//法一:用符号重载[]
//主流写法

void test_string4()
{
    string s4 = "hello word";
    for (size_t i = 0; i < s4.size(); i++)
    {
        cout << s4[i] << " ";
    }
    cout << endl;
}

//法2:运用迭代器iterator,iterator在string这个类里所以要指定类域才可以访问到
//迭代器iterator本身也是一种数据类型

void test_string5()
{
    string s5 = "hello word";
    string::iterator it = s5.begin();//begin()是因为begin是函数所以要加括号,可以直接调用,其他也类似
    
//auto it = s5.begin();//可以用自动推导类型auto,前提是你知道it的具体类型是iterator
    //begin是任何容器包括string的第一个数据位置的iterator,然后将它返回用一个数据类型为iterator的变量接收
    //end是任何容器包括string的最后一个数据的下一个数据位置的iterator,然后将它返回用一个数据类型为iterator的变量接收

    while (it != s5.end())
    {
        cout << *it;
        cout << " ";
        it++;
    }
    cout << endl;
    //迭代器it相当于指针,但不一定是指针,有指针的作用,开头先指向第一个元素,取完一个元素就++取下一个
}

//方法3:范围for
// 底层角度,他就是迭代器

void test_string6()
{
    string s6("hello word", 10);//只有赋值才有等号
    for (auto i : s6)
    {
        cout << i;//比iterator方便的是i会自己++,自己停止
        cout << " ";
    }
    cout << endl;
}

void test_string7()
{
    string s6("hello word", 10);//只有赋值才有等号
    for (auto& i : s6)
    {
        i++;
        cout << i;//比iterator方便的是i会自己++,自己停止
        cout << " ";
    }
    cout << endl;
}

void test_string8()
{
    const string s5 = "hello word";//如果字符串s5加上了const,就相当于字符串的值不可改变
    string::const_iterator it = s5.begin();//begin()是因为begin是函数所以要加括号,可以直接调用,其他也类似
    //auto it = s5.begin();//可以用自动推导类型auto,前提是你知道it的具体类型是iterator
    //begin是任何容器包括string的第一个数据位置的iterator,然后将它返回用一个数据类型为iterator的变量接收
    //end是任何容器包括string的最后一个数据的下一个数据位置的iterator,然后将它返回用一个数据类型为iterator的变量接收

    while (it != s5.end())
    {
        //*it += 3;//const_iterator相对应的使用迭代器的类型也要加上const_以限制指针it指向的值发生改变
        //所以*it += 3是会报错的,所以迭代器类型是有两个版本的,有没有const_
        //为什么是const_而不是const
        //因为:iterator是可读可写的
        //      const iterator是指迭代器本身不能写(修改指向)
        //      const_ iterator是指迭代器指向的内容不能写(修改数值)

        cout << *it;
        cout << " ";
        it++;
    }
    cout << endl;
    //迭代器it相当于指针,但不一定是指针,有指针的作用,开头先指向第一个元素,取完一个元素就++取下一个
}

//reverse 逆制字符串
//搭配iterator使用,需要使用迭代器进行变量组成reverse_iterator, 此为数据类型

void test_string9()
{
    string s9 = "hello word";
    //const string s9 = "hello word";
    string::reverse_iterator it2 = s9.rbegin();
    //string::const_reverse_iterator cit1 = s9.rbegin();//const版本
    //auto cit1 = s9.rbegin();
    //rbegin是任何容器包括string的最后一个数据位置的iterator,然后将它返回用一个数据类型为reverse_iterator的变量接收
    //rend是任何容器包括string的头一个数据的上一个数据位置的iterator,然后将它返会用一个数据类型为reverse_iterator的变量接收

    while (it2 != s9.rend())
    {
        //*it2 += 3;

        cout << *it2 << " ";
        ++it2;//逆序变历,从尾到头,尾为第一个数据,逆着变量上去所以还是++
    }
    cout << endl;
}
int main()
{
    //test_string2();
    //test_string3();
    //test_string4();
    //test_string5();
    //test_string6();
    //test_string7();
    //test_string8();
    //test_string9();
    return 0;
}

//二叉树
//template<class T>
//struct ListNode
//{
//    ListNode<T>* _next;
//    ListNode<T>* _prev;
//    T _data;
//};
//
//template<class T>
//class list
//{
//private:
//    ListNode<T>* _head;
//};

3。算法库中sort的使用

#include<iostream>
using namespace std;
#include<algorithm>
//使用算法如sort快速排序的头文件,包括了就可以直接使用了
int main()
{
    string s1 = "hello wordff";
    string s2("hhfheef");
  
 // sort的作用是将任意数据如s1里的每个字符按字典序排序
    //排序时传入一个待排序的区间的头尾下标的iterator(指针)就可以了,传入的区间下标的iterator是左闭右开的,sort底层会自己通过iterator编译出值
    //[0, 13)

    sort(s1.begin(), s1.end());
    
//[0, 5) 排列前5个数
    sort(s2.begin(), s2.begin() + 5);
    cout << s1 << endl;
    cout << s2 << endl;
//打印的还是整个字符串
    return 0;
}

4。push_back,append,+=,typeid

#include<iostream>
#include<string>
using namespace std;
void test_string7()
{
    string s1 = "hello word";
    string s2 = "hello word";
    string s3 = "hello word";
    s1.push_back('x');
// push_back属于string这个类里面的函数可以直接调用
                      // push_back的作用为将一个字符尾插到一个字符串上面

    //s1.push_back("daf"); //push_back只能尾插字符不可以尾插字符串
    cout << s1 << endl;
    s2.append("dffawf");
// append属于string这个类里面的函数可以直接调用
                        // append的主流的作用(用法)为将一个字符串尾插到一个字符串上面

    //s2.append('s');// append不能直接尾插一个字符

    s3 += "sss";// +=为string的符号重载,作用为尾插字符或者字符串到一个字符串上面(比较好用)
    s3 += 'p';
    s3 += s2;
// += 可以用在string和string之间
    cout << s2 << endl;
    cout << s3 << endl;
  
 //push_back   +=   append都是尾插
}
void test_string8()
{
    
    cout << typeid(std::string::iterator).name() << endl;
//获取变量名称
    string s2("hello world");
    cout << sizeof(s2) << endl;
    
//typeid是操作符,不是函数,这点与sizeof类似,typeid的作用是
    //运行时获取变量类型名称,一般(可以)使用typeid(变量).name()
    //不是所以编译器使用typeid都可以输出“int” “double”等之类的名称

}
int main()
{
    test_string7();
    test_string8();
    return 0;
}

5。assign,insert,erase,replace,reserve,capacity,max_size,resize

#include<iostream>
#include<string>
using namespace std;
void test_string8()
{
    string s1("hello word");
    cout << s1 << endl;
    s1.assign("111111");
  
 //assign的作用是将原本的字符串变成assign函数括号后面指定的字符串
    cout << s1 << endl;
}

void test_string9()
{
    string s2 = "hello word";
    cout << s2 << endl;
    s2.insert(0, "xxx");
    
//insert的作用就是将给定的字符串或者字符头插到s2的给定位置之前,insert是类里的函数
    s2.insert(s2.begin(), 'y');
  
 //如果插入位置之前是类似begin()的这种东西,只能插入字符串
    cout << s2 << endl;
    char ch = 'k';
    string s3("hello bit");
    s3.insert(0, 1, 'k');
    cout << s3 << endl;
    s2.insert(s2.begin(), s3.begin(), s3.end());
  
 //又一种insert的用法,可以看出可以将一个字符串整个弄到另一个字符串的第一个位置
    //这种用法可以看到,第一个参数不可以是数字的
    //用insert插入字符串时要提供个数

    cout << s2 << endl;
    
//insert慎用,因为效率不高 -> O(N)
    // 实践中需求也不高

}

void test_string10()
{
    string s2("hello word");
    cout << s2 << endl;
    s2.erase(0, 2);
  
 //erase的作用是可以删除s2一个左闭右开的区间的字符,然后将此区间后面的字符向前拼接
    //其实就是删除字串(其实这一步都不用做的),再合成新的字符串,注意erase的参数是一个左闭右开的区间, 而且用的是下标

    cout << s2 << endl;
    
//如果所给区间的右边超出了s2的最大值那就会默认删除到结尾,此时就没有移动的操作了
    s2.erase(5, 100);
    
//s2.erase(5);
    //通过网站可以得到最后一个参数是有缺省值的npos,所以可以不写

    cout << s2 << endl;

    //erase效率不高,慎用,和insert类似,要挪动数据
}

void test_string11()
{
    string s2("hello word");
    cout << s2 << endl;
    
//replace的作用是在字符串的给定位置(不是下标)后面的指定字节个数替换成给定的字符串,所以会使字符串变长,所以是3个参数
    //作用和insert刚好相反了
    //会发现第二个参数指定要换的字节个数如果给的过于大就会只替换到数组的最大值

    s2.replace(5, 1, "%20");
    cout << s2 << endl;
  
 //replace效率不高,慎用,和insert类似,要挪动数据
}
void TestPushBack()
{
    string s;
  
 // 知道需要多少空间,提前开好
    s.reserve(200);
    s[100] = 'x';

    size_t sz = s.capacity();
    cout << "capacity changed: " << sz << '\n';

    cout << "making s grow:\n";
    for (int i = 0; i < 200; ++i)
    {
        s.push_back('c');
        if (sz != s.capacity())
        {
            sz = s.capacity();
            cout << "capacity changed: " << sz << '\n';
        }
    }
}
void test_string12()
{
    string s1("hello world hello bit");
    cout << s1.size() << endl;
//字符串的大小没有\0
    cout << s1.capacity() << endl;//capacity不一定是字符串的大小, capacity表示的是字符串所占空间的大小,capacity会比较大
    cout << s1.max_size() << endl;
    for (size_t i = 0; i < s1.max_size(); i++)
    {
        s1 += 'x';
    }

}

void test_string13()
{
    string s2("11111111111111111111111111111");
    string s3("111111111");
  
 //s2.reserve(100);
    //reserve的作用是保留一个字符串s2为指定的空间,就是将字符串设定为reserve指定的空间大小
    //就是在给字符串开空间

    s2.reserve(20);
    cout << s2.capacity() << endl;
    s2.reserve(100);
    cout << s2.capacity() << endl;
//VS通过reserve开的空间会比实际要开的空间大
    //s2.reserve(20);
    //cout << s2.capacity() << endl;
    //通过reserve开空间是不可逆的,就是如果先开了大的空间就无法在通过reserve减小了
}

void test_string14()
{
    string s2("hello word");
    
//resize的功能有两个
    //尾插字符x直到字符串长度达到20,只能尾插字符(两个参数)

    s2.resize(20, 'x');
    cout << s2 << endl;
    s2.resize(5);
    
//尾删字符串,直到字符串长度为5 (一个参数)
    cout << s2 << endl;
}
int main()
{
    //test_string8();
    //test_string9();
    //test_string10();
    test_string11();
    //test_string12();
    //test_string13();
    //test_string14();
    return 0;
}

6。find,rfind,substr,+,<,>,getline,to_string,stoi

#include<iostream>
using namespace std;
#include<string>
void test_string1()
{
    string s1("string.cpp.zip");
    size_t pos = s1.find('.', 7);
    size_t pos2 = s1.find('.', 700);
    size_t ret = s1.find('s');
    int pp = s1.find('a');
    size_t ppp = s1.find('a');
    //find就是找的意思,两个参数,只能查找字符,第一个参数为你要查找的字符
    //第2个参数为你要从那个下标开始查找,返回值为那个字符在字符串中的下标位置
    //如果没有指定要从哪里开始查找就默认从开头开始查找
    //如果要查找的字符字符串没有,就会返回-1
    //如果指定的查找位置越界了,就会按原有的最大位置或者最小位置开始查找

    cout << pos << endl;
    cout << pos2 << endl;
    cout << ret << endl;
    cout << pp << endl;
    cout << ppp << endl;
}
void test_string2()
{
    string s1("string.cpp.zip");
    size_t pos = s1.rfind('.', 100);
    size_t ret = s1.rfind('t', 4);
    int pp = s1.rfind('a');
    size_t ppp = s1.rfind('a');
    //rfind就是倒着找的意思,两个参数,只能查找字符,第一个参数为你要查找的字符
    //第2个参数为你要从那个下标开始倒着查找,返回值为那个字符在字符串中的下标位置
    //如果没有指定要从哪里开始查找就默认从结尾开始查找
    //如果要查找的字符字符串没有,就会返回-1
    //如果指定的查找位置越界了,就会按原有的最大位置或者最小位置开始

    cout << pos << endl;
    cout << ret << endl;
    cout << pp << endl;
    cout << ppp << endl;
}

void test_string3()
{
    string s1("string.cpp.zip");
    string s2("string.cp\0p.zip");
    string sd = s1.substr(2, 2);
    string sdl = s1.substr(2, 3);
    string pp = s2.substr(4);
    string ppp = s2.substr(4, 100);
    //substr的作用是提取字符串,两个参数,第一个是要提取的开始位置(下标)
    //第2个参数为要提取的字符串的长度,提取成功后返回值为string类型的字符串
    //如果没有指定提取长度就会默认提取字符串直到出现\0为止
    //如果第2个参数过大就会提取到\0之前,也就是提前到能提取的最大长度

    cout << sd << endl;
    cout << sdl << endl;
    cout << pp << endl;
    cout << ppp << endl;
}
void test_string33()
{
    //find rfind和substr的混合应用(分割字符串)
    string file("string.cpp.zip");
    size_t pos = file.rfind('.');
    //string suffix = file.substr(pos, file.size() - pos);
    string suffix = file.substr(pos);

    cout << suffix << endl;

    string url("https://gitee.com/ailiangshilove/cpp-class/blob/master/%E8%AF%BE%E4%BB%B6%E4%BB%A3%E7%A0%81/C++%E8%AF%BE%E4%BB%B6V6/string%E7%9A%84%E6%8E%A5%E5%8F%A3%E6%B5%8B%E8%AF%95%E5%8F%8A%E4%BD%BF%E7%94%A8/TestString.cpp");
    size_t pos1 = url.find(':');
    string url1 = url.substr(0, pos1 - 0);
    cout << url1 << endl;

    size_t pos2 = url.find('/', pos1 + 3);
    string url2 = url.substr(pos1 + 3, pos2 - (pos1 + 3));
    cout << url2 << endl;

    string url3 = url.substr(pos2 + 1);
    cout << url3 << endl;
}
void test_string4()
{
    string s1 = "hello";
    string s2 = "fword";
    string s3 = s1 + s2;
    // + 是重载的运算符是字符串附加的意思,+的右边的字符串附加在+左边的字符串的后面
    string s4 = s1 + "ssss";//这个是有隐式类型转换的, "ssss"由char型转换成string型
    cout << s3 << endl;
    cout << s4 << endl;
}

void test_string5()
{
    string s1 = "hello";
    string s3 = "hello";
    string s2 = "fword";
    // <和> 是重载的运算符是两个字符串按字典序比较的意思, 如果比较正确就会输出1,否则输出0
    cout << (s1 > s2) << endl;//流插入<<的优先级大于<,所以一定要加()
    cout << (s1 > s3) << endl;//流插入<<的优先级大于<,所以一定要加()
}

void test_string6()
{
    string s1;
    //cin >> s1;
    //cin默认规定空格或者换行是多个值之间分割, cin一次只能读取一个字符串
    getline(cin, s1);
    //getline的作用就是可以解决cin无法读全中间有空格或者换行的字符串
    //getline是string的类的函数,包了头文件就可以直接单独的用,第一个参数是输入流cin,第2个参数为待输入的字符串

    cout << s1 << endl;
}

void test_string7()
{
    int x = 0;
    int y = 0;
    double y2 = 0.0;
    string pp("100");
    string pp2("1001");
    string str = to_string(x + y);
    string str3 = to_string(x + y2);
    string str2 = to_string(x);
    //to_string的作用是将其他类型的数据或者计算式的计算结果变成string类型,所以参数可以是式子也可以是一个变量
    //所以to_string的返回值就是string类型的字符串
    //to_string的作用有点像atoi函数
    //to_string是string的类的函数,包了头文件就可以直接单独的用

    int z = stoi(pp);
    double z1 = stoi(pp);
    int z2 = stoi(pp + pp2);
    //stoi的作用是将string类型的数据或者计算式的计算结果变成整型,所以参数可以是式子也可以是一个变量
    //所以stoi的返回值就是一个整型
    //stoi的作用有点像itoa函数
    //stoi是string的类的函数,包了头文件就可以直接单独的用

    cout << str << endl;
    cout << str2 << endl;
    cout << str3 << endl;
    cout << z << endl;
    cout << z1 << endl;
    cout << z2 << endl;
}

template<class T>
void func(const T& left, const T& right)
{
    cout << "void func(const T& left, const T& right)" << endl;
}

template<>
void func<int*>(int* const& left, int* const& right)
{
    cout << "void func<int*>(int* const & left, int* const & right)" << endl;
}
int main()
{
    test_string1();
    //test_string2();
    //test_string3();
    //test_string33();
    //test_string4();
    //test_string5();
    //test_string6();
    test_string7();

    //bit::test_string1();
    int a = 0, b = 1;
    func(a, b);
    func(&a, &b);
    return 0;
}

7。全中文或者有中文的字符串大小说明

#include<iostream>
#include<string>
using namespace std;
int main()
{
    char s1[] = "我们";
    char s2[] = "abcd";
    //可以看出全是中文的字符串和全是英文的字符串在长度一样时
    //所占的字节数是一样的,可以得出一个中文占的字节数是一个英文的两倍

    cout << sizeof(s1) << endl;
    cout << sizeof(s2) << endl;
    cout << s1 << endl;
    cout << s2 << endl;
    return 0;
}

专业解释如下图:

8。shrink_to_fit,at,c_str,data,copy,get_allocator,compare,find_first_of,find_first_not_of,find_last_not_of,find_last_of

#include<iostream>
#include<string>
using namespace std;
void string_test1()
{
    string s1("hello word");
    s1.shrink_to_fit();
    //shrink_to_fit的作用是缩容,使所开空间和已存数据大小相等
    //resize也可以缩容,但是不常用,上面这个是专门原来缩容的
    //但是这个不要经常用,不好,缩容的本质是时间换空间
    //shrink_to_fit 没有参数

    cout << s1 << endl;
}

void string_test2()
{
    string s2 = "hello word";
    cout << s2[2] << endl;
    cout << s2.at(2) << endl;
    //可以看出at的作用和[]一样,都是可以访问特定下标的字符。
    //at有一个参数即要访问的字符的下标
    //

}

void string_test3()
{
    string s2("hello world");
    try
    {
        s2[15];
        s2.at(15);
        //这个可以将保存报错的信息,打印出来的代码可以看出at和[]的区别
        //at越界访问时会把错误信息先打印出来
        //[]越界访问时会直接弄出报错弹窗的,不会打印报错信息

    }
    catch (const exception& e)
    {
        cout << e.what() << endl;
    }
    
    string file("test.cpp");
    FILE* fout = fopen(file.c_str(), "r");
    char ch = fgetc(fout);
    while (ch != EOF)
    {
        cout << ch;
        ch = fgetc(fout);
    }
    //这个c_str的作用是获取指向string的指针,也就是获取空间的首元素的地址
    //用以兼容C语言

}

void string_test4()
{
    //data 作用和c_str一样,属于不同时期的产物
    //
copy copy属于库函数,有三个参数,S.copy(A, B, C), 将S的从第B个字符开始拷贝C个字符给string A
    //copy不常用一般用c_str
    //
get_allocator string这个类底下的一个空间配置器,暂时了解一下就行 
    //
compare 比较函数,可以两个string类型的对比,可以string自己和自己的一部分对比,还可以string和不同类型的数据对比
    //compare没什么用,看看就好,虽然功能很强大,但是用的很少 

}

void string_test5()
{
    //find_first_of 的意思是在一个字符串中查找find_first_of函数中输入的字符串
    //的相同字符,找到相同的字符就返回对应的下标,然后再找下一个
    //find_first_of 有两个参数,第一个为要查找的字符构成的字符串,第二个(可以不写就默认从开头开始查找)是查找的开始位置
    //find_first_of的时间复杂度为O(N*M)
    //find_first_of的作用和C语言的strtok类似且都用得比较少
    //find_first_of的作用相反的是find_first_not_of和find_last_not_of
    //find_first_not_of 的意思是在一个字符串中从前往后查找find_first_not_of函数中输入的字符串的不相同的字符,找到不相同的字符就返回对应的下标,然后再找下一个
    //find_last_not_of 的意思是在一个字符串中从后往前查找find_last_not_of函数中输入的字符串的不相同的字符,找到不相同的字符就返回对应的下标,然后再找下一个

    string str("Please, replace the vowels in this sentence by asterisks.");
    int find = str.find_first_of("aeiou");
    while (find != string::npos)
    {
        str[find] = '$';//找到就替换成$
        find = str.find_first_of("aeiou", find + 1);//循环赋值
    }
    cout << str << endl;
}

//find_last_of的作用是取出一个路径串的path和file
//Linux下的路径和windows下的路径都可以取的

void SplitFilename(const std::string& str)
{
    std::cout << "Splitting: " << str << '\n';
    std::size_t found = str.find_last_of("/\\");
    std::cout << " path: " << str.substr(0, found) << '\n';
    std::cout << " file: " << str.substr(found + 1) << '\n';
}

int main2()
{
    std::string str1("/usr/bin/man");
    std::string str2("c:\\windows\\winhelp.exe");

    cout << str2 << endl;

    SplitFilename(str1);
    SplitFilename(str2);

    return 0;
}


int main()
{
    //string_test1();
    //string_test2();
    //string_test3();
    //string_test4();
    string_test5();
    return 0;
}

9。swap

由于std流里也有个swap,string类里面的swap和string里面的swap功能基本一致,但是由于std流里的swap进行交换时消耗比较大会生成很多拷贝构造,string里面的由于没有那么多拷贝构造,所以消耗比较小,用法也和std流里的swap基本一样。

#include <iostream>
#include <string>

main ()
{
  std::string buyer ("money");
  std::string seller ("goods");

  std::cout << "Before the swap, buyer has " << buyer;
  std::cout << " and seller has " << seller << '\n';

  seller.swap (buyer);

  std::cout << " After the swap, buyer has " << buyer;
  std::cout << " and seller has " << seller << '\n';

  return 0;
}

可以看到,swap就一个参数,就是待交换的一个string类型的字符串,把待交换的字符串的内容和调用swap函数的string的内容互相交换

但是类里的std是可以交换string类型的数据的,所以可以直接使用std里面的swap交换两个string,交换两个string类型的数据时可以直接交换指向两块空间的指针,这样可以减少拷贝构造的产生。

下面自我实现和上图都有利用到!!!

10。string类(及其部分好用的比较有意义的功能)的自我实现

(本部分的test.cpp只显示了部分功能的测试,如果想自己测试更多string.cpp里的功能,可以自己进行测试,欢迎添加新功能欢迎添加新功能)

//string.h//

#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
namespace bit
{
    class string
    {
    public:
        typedef char* iterator;
        typedef const char* const_iterator;
        //string();构造函数
        string(const char* str = "");//全缺省可以给个空的,这样没有参数也可以用
        string(const string& str);//由于和string冲突了所以用&
        string& operator=(const string& str);//赋值
        ~string();
        const char* c_str() const;//打印函数
        size_t size() const;//字符串的长度
        char& operator[](size_t pos);//提取pos位置的元素
        const char& operator[](size_t pos) const;
        void reserve(size_t n);//保存或者指定长度为n, 这里相当于扩容
        void push_back(char ch);//尾插一个字符
        void append(const char* str);//尾插一个字符串
        string& operator+=(char ch);//尾插一个字符
        string& operator+=(const char* str);//尾插一个字符串
        void insert(int pos, char ch);//指定位置尾插一个字符
        void insert(int pos, const char* str);//指定位置尾插一个字符串
        void erase(size_t pos = 0, size_t len = npos);//给定位置删除字符串
        size_t find(char ch, size_t pos = 0);//查找字符
        size_t find(const char* str, size_t pos = 0);//查找字符串
        void swap(string& s);//字符串交换
        string substr(size_t pos = 0, size_t len = npos);//提取字符
        bool operator<(const string& s) const;//比较<
        bool operator>(const string& s) const;//比较>
        bool operator<=(const string& s) const;//比较<=
        bool operator>=(const string& s) const;//比较>=
        bool operator==(const string& s) const;//比较==
        bool operator!=(const string& s) const;//比较!=
        void clear();//string内存清0
    private:
        //char _buff[16];//原本还会有个buff但是我们这边就不考虑了
        char* _str;
        int _capacity;//空间大小
        int _size;
        const static size_t npos;
    };

    istream& operator>> (istream& is, string& str);//流写入和下面的流提取第一个参数都不是成员变量,所以不能写成成员函数
    ostream& operator<< (ostream& os, const string& str);
}

//string.cpp//

#include"string.h"
namespace bit
{
    const static size_t npos = -1;//得在这个域里完成对静态全局变量的定义不然会有链接错误
    string::string(const char* str)
        :_size(strlen(str))
    {
        _str = new char[_size + 1];//留个位置给\0
        _capacity = _size;
        strcpy(_str, str);
    }
    //string::string()//单独空string的初始化,也可以用全缺省的
    //{
    //    _str = new char[1]{'\0'};
    //    _size = 0;
    //    _capacity = 0;
    //}

    string::string(const string& str)
    {
        _str = new char[str._capacity + 1];
        strcpy(_str, str._str);
        _size = str._size;
        _capacity = str._capacity;
    }
    string& string::operator=(const string& str)
    {
        if (this != &str)
        {
            char* tmp = new char[str._capacity + 1];
            strcpy(tmp, str._str);
            delete[] _str;
            _str = tmp;
            _capacity = str._capacity;
            _size = str._size;
        }
        return *this;
    }
    const char* string::c_str() const
    {
        return _str;//重载函数的函数体内做了处理,打印的是指针指向空间中的内容
    }
    string::~string()
    {
        delete[] _str;
        _str = nullptr;
        _capacity = 0;
        _size = 0;
    }

    size_t string::size() const
    {
        return _size;
    }

    char& string::operator[](size_t pos)
    {
        assert(pos < _size);
        return _str[pos];
    }
    const char& string::operator[](size_t pos) const
    {
        assert(pos < _size);
        return _str[pos];
    }

    void string::reserve(size_t n)
    {
        assert(n >= _capacity);
        char* tmp2 = new char[n];
        strcpy(tmp2, _str);
        delete[] _str;
        _str = tmp2;
        _capacity = n;
    }

    void string::push_back(char ch)
    {
        assert(ch != '\0');
        if (_capacity == _size)
        {
            int newcapacity = _capacity == 0 ? 4 : 2 * _capacity;
            reserve(newcapacity);
        }
        _str[_size] = ch;//_size位置给\0了
        _str[_size + 1] = '\0';
        _size++;
    }

    void string::append(const char* str)
    {
        int len = strlen(str);
        reserve(_capacity + len);
        strcpy(_str + _size, str);
        _size = _size + len;
    }

    string& string::operator+=(char ch)
    {
        assert(ch != '\0');
        push_back(ch);
        return *this;
    }
    string& string::operator+=(const char* str)
    {
        append(str);
        return *this;
    }

    void string::insert(int pos, char ch)//pos的类型不要写成size_t
    {
        assert(ch != '\0');
        assert(pos <= _size);
        assert(pos >= 0);
        if (_capacity == _size)
        {
            int newcapacity = _capacity == 0 ? 4 : 2 * _capacity;
            reserve(newcapacity);
        }
        int end = _size;
        while (end >= pos)//不然这边两个数比较一定要先统一类型(int)pos
        {
            _str[end + 1] = _str[end];
            end--;
        }
        _str[end + 1] = ch;
    }
    //另一种写法
    size_t end = _size + 1;
    while (end > pos)
    {
        _str[end] = _str[end - 1];
        --end;
    }

    _str[pos] = ch;
    ++_size;

    void string::insert(int pos, const char* str)
    {
        assert(pos <= _size);
        assert(pos >= 0);
        int len = strlen(str);
        reserve(_capacity + len + 1);
        int end2 = _size;//结尾有个\0
        int end = _capacity - 1;
        while (end2 >= pos)
        {
            _str[end] = _str[end2];
            end--;
            end2--;
        }
        memcpy(_str + end2 + 1, str, len);
    }

    void string::erase(size_t pos = 0, size_t len = npos)
    {
        assert(pos < _size);
        if (len >= _size - pos)
        {
            _str[pos] = '\0';
            _size = pos;
        }
        else
        {
            memcpy(_str + pos, _str + pos + len, len);
            _size = _size - len;
        }
    }

    size_t string::find(char ch, size_t pos)
    {
        for (int i = pos; i < _size; i++)
        {
            if (_str[i] == ch)
            {
                return i;
            }
        }
        return npos;//没有找到就返回一个无穷大的数
    }

    size_t string::find(const char* sub, size_t pos)
    {
        char* p = strstr(_str + pos, sub);//strtsr就作用是找到字串找到就返回,找不到就返回nullptr
        return  p - _str;//指针减指针得到一个相对位置
    }

    void string::swap(string& s)
    {
        std::swap(_str, s._str);//库里的swap可以完成指针及其指向空间的交换, 交换指向的指针就可以了
        std::swap(_capacity, s._capacity);
        std::swap(_size, s._size);
    }

    string string::substr(size_t pos, size_t len)
    {
        assert(pos < _size);
        assert(pos >= 0);
        int i = 0;
        //len大于后面剩余字符,有多少取多少
        if (len > _size - pos)
        {
            string u(_str + pos);//强制性类型转换成string再赋值
            return u;
        }
        else
        {
            string u;//创建一个空string
            u.reserve(len);
            for (int i = 0; i < len; i++)
            {
                //u += _str[pos + i];//尾插法
                u._str[i] = _str[pos + i];
            }
            return u;
        }
    }

    bool string::operator<(const string& s) const
    {
        int ret = strcmp(_str, s._str);
        if (ret < 0)
        {
            return ret;
        }
        else
        {
            return npos;
        }
    }
    bool string::operator>(const string& s) const
    {
        return !(*this <= s._str);//学会复用
    }
    bool string::operator<=(const string& s) const
    {
        int ret = strcmp(_str, s._str);
        if (ret > 0)
        {
            return npos;
        }
        else
        {
            return ret;
        }
    }
    bool string::operator>=(const string& s) const
    {
        return !(*this < s._str);
    }
    bool string::operator==(const string& s) const
    {
        int ret = strcmp(_str, s._str);
        if (ret == 0)
        {
            return ret;
        }
        else
        {
            return npos;
        }
    }
    bool string::operator!=(const string& s) const
    {
        return !(*this == s._str);
    }
    void string::clear()
    {
        _str[0] = '\0';
        _size = 0;
    }
    istream& operator>> (istream& is, string& str)
    {
        str.clear();
        char ch = is.get();//系统自动写入is后,先读取第一个字符
        while (ch != ' ' && ch != '\0')//注意是用&&
        {
            str += ch;//如果满足条件就尾插在后面
            ch = is.get();//可以实现反复读取的,再get一下就再次读取下一个
            //为什么不用>>而是用is.get(),因为>>, 不能提取/0和换行,所以不能判断,scanf可以读取空格和换行

        }
        return is;
    }
    ostream& operator<< (ostream& os, const string& str)
    {
        for (int i = 0; i < str.size(); i++)
        {
            os << str[i];
        }
        return os;
    }
}

//test.cpp//

#include"string.h"
int main()
{
    bit::string s2("hello word");
    cout << s2.c_str() << endl;
    //s2.insert(2, 'p');
    //cout << s2.c_str() << endl;
    s2.insert(2, "pp");
    cout << s2.c_str() << endl;//流提取可以传入一个指针,这样就会打印指针指向的内容
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/626993.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

回归预测 | Matlab实现DBO-ESN蜣螂算法优化回声状态网络多输入单输出回归预测

回归预测 | Matlab实现DBO-ESN蜣螂算法优化回声状态网络多输入单输出回归预测 目录 回归预测 | Matlab实现DBO-ESN蜣螂算法优化回声状态网络多输入单输出回归预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.Matlab实现DBO-ESN蜣螂算法优化回声状态网络多输入单输出…

图像融合-下游任务(目标检测、实例分割、深度估计、局部区域细节放大)

下游任务: 采用目标检测、实例分割和深度估计的下游任务来验证图像融合结果质量。 文章目录 下游任务:1.目标检测2.实例分割3.深度估计局部细节放大工具Update1.目标检测 YOLOv8:https://github.com/ultralytics/ultralytics 步骤内容第一步下载项目到本地第二步安装READ…

20232810 2023-2024-2 《网络攻防实践》实验九

一、实践内容 1.1 反汇编 1.1.1 编程原理 编程的原理是一套指导软件开发和维护的概念、原则和实践&#xff0c;包括抽象以简化复杂系统、模块化以分解程序、封装以隐藏内部状态、继承以共享特性、多态以允许不同响应、算法和数据结构以组织计算和存储、控制结构以控制流程、…

Spring Cloud系列—Spring Cloud Gateway服务网关的部署与使用指南

Gateway网关 文章目录 Gateway网关1. 网关基本简介1.1 什么是网关1.2 为什么需要网关&#xff1f; 2. 快速搭建gateway网关2.1 创建新模块2.2 引入依赖2.3 编写启动类2.4 配置路由规则2.5 测试 3. 路由过滤4. 过滤器4.1 简介4.2 网关过滤器4.2.2 种类 4.3 自定义过滤器4.3.1 自…

windows11 Django环境安装

相关文档 1、验证python和pip3环境 C:\Users\Administrator>python Python 3.12.3 (tags/v3.12.3:f6650f9, Apr 9 2024, 14:05:25) [MSC v.1938 64 bit (AMD64)] on win32 Type "help", "copyright", "credits" or "license" for…

Linux修改终端命令颜色

1.在家目录中修改.bashrc文件 cd ~ vim .bashrc2.找到PS1相关段落&#xff0c;把其他的注释掉&#xff0c;填上该行代码&#xff0c;修改为自己设置的颜色 (具体颜色查看参考文章) 提供两种颜色&#xff0c;其他的自学调色盘吧(下文有)~ (祝你愉快) ①浅蓝色 深蓝 PS1\[\03…

Ubuntu环境搭建与共享文件

vmtool 然后依次执行以下指令 sudo apt-get update 更新包列表。访问系统的软件仓库源,检查所有已知软件包的最新版本,并更新本地数据库,使得可以安装或升级到最新的软件版本。sudo apt-get upgrade 升级所有已安装的软件包到它们的最新版本。这不包括新安装的软件包,仅限…

6. RedHat认证-基于公钥的认证方式

6. RedHat认证-基于公钥的认证方式 主要学习客户端访问服务端的时候&#xff0c;免密登录这一方式 注意: 免密登录只是基于公钥认证的一个附带属性(基于公钥认证的方式更加安全&#xff0c;防止黑客暴力破解) 第一步&#xff1a;将客户端生成的秘钥传送到服务器 在客户端通过…

基于 Spring Boot 博客系统开发(九)

基于 Spring Boot 博客系统开发&#xff08;九&#xff09; 本系统是简易的个人博客系统开发&#xff0c;为了更加熟练地掌握 SprIng Boot 框架及相关技术的使用。&#x1f33f;&#x1f33f;&#x1f33f; 基于 Spring Boot 博客系统开发&#xff08;八&#xff09;&#x1f…

短剧看剧系统,当前互联网热门项目工具系统模板。

目录 揭秘爆款神器&#xff1a;短剧看剧系统&#xff0c;让你的内容火遍全网&#xff01; 一、短剧看剧系统&#xff1a;一站式解决方案 二、灵活定价&#xff0c;实现收益最大化 三、高效管理&#xff0c;团队协作更轻松 四、数据驱动&#xff0c;精准把握市场动态 五、智…

MM模块学习二 (供应商,物料后台相关配置)

公司代码配置 新建条目&#xff08;只是建了一个名字出来&#xff0c;后面很多表都是没有得&#xff09; 接下来定义公司代码&#xff1a; 公司代码复制完成&#xff08;后续修改交给财务顾问去做&#xff09; 复制工厂&#xff1a; 复制工厂完成&#xff1a; 修改复制过去的工…

风电功率预测 | 基于GRU门控循环单元的风电功率预测(附matlab完整源码)

风电功率预测 风电功率预测 | 基于GRU门控循环单元的风电功率预测(附matlab完整源码)完整代码风电功率预测 | 基于GRU门控循环单元的风电功率预测(附matlab完整源码) 完整代码 clc; clear close allX = xlsread(风电场预测.xlsx)

【微记录】Makefile中wildcard(通配)的一种用法--如何避免某个头文件路径不存在造成CLFAGS添加后编译报错?

文章目录 背景方法&#xff1a;wildcard补充信息wildcard解释Make中wildcard用法 背景 工程中&#xff0c;如果某个代码需要再不同平台有不同的依赖头文件&#xff0c;于是会出现不同平台依赖头文件路径不一样&#xff0c;但是为了适配多个平台如何做到避免某个头文件路径不存…

数字集成电路物理设计[陈春章]——知识总结与精炼01

第一章 集成电路物理设计方法 1.1 数字集成电路设计挑战 1.2 数字集成电路设计流程 前两节内容讲述的是数字集成电路发展与流程&#xff0c;知识体系比较宏观和简单&#xff0c;请读者自行了解即可。 1.3 数字集成电路设计收敛 实现设计收敛任务&#xff1a;①数据系统;②优…

tensorflow实现二分类

# 导入所需库和模块 from tensorflow.keras.layers import Dense, Input, Activation # 导入神经网络层和激活函数模块 from tensorflow.keras.models import Sequential # 导入Keras的Sequential模型 import pandas as pd # 导入Pandas库用于数据处理 import numpy as np …

AI网络爬虫:用kimichat自动批量提取网页内容

首先&#xff0c;在网页中按下F12键&#xff0c;查看定位网页元素&#xff1a; 然后在kimi中输入提示词&#xff1a; 你是一个Python编程专家&#xff0c;要完成一个爬取网页内容的Python脚本&#xff0c;具体步骤如下&#xff1a; 在F盘新建一个Excel文件&#xff1a;提示词…

JavaScript异步编程——09-Promise类的方法【万字长文,感谢支持】

Promise 类的方法简介 Promise 的 API 分为两种&#xff1a; Promise 实例的方法&#xff08;也称为&#xff1a;Promis的实例方法&#xff09; Promise 类的方法&#xff08;也称为&#xff1a;Promise的静态方法&#xff09; 前面几篇文章&#xff0c;讲的都是 Promise 实…

SpringBoot解决CORS跨域——WebMvcConfigurationSupport

前端请求后端报错了。 状态码&#xff1a;403 返回错误&#xff1a;Invalid coRs request 增加配置类WebMvcConfig Configuration public class WebMvcConfig extends WebMvcConfigurationSupport {Overridepublic void addCorsMappings(CorsRegistry registry) {// 允许跨域…

Python中tkinter编程入门4

在Python中tkinter编程入门3-CSDN博客中创建了Button控件&#xff0c;点击该控件就会产生一个点击事件&#xff0c;在创建Button控件时指定该点击事件的处理程序后&#xff0c;按键控件就会对用户的点击事件产生响应。 1 定义事件处理器 定义事件处理器就是一个自定义的函数。…

2024年小学生古诗文大会备考:吃透历年真题和知识点(持续)

根据往年的安排&#xff0c;2024年小学生古诗文大会预计这个月就将启动。该如何备考2024年小学生古诗文大会呢&#xff1f;根据往期的经验&#xff0c;只要吃透这些真题和背后的知识点&#xff0c;通过上海小学生古诗文大会的初选&#xff08;初赛&#xff09;一点问题都没有。…