C++标准程序库之string

首先,我们对术语“string”的定义是:C++标准程序库中某个字符串类型(string或wstring)的对象。至于一般字符串,也就是char* 或const char*,我们用术语“C-string”来表示。

1.引言

C++标准程序库中的string class使你可以将string当做一个一般类型而不会令用户感觉有任何问题。你可以像对待基本类型那样地复制、赋值和比较string,再也不必担心内存是否足够、占用的内存实际长度等问题,只需运用操作符操作函数即可,例如以=进行赋值动作,以==进行比较动作,以+进行串联动作。

简而言之,C++标准程序库对于string的设计思维就是,让它的行为尽可能像基本类型,不会再操作上引起什么麻烦。

注意,字符串字面常数(例如,”hello”)会被转为const char*。为了向下兼容,它们可以被隐式转换为char*,不过这种转换并不值得赞赏。

2.string class描述

(1)string各种相关类型

  • 头文件
    使用string需要包含头文件<string>。一如既往,所有标识符都定义于命名空间std之中。
  • 模板类别basic_string<>
    在<string>之中,basic_string<>被定义为所有字符串类型的基本模板类别:

    此一类别将字符类型、字符类型特性(traits)、内存模型(memory model)加以参数化:
    第一个参数是单个字符所属类型;
    第二个参数是个特性类别(traits class),提供字符串类别中所有的字符核心操作。此种特性类别规定了“赋值字符”或“比较字符”的做法,如果没有指定它,就会根据现有的字符类型采用缺省的特性类别。
    第三个参数定义了字符串类型所采用的内存模式,通常设定为“缺省的内存模型allocator”。
  • string类型和wstring类型
    C++标准程序库提供了两个basic_string<>特化版本:

    • string是针对char而预先定义的特化版本:
      namespace std {
      typedef basic_string<char> string;
      }
    • wstring是针对wchar_t而预先定义的特化版本:
      namespace std {
      typedef basic_string<wchar_t> wstring;
      }

(2)操作函数

1)字符串的各种操作函数:

2)字符串操作函数的参数:STL提供了很多字符串操作函数。其中许多往往具有数个重载版本,分别以一个、两个或三个参数来指定新值。

3)注意,只有在单参数版本中,才将char*字符’\0’当做字符串结尾特殊符号来处理,其他所有情况下’\0’都不被视为特殊字符:

因此,一般而言一个字符串内可以包含任何字符,甚至可以包含二进制文件的内容。

(3)string构造函数和析构函数

注意,不能以单一字符来初始化字符串:

这表示编译器提供了一个从const char*到string的自动类型转换功能,但不存在一个从char到string的自动类型转换功能。

(4)string和C-string

1)C++ Standard将字符串字面常数的类型由char*改为const char*。为了提供向下兼容性,C++ Standard规定了一个颇有争议的隐式转换,可从const char*隐式转换为char*。

2)由于字符串字面常数的类型并非string,因此在新的string object和传统的C-string之间必须存在一种强烈关系:在“string和string-like object共通的操作场合”(例如比较、追加、插入等动作)都应该使用C-string。或者具体地说,存在一个从const char*到string的隐式类型转换。然后却不存在一个从string object到C-string的自动类型转换。(c_str()可以得到“string对应的C-string”,所得结果和“以’\0’为结尾的字符数组一样)
运用copy(),可以将字符串内容复制或写入既有的C-string或字符数组内。

3)注意,’\0’在string之中并不具有特殊意义,但在一般C-string中却用来标识字符串结束。在string中,字符’\0’和其它字符的地位完全相同。

4)有三个函数可以将字符串内容转换为字符数组或C-string:

  • data():以字符数组的形式返回字符串内容。由于并未追加’\0’字符,所以返回类型并非有效的C-string。
  • c_str():以C-string形式返回字符串内容,也就是在尾端添加’\0’字符。
  • copy():将字符串内容复制到“调用者提供的字符数组”中。不添加’\0’字符。

(5)大小size和容量capacity

  • 一个string存在三种“大小”

1)size()和length()
返回string中出现的字符个数。上述两个函数等效。

2)max_size()
此函数返回一个string最多能够包含的字符数。一个string通常包含一块单独内存区块内的所有字符,所有可能跟PC机器本身的限制有关系。

3)capacity()
重新分配内存之前,string所能包含的最大字符数。

  • 让string拥有足够的容量是很重要的:其一,重新分配会造成所有指向string的reference、pointers、iterators失效;其二,重新分配(reallocation)很耗时。

1)reserve()使你得以预留一定容量:
string s;
s.reserve(80);

2)容量概念应用于string和应用于vector是相同的,但有一个显著差异:对string来说可以调用reserve()来缩减实际容量;而对vector来说却不可以。

(6)元素存取

两种方法可以访问单一字符:subscript下标和成员函数at()。

  • operator[]并不检查索引是否有效,at()则会检查。如果调用at()时指定的索引无效,系统会抛出out_of_range异常。
  • 对于operator[]的const版本,最后一个字符的后面位置也是有效的。此时的实际字符数是有效索引。在此情况下operator[]返回值是“由char类型之default构造函数所产生”的字符。因此,对于类型string的对象,返回值为’\0’。举例说明:

    (7)比较

string支持常见的比较操作符,操作数可以是string或C-string。

1)可以使用<, <=, >, >=来比较string,得到的结果是根据“当前字符特性”将字符依字典顺序逐一比较。

2)也可以使用成员函数compare()来比较子字符串,此函数针对一个string可使用多个参数进行处理。compare()返回的是整数值而非布尔值。返回值意义如下:0表示相等,小于0表示小于,大于0表示大于:

(8)更改内容

可以使用不同的成员函数或操作符来更改字符串内容。

1)赋值

可使用operator=赋新值,如果需要多个参数来描述新值,可采用成员函数assign()

2)交换swap()

3)令string成空

s = ” “;
s.clear();
s.erase();

4)插入和移除字符

  • string提供了许多成员函数用于插入、移除、替换、擦除字符。另有operator+=, append()push_back()可添加字符。

  • 成员函数insert()也可以插入字符,使用insert()时需知道插入位置的索引:

    注意,insert()不接受“索引+单独字符”的参数组合,必须传入一个string或一个额外数字:

    由于insert()具有以下重载形式,容易导致模棱两可的现象:

    string的size_type通常被定义为unsigned, string的iterator通常被定义为char*。所以,如下调用是错误的:
    s.insert(0, 1, ‘  ‘);    //error:ambiguous
    这种情况下,第一个参数0有两种转换可能,为了获得正确操作,你必须如此:
    s.insert( (string::size_type)0, 1, ‘  ‘);    //OK
  • 成员函数erase()用来移除字符,成员函数replace()用来替换字符:

(9)子串和字符串接合(concatenation)

成员函数substr()从string身上提取子字符串:

operator+把两个string(或C-string,但+号两边至少有一个是string)接合起来:

输出如下:
i18n means: internationalization

(10)I/O操作符

string定义了常用的I/O操作符:

  • operator >> 从input stream读取一个string。
  • operator << 把一个string写到output stream中。

这些操作符的使用方法和面对一般C-string时相同。更明确地说,operator>>的执行方式如下:
1)如果设置了skipws标志,则跳过开头空格。
2)持续读取所有字符,直到发生以下情形之一:
下一个字符为空格符
stream不再处于good状态(例如遇到end-of-file)
stream的width()结果大于0,而目前已读出width()个字符
以读取max_size()个字符
3)stream width()被设为0

string class在命名空间std内还提供了一种用于逐行读取的特殊函数——std::getline()。该函数读取所有字符,包括开头的空格符,知道遇到分行符号或end-of-file。

(11)搜索和查找

string提供了许多用于搜索和查找字符及字符串的函数。另外,如果配上迭代器,STL的所有搜寻算法都可派上用场。

string搜寻函数:

所有搜寻函数都返回符合搜寻条件之字符区间内的第一个字符的索引。如果搜寻不成功,则返回npos。这些受损函数都采用下面的参数方案:

  • 第一个参数总是被搜寻的对象
  • 第二个参数(可有可无)指出string内的搜寻起点
  • 第三个参数(可有可无)指出搜寻的字符个数

不幸的是上面这个参数方案与其他string相关函数不同。其他string函数的第一个参数是起点索引,随后是数值和长度。特别要指出的是,每个搜寻函数都以下面的参数进行重载:

  • const string& value
    搜寻对象为value(一个string)
  • const string& value, size_type idx
    从*this的idx索引位置开始,搜寻value(一个string)
  • const char* value
    搜寻value(一个C-string)
  • const char* value, size_type idx
    从*this的idx索引位置开始,搜寻value(一个C-string)
  • const char* value, size_type idx, size_type value_len
    从*this的idx索引位置开始,搜索value(一个C-string)内的前value_len个字符组成的字符区间。value内的null字符(‘\0’)将不复特殊意义。
  • const char value
    搜寻value(一个字符)
  • const char value, size_type idx
    从*this的idx索引位置开始,搜寻value(一个字符)

(12)数值npos的意义

如果搜寻函数失败,就会返回string::npos。考虑如下示例:

1)只有当“substring”不是s的子字符串时,if语句才会得到true。使用string的npos值及其类型要格外小心:若要检查返回值,一定要使用类型string::size_type,不能以int或unsigned作为返回值类型;否则返回值与string::npos之间的比较可能无法正确执行。

2)这是因为npos被设计为-1:

3)而size_type需为无正负号整数类型。于是-1被转换为无正负号整数类型,npos也就成了该类型的最大无符号值。不过实际数值还是取决于类型size_type的实际定义。不幸的是,这些最大值都不相同,比如(unsigned long)-1和(unsigned short)-1不同。

4)因此,比较式idx == string::npos,如果idx值为-1,由于idx和字符串string::npos类型不同,比较结果可能得到false。

(13)string对迭代器的支持

1)string是字符的有序群集。所以C++标准程序库为string提供了相应的接口,以便字符串当做STL容器使用。

2)string迭代器是random access迭代器,也就是说它支持随机存取,所以任何一个STL算法都可与它搭配。

3)string_iter.cpp

输出:

发表评论

电子邮件地址不会被公开。 必填项已用*标注