string
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;
其示意图如下:

字符串的长度和容量相关
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;
}