C/C++中的字符串
C语言中的字符串
C语言的字符串很好理解,本身是一个数组,只不过对字符串的最后一个元素有特别的要求,最后一个元素必须是null。但是C语言也提供了一系列的库函数来操作字符串,避免自己去实现相关的操作。
库函数
C语言的库函数放在一个叫cstring的头文件中,其实它是对string.h的封装,因为C++有命名空间的概念,所以ctring就是加了一层命名空间。对于写C程序的人来说,只需要包含string.h即可。
// 精确复制num个字节 void *memcpy(void *destination, const void *source, size_t num); // memmove更强大的地方在于两个内存段可以有交叉, // memcpy则不能保证在有交叉的情况还能正确工作。 void *memmove(void *destination, const void *source, size_t num); // 目标数组要能够装得下新的字符串,也就是需要预先保证有足够的空间 char *strcpy(char *destination, const char *source); // 最多复制num个字符,具体长度有源字符串的长度决定 char *strncpy(char *destination, const char *source, size_t num); // 调用者首先要保证目标有足够的空间,其次还需要保证没有交叉 char *strcat(char *destination, const char *source); char *strncat(char *destination, const char *source, size_t num); // 简单的逐字节比较 int memcmp(const void *ptr1, const void *ptr2, size_t num); // 字符串比较 int strcmp(const char *str1, const char *str2); // 也是字符串比较,但行为依赖于LC_COLLATE int strcoll(const char *str1, const char *str2); // 只比较前num个字符 int strncmp(const char *str1, const char *str2, size_t num); // 根据LC_COLLATE将源字符串转换为目标字符串 size_t strxfrm(char *destination, const char *source, size_t num); // 用于搜索value的第一次出现位置,如果没有找到返回NULL const void *memchr(const void *ptr, int value, size_t num); void *memchr(void *ptr, int value, size_t num); // 用来搜索指定字符的第一次出现位置 const char *strchr(const char *str, int character); char *strchr(char *str, int character); // 查找最后一次出现,如果没有找到返回NULL,注意可以用来查找null const char *strrchr(const char *str, int character); char *strrchr(char *str, int character); // 用来搜索str2中任意一个字符的出现,返回值是前面没有在str2中出现的字符个数 // 因为搜索包含null字符,所以即便没有出现,也会返回str1的长度 size_t strcspn(const char *str1, const char *str2); // 和strcspn的区别有两点,第一是查找从str1开头有多少个字符属于str2, // 第二是不包括对null字符的搜索。 size_t strspn(const char *str1, const char *str2); // 也是搜索str2中任意字符的出现,只不过换成了找到的指针, // 如果没有找到就返回NULL const char *strpbrk(const char *str1, const char *str2); char *strpbrk(char *str1, const char *str2); // 用来查找整个str2在str1第一次出现,如果没有找到就返回NULL const char *strstr(const char *str1, const char *str2); char *strstr(char *str1, const char *str2); // 将str分割,分割字符由delimiters决定,其中出现的任意字符都作为分割符 char *strtok(char *str, const char *delimiters); void *memset(void *ptr, int value, size_t num); char *strerror(int errnum); // 将错误码转换为字符串 size_t strlen(const char *str); // 计算字符串长度
long int strtol(const char *nptr, char **endptr, int base); long long int strtoll(const char *nptr, char **endptr, int base); unsigned long int strtoul(const char *nptr, char **endptr, int base); unsigned long long int strtoull(const char *nptr, char **endptr, int base);
调用之后通常需要检查两个值的有效性,一个是nptr,需要确保其第一个字符不能为null,一个是endptr,要确保*endptr为null,也就是说完整的将nptr转换为数字,如果nptr本身不需要完整转换,也可以检查endptr是否走到指定位置。
C++中的字符串
C++中string是以一个库的形式提供的,由于C++引入了命名空间的概念,所以string实际处于命名空间std中。为了避免累赘,这里忽略命名空间的概念,直接引用string。
要使用string,必须要要包含头文件<string>。接下来可以看一下string类的定义:
typedef basic_string<char> string;
从这里可以看到,实际上string类是模板类basic_string的一个实例,用char字符来实例化,如果熟悉basic_string可以利用它来实现其他类型的string,也就是除了字符串以外还能轻松实现其他类型的串。
既然提到basic_string不妨看一下它长什么样子:
template < class charT, class traits = char_traits<charT>, class Alloc = allocator<charT> > class basic_string;
- charT
设计者对这个模板参数提出了一个要求,必须是非数组的POD类型。
因为我们要实现一个串,所以不让串的元素本身是串,这是比较容易理解的。另一方面要求是POD类型,称之为Plain Old Type,就是源于C的那些数据类型。
- traits
- 定义一组特征。
- Alloc
- 提供分配内存的方法。
一般情况不会去修改traits和Alloc,因为默认的就实现的很好了,如果我们想使用其他类型的串,就可以简单添加typedef实现。例如标准库里面已经提供了如下类型:
typedef basic_string<wchar_t> wstring; typedef basic_string<char16_t> u16string; typedef basic_string<char32_t> u32string;
数据成员
我们可以认为数据成员已经被完全封装,虽然有一个npos可以访问,但它是一个全局静态常量,其含义是size_type能表示的最大值。
static const size_type npos = -1;
函数成员
string(); string(const string &str); // 从给定str的下标pos开始复制最多len个字符。 string(const string &str, size_t pos, size_t len = npos); string(const char *s); // 从字符串复制最多n个字符。 string(const char *s, size_t n); string(size_t n, char c); // 从迭代器复制。 template <class InputIterator> string(InputIterator first, InputIterator last);
析构函数对使用者来说其实没有什么需要注意的,只需要在动态申请是能记得释放就可以了。
string &operator=(const string &str); string &operator=(const char *s); string &operator=(char c);
迭代器方法和其他容器类几乎一样,如果熟悉vector就知道有哪些: begin(),end(),rbegin(),rend(),cbegin(),cend(),crbegin(),crend()。其中r的含义是reverse,而c的含义是const。
size_t size() const; // 就是总共多少个字符 size_t length() const; // 字符串长度,同size() size_t max_size() const; // 系统可以分配的最大长度 void resize(size_t n); void resize(size_t n, char c); // 增加的部分用c填充 size_t capacity() const; // 当前分配到的内存长度 void clear(); // 将字符串擦除,长度变为0 bool empty() const; // 用于检查是否为空字符串 void reserve(size_t n = 0); // 设置预留长度 void shrink_to_fit(); // 清除预留长度
- resize()
- 如果往小的调整,实际上不会释放内存,只会减少字符串长度,如果要扩大,增加字符串长度,如果超出capacity则会重新分配内存。
- reserve()
- 在请求减少容量的时候并不一定会真的减少容量,这取决于编译器的实现。 shrink_to_fit()同样如此。
下标操作有[index]和at(index)两种方法,后者会检查是否越界,而前者不会检查。 back()和front()用于返回最后一个和第一个字符。
string &operator+=(const string &str); string &operator+=(const char *s); string &operator+=(char c); string &append(const string &str); string &append(const string &str, size_t subpos, size_t sublen); string &append(const char *s); string &append(const char *s, size_t n); string &append(size_t n, char c); template <class InputIterator> string &append(InputIterator first, InputIterator last); void push_back(char c); string &assign(const string &str); string &assign(const string &str, size_t subpos, size_t sublen); string &assign(const char *s); string &assign(const char *s, size_t n); string &assign(size_t n, char c); template <class InputIterator> string &assign(InputIterator first, InputIterator last); string &insert(size_t pos, const string &str); string &insert(size_t pos, const string &str, size_t subpos, size_t sublen); string &insert(size_t pos, const char *s); string &insert(size_t pos, const char *s, size_t n); string &insert(size_t pos, size_t n, char c); void insert(iterator p, size_t n, char c); iterator insert(iterator p, char c); template <class InputIterator> void insert(iterator p, InputIterator first, InputIterator last); string &erase(size_t pos = 0, size_t len = npos); iterator erase(iterator p); iterator erase(iterator first, iterator last); string &replace(size_t pos, size_t len, const string &str); string &replace(iterator i1, iterator i2, const string &str); string &replace(size_t pos, size_t len, const string &str, size_t subpos, size_t sublen); string &replace(size_t pos, size_t len, const char *s); string &replace(iterator i1, iterator i2, const char *s); string &replace(size_t pos, size_t len, const char *s, size_t n); string &replace(iterator i1, iterator i2, const char *s, size_t n); string &replace(size_t pos, size_t len, size_t n, char c); string &replace(iterator i1, iterator i2, size_t n, char c); template <class InputIterator> string &replace(iterator i1, iterator i2, InputIterator first, InputIterator last); void swap(string &str); void pop_back();
复合加法,也就是+=实际上是在尾部添加,和append()一样,push_back()也是向尾部添加。
const char *c_str() const; // 获取C字符串 const char *data() const; allocator_type get_allocator() const; size_t copy(char *s, size_t len, size_t pos = 0) const; size_t find(const string &str, size_t pos = 0) const; size_t find(const char *s, size_t pos = 0) const; size_t find(const char *s, size_t pos, size_t n) const; size_t find(char c, size_t pos = 0) const; size_t find_first_of(const string &str, size_t pos = 0) const; size_t find_first_of(const char *s, size_t pos = 0) const; size_t find_first_of(const char *s, size_t pos, size_t n) const; size_t find_first_of(char c, size_t pos = 0) const; string substr(size_t pos = 0, size_t len = npos) const; int compare(const string &str) const; int compare(size_t pos, size_t len, const string &str) const; int compare(size_t pos, size_t len, const string &str, size_t subpos, size_t sublen) const; int compare(const char *s) const; int compare(size_t pos, size_t len, const char *s) const; int compare(size_t pos, size_t len, const char *s, size_t n) const;
- find()
如果没找到,返回npos。
rfind()
和find()形式相同,默认参数是npos。find_first_of()系列包括find_last_of()、find_first_not_of()、 find_last_not_of()。原型完全一致,find_first_of()用于查找指定串中的任意一个字符的首次出现, find_first_not_of()查找指定串以外任意一个字符的首次出现。
库函数
库函数用于操作string但是它不属于成员函数。
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); bool operator==(const string &lhs, const string &rhs); bool operator==(const char *lhs, const string &rhs); bool operator==(const string &lhs, const char *rhs); void swap(string &x, string &y); istream &operator>>(istream &is, string &str); ostream &operator<<(ostream &os, const string &str); istream &getline(istream &is, string &str, char delim); istream &getline(istream &is, string &str);
比较操作符==、!=、<、<=、>、>=具有完全相同的函数原型和用法,因此只需要参考==即可。