Skip to content

string容器

C风格字符串(以空字符结尾的字符数组)太过复杂难于掌握,不适合大程序的开发,所以C++标准库定义了一种string类,可变长字符串的处理类,定义在头文件<string>

string和c风格字符串对比

  • char *是一个指针,String是一个类 string封装了char *,管理这个字符串,是一个char *型的容器。
  • string封装了很多实用的成员方法
    查找find,拷贝copy,删除delete 替换replace,插入insert
  • 不用考虑内存释放和越界 string管理char *所分配的内存。每一次string的复制,取值都由string类负责维护,不用担心复制越界和取值越界等。

string容器常用操作

构造函数

cpp
// 创建一个空的字符串 例如: string str;
string();   
// 使用一个string对象初始化另一个string对象
string(const string& str);  
// 使用字符串s初始化
string(const char* s);  
// 使用n个字符c初始化 
string(int n, char c);

基本赋值操作

cpp
// char*类型字符串 赋值给当前的字符串
string& operator=(const char* s);
// 把字符串s赋给当前的字符串    
string& operator=(const string &s); 
// 字符赋值给当前的字符串
string& operator=(char c);  
// 把字符串s赋给当前的字符串
string& assign(const char *s);  
// 把字符串s的前n个字符赋给当前的字符串
string& assign(const char *s, int n);   
// 把字符串s赋给当前字符串
string& assign(const string &s);    
// 用n个字符c赋给当前字符串
string& assign(int n, char c);  
// 将s从start开始n个字符赋值给字符串
string& assign(const string &s, int start, int n);

存取字符操作

cpp
// 通过[]方式取字符
char& operator[](int n);
// 通过at方法获取字符
char& at(int n);

拼接操作

cpp
// 重载+=操作符,拼接另一个string字符串
string& operator+=(const string& str);
// 重载+=操作符,拼接另一个C风格字符串
string& operator+=(const char* str);
// 重载+=操作符,拼接一个字符
string& operator+=(const char c);
// 把字符串s连接到当前字符串结尾
string& append(const char *s);
// 把字符串s的前n个字符连接到当前字符串结尾
string& append(const char *s, int n);
// 同operator+=()
string& append(const string &s);
// 把字符串s中从pos开始的n个字符连接到当前字符串结尾
string& append(const string &s, int pos, int n);
// 在当前字符串结尾添加n个字符c
string& append(int n, char c);

查找和替换

cpp
// 查找str第一次出现位置,从pos开始查找
int find(const string& str, int pos = 0) const; 
// 查找s第一次出现位置,从pos开始查找
int find(const char* s, int pos = 0) const;  
// 从pos位置查找s的前n个字符第一次位置
int find(const char* s, int pos, int n) const;  
// 查找字符c第一次出现位置
int find(const char c, int pos = 0) const;  
// 查找str最后一次位置,从pos开始查找
int rfind(const string& str, int pos = npos) const;
// 查找s最后一次出现位置,从pos开始查找
int rfind(const char* s, int pos = npos) const;
// 从pos查找s的前n个字符最后一次位置
int rfind(const char* s, int pos, int n) const;
// 查找字符c最后一次出现位置
int rfind(const char c, int pos = 0) const; 
// 替换从pos开始n个字符为字符串str
string& replace(int pos, int n, const string& str); 
// 替换从pos开始的n个字符为字符串s
string& replace(int pos, int n, const char* s);

比较操作

cpp
/*
    compare函数在>时返回 1,<时返回 -1,==时返回 0。
    比较区分大小写,比较时参考字典顺序,排越前面的越小。
    大写的A比小写的a小。
*/

// 与字符串s比较
int compare(const string &s) const;
// 与字符串s比较
int compare(const char *s) const;

string子串

cpp
// 返回由pos开始的n个字符组成的字符串
string substr(int pos = 0, int n = npos) const;

插入和删除操作

cpp
// 插入字符串
string& insert(int pos, const char* s); 
// 插入字符串
string& insert(int pos, const string& str); 
// 在指定位置插入n个字符c
string& insert(int pos, int n, char c);
// 删除从Pos开始的n个字符 
string& erase(int pos, int n = npos);

string和C风格字符串转换

cpp
// string 转 char*
string str = "itcast";
const char* cstr = str.c_str();

// char* 转 string 
char* s = "itcast";
string str(s);

Tips

在C++中存在一个从const char *到string的隐式类型转换,却不存在从一个string对象到C_string的自动类型转换。对于string类型的字符串,可以通过c_str()函数返回string对象对应的c_string。

通常,

为了修改string字符串的内容,下标操作符[]at都会返回字符的引用。但当字符串的内存被重新分配之后,可能发生错误。

cpp
string s = "abcdefg";
char& a = s[2];
char& b = s[3];

a = '1';
b = '2';

cout << s << endl;
cout << (int *)s.c_str() << endl;

s = "pppppppppppppppppppppppp";

//a = '1';
//b = '2';

cout << s << endl;
cout << (int *)s.c_str() << endl;
小练习:写一个函数,函数内部将string字符串中的所有小写字母都变为大写字母。
cpp
#include <iostream>
#include <string>
using namespace std;

void upperstr(string& str) {
    for (auto& item : str) {
        item = toupper(item);
    }
}

int main(int argc, char *argv[]) {
    string str("I love china");
    cout << str << endl;

    upperstr(str);
    cout << str << endl;

    return 0;
}

数字转换

C++标准库提供了相关类对字符串和数字进行转换。

字符串流类sstream用于string的转换。

使用字符串流类需要包含#include <sstream>头文件。

istringstream表示字符串输入流,ostringstream表示字符串输出流。

cpp
#include <iostream>
#include <sstream>
using namespace std;

int main(int argc, char *argv[])
{
    //字符串转换为数字
    istringstream iss("3.14");
    double pi;
    if(iss >> pi)
    {
        cout << "PI = " << pi<< endl;
    }

    //数字转换为字符串
    ostringstream oss;
    if(oss << pi)
    {
        string PI = oss.str();
        cout << "PI = " << PI << endl;
    }
    return 0;
}

手动实现string

基础实现

cpp
#pragma once

#include <cstring>
#include <iostream>
#include <istream>
#include <ostream>

class MyString {
public:
    MyString() : _str(new char[1]) {
        _str[0] = '\0';
    }
    ~MyString() {
        delete[] _str;
    }
    MyString(const char *str) {
        const int len = strlen(str);
        _str = new char[len + 1];
        strcpy(_str, str);
        _str[len] = '\0';
    }

    // 成员函数使用了堆空间 实现拷贝构造和赋值重载以避免浅拷贝
    MyString(const MyString &s) {
        _str = new char[s.size()];
        strcpy(_str, s._str);
    }
    MyString &operator=(const MyString &s) {
        if (&s == this) {
            return *this;
        }

        delete[] _str;
        _str = new char[s.size()];
        strcpy(_str, s._str);

        return *this;
    }

    // 移动构造
    MyString(MyString &&s) noexcept : _str(std::move(s._str)) {
        s._str = nullptr;
    }
    // 移动赋值运算符重载
    MyString &operator=(MyString &&s) noexcept {
        if (&s == this) {
            return *this;
        }

        delete[] _str;

        this->_str = s._str;
        s._str = nullptr;

        return *this;
    }

    // 重载运算符
    bool operator>(const MyString &rhs) const {
        return strcmp(_str, rhs._str) > 0;
    }
    bool operator<(const MyString &rhs) const {
        return strcmp(_str, rhs._str) < 0;
    }
    bool operator==(const MyString &rhs) const {
        return strcmp(_str, rhs._str) == 0;
    }
    char &operator[](int index) {
        if (index < 0 || index >= size()) {
            throw std::out_of_range("index error");
        }
        return _str[index];
    }
    const char &operator[](int index) const {
        if (index < 0 || index >= size()) {
            throw std::out_of_range("index error");
        }
        return _str[index];
    }

    // API
    int size() const { return strlen(_str); }

    const char *c_str() const {
        return _str;
    }

private:
    // 重载输出运算符
    friend std::ostream &operator<<(std::ostream &out, const MyString &s);
    // 重载+运算符
    friend MyString operator+(const MyString &lhs, const MyString &rhs);

private:
    char *_str;
};

std::ostream &operator<<(std::ostream &out, const MyString &s) {
    std::cout << s._str;
    return out;
}

MyString operator+(const MyString &lhs, const MyString &rhs) {
    char *newStr = new char[strlen(lhs._str) + strlen(rhs._str) + 1];
    strcpy(newStr, lhs._str);
    strcat(newStr, rhs._str);

    // 1.一定要构造临时对象然后释放newStr,否则造成内存泄漏
    MyString temp(newStr);
    delete[] newStr;

    // 2.使用移动语义来优化
    return std::move(temp);
}
cpp
#include "my-string.h"

int main() {
    MyString s1("zhangsan");
    std::cout << s1 << std::endl;
    std::cout << s1.size() << std::endl;

    MyString s2 = s1;
    std::cout << s2 << std::endl;
    std::cout << s2.size() << std::endl;

    MyString s3(std::move(s1));
    std::cout << s3 << std::endl;
    std::cout << s3.size() << std::endl;

    MyString s4 = "lisi";
    std::cout << s4 << std::endl;
    std::cout << s4.size() << std::endl;

    MyString s5 = s3 + s4;
    std::cout << s5 << std::endl;
    std::cout << s5.size() << std::endl;

    MyString s6 = "hello" + s4;
    std::cout << s6 << std::endl;
    std::cout << s6.size() << std::endl;

    return 0;
}
bash
zhangsan
8
zhangsan
8
zhangsan
8
lisi
4
zhangsanlisi
12
hellolisi
9

添加迭代器

cpp
#pragma once

#include <cstring>
#include <iostream>
#include <istream>
#include <ostream>

class MyString {
public:
    MyString() : _str(new char[1]) {
        _str[0] = '\0';
    }
    ~MyString() {
        delete[] _str;
    }
    MyString(const char *str) {
        const int len = strlen(str);
        _str = new char[len + 1];
        strcpy(_str, str);
        _str[len] = '\0';
    }

    // 成员函数使用了堆空间 实现拷贝构造和赋值重载以避免浅拷贝
    MyString(const MyString &s) {
        _str = new char[s.size()];
        strcpy(_str, s._str);
    }
    MyString &operator=(const MyString &s) {
        if (&s == this) {
            return *this;
        }

        delete[] _str;
        _str = new char[s.size()];
        strcpy(_str, s._str);

        return *this;
    }

    // 移动构造
    MyString(MyString &&s) noexcept : _str(std::move(s._str)) {
        s._str = nullptr;
    }
    // 移动赋值运算符重载
    MyString &operator=(MyString &&s) noexcept {
        if (&s == this) {
            return *this;
        }

        delete[] _str;

        this->_str = s._str;
        s._str = nullptr;

        return *this;
    }

    // 重载运算符
    bool operator>(const MyString &rhs) const {
        return strcmp(_str, rhs._str) > 0;
    }
    bool operator<(const MyString &rhs) const {
        return strcmp(_str, rhs._str) < 0;
    }
    bool operator==(const MyString &rhs) const {
        return strcmp(_str, rhs._str) == 0;
    }
    char &operator[](int index) {
        if (index < 0 || index >= size()) {
            throw std::out_of_range("index error");
        }
        return _str[index];
    }
    const char &operator[](int index) const {
        if (index < 0 || index >= size()) {
            throw std::out_of_range("index error");
        }
        return _str[index];
    }

    // API
    int size() const { return strlen(_str); }

    const char *c_str() const {
        return _str;
    }

    // ✨ 自定义迭代器
    // 1.迭代器类实现
    class iterator {
    public:
        iterator(char *p = nullptr) : _pi(p) {}
        bool operator!=(const iterator &it) { return _pi != it._pi; }
        void operator++() { ++_pi; } // 迭代器遍历时一般都使用前置++,注意返回值
        char *operator++(int) { return _pi++; }
        char &operator*() { return *_pi; }

    private:
        char *_pi;
    };

    // 2.提供begin和end函数
    iterator begin() { return iterator(_str); }
    iterator end() { return iterator(_str + size()); }

private:
    // 重载输出运算符
    friend std::ostream &operator<<(std::ostream &out, const MyString &s);
    // 重载+运算符
    friend MyString operator+(const MyString &lhs, const MyString &rhs);

private:
    char *_str;
};

std::ostream &operator<<(std::ostream &out, const MyString &s) {
    std::cout << s._str;
    return out;
}

MyString operator+(const MyString &lhs, const MyString &rhs) {
    char *newStr = new char[strlen(lhs._str) + strlen(rhs._str) + 1];
    strcpy(newStr, lhs._str);
    strcat(newStr, rhs._str);

    // 1.一定要构造临时对象然后释放newStr,否则造成内存泄漏
    MyString temp(newStr);
    delete[] newStr;

    // 2.使用移动语义来优化
    return std::move(temp);
}
cpp
#include "my-string.h"

int main() {
    MyString s1("zhangsan");
    std::cout << s1 << std::endl;
    std::cout << s1.size() << std::endl;

    for (auto it = s1.begin(); it != s1.end(); ++it) {
        std::cout << *it;
    }
    std::cout << std::endl;

    for (auto it = s1.begin(); it != s1.end(); it++) {
        std::cout << *it;
    }
    std::cout << std::endl;

    for (auto it : s1) {
        std::cout << it;
    }
    std::cout << std::endl;

    return 0;
}
bash
zhangsan
8
zhangsan
zhangsan
zhangsan