跳至主要內容

string

张威大约 9 分钟c/c++c++基础

string

c风格字符串

两种定义字符串方式:

//1.数组定义
char str1[] = {'h', 'e', 'l', 'l', 'o', '\0'};//局部变量,栈空间
char str2[] = "hello";	//结尾自动包含\0,等价于str1
//2.指针定义
char* str3 = "world";	//文字常量区
  • 数组名char str1[]实际上是char * const str1只能修改数组值,不能修改指针指向
str2[0] = 'H';	
//str2 = nullptr;	//error

str3[0] = 'W';	//error,说明str3的内容是不可以被修改的,
//所以c++写c风格字符串最好写为const char* ,如果在修改内容编译器会报错提示
const char *str3 = "world";

size_t len1 = sizeof(str3);	//表示指针地址大小64位为8,32位为4
size_t len2 = strlen(str3);	//表示存储字符的个数
//如何测试系统是32位还是64位?
//sizeof(指针)

//size_t与int区别
//1. size_t 实际上是unsigned int
//2. size_t 具有跨平台的作用

字符串处理在程序中应用广泛,C风格字符串是以'\0'(空字符)来结尾的字符数组。对字符串进行操作的C函数定义在头文件<string.h>或中。常用的库函数如下:

//字符检查函数(非修改式操作)
size_t strlen( const char *str );//返回str的长度,不包括null结束符

//比较lhs和rhs是否相同。lhs等于rhs,返回0; lhs大于rhs,返回正数; lhs小于rhs,返回负数
int strcmp( const char *lhs, const char *rhs );
int strncmp( const char *lhs, const char *rhs, size_t count );

//在str中查找首次出现ch字符的位置;查找不到,返回空指针
char *strchr( const char *str, int ch );
//在str中查找首次出现子串substr的位置;查找不到,返回空指针
char *strstr( const char* str, const char* substr );

//字符控制函数(修改式操作)
char *strcpy(char *dest, const char *src);//将src复制给dest,返回dest
char *strncpy(char *dest, const char *src, size_t count);
char *strcat( char *dest, const char *src );//concatenates two strings
char *strncat( char *dest, const char *src, size_t count );

在使用时,程序员需要考虑字符数组大小的开辟,结尾空字符的处理,使用起来有诸多不便。

void test0()
{
    char str[] = "hello";
    char * pstr = "world";
    
    //求取字符串长度
    printf("%d\n", strlen(str));
    
    //字符串拼接
    char * ptmp = (char*)malloc(strlen(str) + strlen(pstr) + 1);
    strcpy(ptmp, str);
    strcat(ptmp, pstr);
    printf("%s\n", ptmp);
    
    //查找子串
    char * p1 = strstr(ptmp, "world");
    
    free(ptmp);
}
  • 注意:c/c++字符串使用的是双引号,数据库中字符串使用的是单引号

C++风格字符串

C++提供了std::string(后面简写为string)类用于字符串的处理。string类定义在C++头文件中,注意和头文件区分,其实是对C标准库中的<string.h>的封装,其定义的是一些对C风格字符串的处理函数。

尽管C++支持C风格字符串,但在C++程序中最好还是不要使用它们。这是因为C风格字符串不仅使用起来不太方便,而且极易引发程序漏洞,是诸多安全问题的根本原因。**与C风格字符串相比,string不必担心内存是否足够、字符串长度,结尾的空白符等等。**string作为一个类出现,其集成的成员操作函数功能强大,几乎能满足所有的需求。从另一个角度上说,完全可以string当成是C++的内置数据类型,放在和int、double等内置类型同等位置上。string类本质上其实是basic_string类模板关于char型的实例化。

#include<string> //包含头文件
using std::string;	//使用实体

我们先来看一个简单的例子:

void test1()
{
    //C风格字符串转换为C++风格字符串
    std::string s1 = "hello";
    std::string s2("world");
    
    //求取字符串长度
    cout << s1.size() << endl;
    cout << s1.length() << endl;
    
    //字符串的遍历
    for(size_t idx = 0; idx != s1.size(); ++idx) 
    {
        cout << s1[idx] << " ";
    }
    cout << endl;
    
    //字符串拼接
    std::string s3 = s1 + s2;	//两个C++字符串拼接
    std::string s4 = s1 + "hahaha";//与C字符串拼接
    std::string s5 = s1 + 'A';//与字符拼接
    s1.append(s2);	//s1 = helloworld
    cout << "s3 = " << s3 << endl;
    cout << "s4 = " << s4 << endl;
    cout << "s5 = " << s5 << endl;
    
    //查找子串
    size_t pos = s1.find("wor");	//5
    
    //截取子串
    std::string substr = s1.substr(pos);
    cout << "substr = " << substr << endl; //world
}

std::string提供了很多方便字符串操作的方法。

string对象的构造

首先来看一下string类型常用的构造函数

string();//默认构造函数,生成一个空字符串
string(const char * rhs);//通过c风格字符串构造一个string对象
string(const char * rhs, size_type count);//通过rhs的前count个字符构造一个string对象
string(const string & rhs);//复制拷贝构造函数
string(size_type count, char ch);//生成一个string对象,该对象包含count个ch字符
string(InputIt first, InputIt last);//以区间[first, last)内的字符创建一个string对象

string与C风格字符串的转换

C风格字符串转换为string字符串相对来说比较简单,通过构造函数即可实现。但由于string字符串实际上是类对象,其并不以空字符'\0'结尾,因此,string字符串向C风格字符串的转化是通过3个成员函数完成的,分别为:

const char *c_str() const;// 返回一个C风格字符串
const char *data() const;// c++11之后与c_str()效果一致

//字符串的内容复制或写入既有的C风格字符串或字符数组内
size_type copy(char* dest, size_type count, size_type pos = 0) const;

元素遍历和存取

string对象可以使用下标操作符[]和函数at()对字符串中包含的字符进行访问。需要注意的是操作符[]并不检查索引是否有效,如果索引超出范围,会引起未定义的行为。而at()会检查,如果使用at()的时候索引无效,会抛出out_of_range异常。

reference operator[]( size_type pos );// 返回下标为pos的元素
const_reference operator[]( size_type pos ) const;//

reference at( size_type pos );// 返回下标为pos的元素
const_reference at( size_type pos ) const;//

除此以外,还可以使用迭代器进行遍历访问

iterator begin();
const_iterator begin() const;

iterator end();
const_iterator end() const;

reverse_iterator rbegin();
const_reverse_iterator rbegin() const;

reverse_iterator rend();
const_reverse_iterator rend() const;

其示意图如下:

img
img

字符串的长度和容量相关

bool empty() const;
size_type size() const;
size_type length() const;
size_type capacity() const;
size_type max_size() const;

元素追加和相加

string &operator+=(const string & tr);
string &operator+=(CharT ch);
string &operator+=(const CharT* s);

string &append(size_type count, CharT ch);
string &append(const basic_string & str);
string &append(const CharT* s);
string &append(InputIt first, InputIt last);

//以下为非成员函数
string operator+(const string & lhs, const string & rhs);
string operator+(const string & lhs, const char* rhs);
string operator+(const char* lhs, const string & rhs);
string operator+(const string & lhs, char rhs);
string operator+(char lhs, const string & rhs);

提取子串

string substr(size_type pos = 0, size_type count = npos) const;

元素删除

iterator erase(iterator position);
iterator erase(const_iterator position);
iterator erase(iterator first, iterator last);

元素清空

void clear();

字符串比较

//非成员函数
bool operator==(const string & lhs, const string & rhs);
bool operator!=(const string & lhs, const string & rhs);
bool operator>(const string & lhs, const string & rhs);
bool operator<(const string & lhs, const string & rhs);
bool operator>=(const string & lhs, const string & rhs);
bool operator<=(const string & lhs, const string & rhs);

搜索与查找

//find系列:
size_type find(const basic_string & str, size_type pos = 0) const;
size_type find(const CharT* s, size_type pos = 0) const;
size_type find(const CharT* s, size_type pos, size_type count) const;
size_type find(char ch, size_type pos = npos ) const;

//rfind系列:
size_type rfind(const basic_string & str, size_type pos = 0) const;
size_type rfind(const CharT* s, size_type pos = 0) const;
size_type rfind(const CharT* s, size_type pos, size_type count) const;
size_type rfind(char ch, size_type pos = npos) const;

课堂代码

#include <string.h>//C的头文件
#include <iostream>
#include <string>//C++头文件

using std::cout;
using std::endl;
using std::string;

void test()
{	
    //C风格字符串两种定义方式
    //1.用数组定义
    //char str1[] = {'h', 'e', 'l', 'l', 'o'};
    //C风格字符串默认以'\0'结尾
    char  str1[] = "hello";
    char str2[] = "world";
    str1[0] = 'H';//const *p1
    /* str1 = nullptr;//error, * const */ 
    printf("str1 = %s\n", str1);
	//2.用指针定义
    const char *pstr = "hello, world";
    
    //debug定位小技巧,多使用printf打印信息
    /* printf("111\n"); */
    /* pstr[0] = 'H';//error */
    /* printf("222\n"); */
    pstr = nullptr;
    /* printf("333\n"); */
    
    /* size_t len2 = sizeof(pstr);//8,指针的大小 */
    /* size_t len3 = strlen(pstr);//13,以\0结尾 */
    size_t len1 = sizeof(str1);
    size_t len2 = sizeof(str2);
    printf("len1 = %lu\n", len1);
    printf("len2 = %lu\n", len2);

    size_t len = len1 + len2 - 1;
    char *pstr2 = static_cast<char *>(malloc(len));
    memset(pstr2, 0, sizeof(len));
    strcpy(pstr2, str1);
    strcat(pstr2, str2);
    printf("pstr2 = %s\n", pstr2);

    free(pstr2);
    pstr2 = nullptr;
}
#if 0
typedef struct A
{
int ia;
char cb;
}structAA;

structAA a;
a.ia = 10;
a.cb = 'c';
#endif

void test2()
{
    //C++        C
    string s1 = "hello";//C风格字符串可以转换为C++风格字符串
    string s2 = "world";
    string s3 = s1 + s2;
    cout << "s1 = " << s1 << endl
         << "s2 = " << s2 << endl
         << "s3 = " << s3 << endl;

    //从C++风格字符串转换为C风格字符串
    cout << endl;
    const char *pstr = s3.c_str();
    cout << "pstr = " << pstr << endl;

    //C++风格字符串的长度
    cout << endl;
    /* size_t len1 = sizeof(s3);//32,与string的底层实现有关,不同系统不同模型值不同 */
    size_t len2 = s3.size();
    size_t len3 = s3.length();
    /* cout << "len1 = " << len1 << endl; */
    cout << "len2 = " << len2 << endl;//10
    cout << "len3 = " << len3 << endl;//10

    //遍历C++风格字符串
    cout << endl;
    for(size_t idx = 0; idx != len2; ++idx)
    {
        cout << s3[idx] << "   ";
    }
    cout << endl;

    //C++风格字符串的拼接
    cout << endl;
    string s4 = s3 + "wuhan";
    cout << "s4 = " << s4 << endl;

    cout << endl;
    string s5 = s4 + 'A';
    cout << "s5 = " << s5 << endl;

    cout << endl;
    s5.append(s1);
    cout << "s5 = " << s5 << endl;

}
int main(int argc, char **argv)
{
    test2();
    return 0;
}

实现一个String类

#include <string.h>
#include <iostream>


using std::cout;
using std::endl;

class String
{
public:
    String()
    : _pstr(nullptr)
    /* : _pstr(new char[1]())//ok */
    {
        cout << "String()" << endl;
    }

    //pstr = nullptr
    String(const char *pstr)
    : _pstr(new char[strlen(pstr) + 1]())
    {
        cout << "String(const char *)" << endl;
        strcpy(_pstr, pstr);
    }

    String(const String & rhs)
    : _pstr(new char[strlen(rhs._pstr) + 1]())
    {
        cout << "String(const char *)" << endl;
        strcpy(_pstr, rhs._pstr);

    }

    //str1 = str2;
    String & operator=(const String & rhs)
    {
        cout << "String &operator=(const String &)" << endl;
        if(this != &rhs)//1、自复制
        {
            if(_pstr)
            {
               delete [] _pstr;//2、释放左操作数
               _pstr = nullptr;
            }

            _pstr = new char[strlen(rhs._pstr) + 1]();//3、深拷贝
            strcpy(_pstr, rhs._pstr);
        }

        return *this;//4、返回*this
    }

    ~String()
    {
        cout << "~String()" << endl;
        if(_pstr)
        {
            delete [] _pstr;
            _pstr = nullptr;
        }
    }

    void print() const
    {
        if(_pstr)
        {
            cout << _pstr << endl;
        }
    }

private:
    char * _pstr;

};

int main(void)
{
    String str1;
    str1.print();
#if 1
    String str2 = "Hello,world";
    String str3("wangdao");

    str2.print();       
    str3.print();   

    String str4 = str3;
    str4.print();

    str4 = str2;
    str4.print();
#endif
    return 0;

}