zoukankan      html  css  js  c++  java
  • 【C++ Primer每日刷】之三 标准库 string 类型


    标准库 string 类型

     

     

    string 类型支持长度可变的字符串。C++ 标准库将负责管理与存储字符相关的内存,以及提供各种实用的操作。标准库string 类型的目的就是满足对字符串的一般应用。

     

    与其它的标准库类型一样,用户程序要使用 string 类型对象。必须包括相关头文件。假设提供了合适的 using 声明,那么编写出来的程序将会变得简短些:

     

     

    #include <string>

    using std::string;

     

     

    1.1 string 对象的定义和初始化

     

     

    string 标准库支持几个构造函数。

    构造函数是一个特殊成员函数。定义怎样初始化该类型的对象。表中列出了几个 string 类型经常使用的构造函数。当没有明白指定对象初始化式时。系统将使用默认构造函数。

     

    几种初始化string 对象的方式:

     

     

    string s1; 默认构造函数s1 为空串

    string s2(s1); 将 s2初始化为 s1 的一个副本

    string s3("value"); 将 s3 初始化为一个字符串字面值副本

    string s4(n, 'c'); 将 s4 初始化为字符 'c' 的 n 个副本

     

     

     

    1.2 string 对象的读写

     

    我们已在前面学习了用 iostream 标准库来读写内置类型的值。如 int double 等。相同地,也能够用 iostream 和 string 标准库,使用标准输入输出操作符来读写 string 对象:

     

    // Note: #include and using declarationsmust be added to compile this code

    int main()

    {

    string s; // empty string

    cin >> s; // readwhitespace-separated string into s

    cout << s << endl; // write sto the output

    return 0;

    }

     

    以上程序首先定义命名为 s 的string 第二行代码:

    cin >> s; // readwhitespace-separated string into s

    从标准输入读取 string 并将读入的串存储在 s 中。string 类型的输入操

    作符:

     

    • 读取并忽略开头全部的空白字符(如空格,换行符,制表符)。

    • 读取字符直至再次遇到空白字符,读取终止。

    假设给定和上一个程序相同的输入。则输出的结果是"HelloWorld!"(注意到开头和结尾的空格),则屏幕上将输出"Hello"。而不含不论什么空格。输入和输出操作的行为与内置类型操作符基本类似。

    尤其是,这些操作符返回左操作数作为运算结果。因此,我们能够把多个读操作或多个写操作放在一起:

     

    string s1, s2;

    cin >> s1 >> s2; // read firstinput into s1, second into s2

    cout << s1 << s2 << endl;// write both strings

     

    假设给定和上一个程序相同的输入。则输出的结果将是:

     

    HelloWorld!

     

    对于上例,编译时必须加上 #include 来标示 iostream 和 string 标准库。以及给出用到的全部标准库中的名字(如 string。cin。cout,endl)的 using 声明。

     

    读入未知数目的string 对象

    和内置类型的输入操作一样,string 的输入操作符也会返回所读的数据流。因此,能够把输入操作作为推断条件。这与我们在 1.4.4 节读取整型数据的程序做法是一样的。以下的程序将从标准输入读取一组string 对象。然后在标准输出上逐行输出:

     

    int main()

    {

    string word;

    // read until end-of-file, writing eachword to a new line

    while (cin >> word)

     

     

    上例中。用输入操作符来读取 string 对象。

    该操作符返回所读的 istream 对象,并在读取结束后,作为 while 的推断条件。假设输入流是有效的,即还未到达文件尾且未遇到无效输入,则运行 while 循环体。并将读取到的字符串输出到标准输出。假设到达了文件尾。则跳出while 循环。

     

     

     

    使用getline 读取整行文本

     

     

    另外另一个实用的 string IO 操作:getline。

    这个函数接受两个參数:

     

    一个输入流对象和一个 string 对象。

    getline 函数从输入流的下一行读取,并保存读取的内容到不包含换行符。

    和输入操作符不一样的是,getline 并不忽略行开头的换行符。

    仅仅要 getline 遇到换行符。即便它是输入的第一个字符,getline 也将停止读入并返回。假设第一个字符就是换行符,则 string 參数将被置为空 string。

     

    getline 函数将 istream參数作为返回值。和输入操作符一样也把它用作推断条件。比如,重写前面那段程序,把每行输出一个单词改为每次输出一行文本:

     

     

    int main()

    {

    string line;

    // read line at time until end-of-file

    while (getline(cin, line))

    cout << line << endl;

    return 0;

    }

     

     

    因为 line 不含换行符,若要逐行输出须要自行加入。

    照常,我们用 endl 来输出一个换行符并刷新输出缓冲区。

     

    因为 getline 函数返回时丢弃换行符。换行符将不会存储

    在 string 对象中。

     

    1.3 string 对象的操作

     

    s.empty() 假设 s 为空串,则返回 true。否则返回 false。

     

    s.size() 返回 s 中字符的个数

     

    s[n] 返回 s 中位置为 n 的字符,位置从 0 開始计数

     

    s1 + s2 把 s1 和s2 连接成一个新字符串。返回新生成的字符串

     

    s1 = s2 把 s1 内容替换为 s2 的副本

     

    v1 == v2 比較 v1 与 v2 的内容,相等则返回 true,否则返回 false

     

    !=, <, <=, >, and >= 保持这些操作符惯有的含义

     

     

     

     

    string 的 size 和 empty 操作

     

    string 对象的长度指的是string 对象中字符的个数,能够通过 size 操

    作获取:

     

    int main()

    {

    string st("The expense ofspirit ");

    cout << "The size of "<< st << "is " << st.size()

    << " characters, including thenewline" << endl;

    return 0;

    }

     

    编译并执行这个程序,得到的结果为:

     

    The size of The expense of spirit

    is 22 characters, including the newline

     

     

    了解 string 对象是否空是实用的。一种方法是将 size 与 0 进行比較:

     

    if (st.size() == 0)

     

    // ok: empty

     

    本例中,程序猿并不须要知道 string 对象中有多少个字符,仅仅想知道 size 是否为 0。用 string的成员函数 empty() 能够更直接地回答这个问题:

     

    if (st.empty())

    // ok: empty

     

     

    empty() 成员函数将返回bool(2.1 节),假设 string 对象为空则返

    回 true 否则返回 false。

     

    string::size_type 类型

     

    从逻辑上来讲。size() 成员函数似乎应该返回整形数值。或如 2.2 节“建议”中所述的无符号整数。

    但其实。size 操作返回的是 string::size_type 类型的值。

    我们须要对这样的类型做一些解释。

     

    string 类类型和更多库类型都定义了一些配套类型(companion type)。通过这些配套类型,库类型的使用就能与机器无关(machine-independent)。size_type 就是这些配套类型中的一种。它定义为与 unsigned 型(unsigned int 或 unsigned long)具有同样的含义,并且可以保证足够大可以存储随意 string 对象的长度。为了使用由 string 类型定义的 size_type 类型是由 string 类定义。

     

    尽管我们不知道 string::size_type 的确切类型,但能够知道它是 unsigned 型(2.1.1 节)。对于随意一种给定的数据类型,它的 unsigned 型所能表示的最大正数值比相应的 signed 型要大。这个事实表明 size_type 存储的 string 长度是 int 所能存储的两倍。

     

    使用 int 变量的还有一个问题是,有些机器上 int 变量的表示范围太小。甚至无法存储实际并不长的 string 对象。如在有 16 位 int 型的机器上。int 类型变量最大仅仅能表示 32767 个字符的 string 个字符的 string 对象。而能容纳一个文件内容的 string 对象轻易就会超过这个数字。因此。为了避免溢出。保存一个 stirng 对象 size 的最安全的方法就是使用标准库类型 string::size_type。

     

     

    string 关系操作符

     

    string 类定义了几种关系操作符用来比較两个 string 值的大小。

    这些操作符实际上是比較每一个 string 。

     

    string对象比較操作是区分大写和小写的。即同一个字符的大写和小写形式被觉得是两个不同的字符。在多数计算机上,大写的字母位于小写之前:不论什么一个大写之母都小于随意的小写字母。

     

     

    == 操作符比較两个 string 对象,假设它们相等,则返回 true。两个 string 对象相等是指它们的长度同样,且含有同样的字符。标准库还定义了 != 操作符来測试两个 string 对象是否不等。关系操作符 <。<=,>,>= 分别用于測试一个 string 对象是否小于、小于或等于、大于、大于或等于还有一个 string 对象:

     

    string big = "big", small ="small";

    string s1 = big; // s1 is a copy of big

    if (big == small) // false

    // ...

    if (big <= s1) // true, they're equal,so big is less than or

    equal to s1

    // ...

     

     

    关系操作符比較两个 string 对象时採用了和(大写和小写敏感的)字典排序同样的策略:

     

    • 假设两个 string 对象长度不同,且短的 string 对象与长的 string 对

    象的前面部分相匹配。则短的 string 对象小于长的 string 对象。

    • 假设 string 对象的字符不同,则比較第一个不匹配的字符。string

    举例来说,给定 string 对象;

     

    string substr = "Hello";

    string phrase = "Hello World";

    string slang = "Hiya";

    则 substr 小于 phrase,而 slang 则大于 substr 或phrase

     

     

    string 对象的赋值

     

    整体上说。标准库类型尽量设计得和基本数据类型一样方便易用。因此。大多数库类型支持赋值操作。对 string 对象来说。能够把一个 string 对象赋值给还有一个 string 对象;

     

    // st1 is an empty string, st2 is a copy ofthe literal

    string st1, st2 = "The expense ofspirit";

    st1 = st2; // replace st1 by a copy of st2

     

    赋值操作后,st1 就包括了 st2 串全部字符的一个副本。

    大多数 string 库类型的赋值等操作的实现都会遇到一些效率上的问题。但

    值得注意的是。从概念上讲,赋值操作确实须要做一些工作。它必须先把 st1 占

    用的相关内存释放掉。然后再分配给 st2 足够存放 st2 副本的内存空间。最后

    把 st2 中的全部字符拷贝到新分配的内存空间。

     

     

    两个string 对象相加

    多个)string 对象能够通过使用加操作符 + 或者复合赋值操作符 +=

    (1.4.1 节)连接起来。

    给定两个string 对象:

    string s1("hello, ");

    string s2("world ");

    以下把两个 string 对象连接起来产生第三个 string 对象:

    string s3 = s1 + s2; // s3 is hello,world

    假设要把 s2 直接追加到 s1 的末尾,能够使用 += 操作符:

    s1 += s2; // equivalent to s1 = s1 + s2

     

     

    和字符串字面值的连接

     

     

    上面的字符串对象 s1 和 s2 直接包括了标点符号。

    也能够通过

    将 string 对象和字符串字面值混合连接得到相同的结果:

     

    string s1("hello");

    string s2("world");

    string s3 = s1 + ", " + s2 +" ";

     

    当进行 string 对象和字符串字面值混合连接操作时,+ 操作符的左右操作

    数必须至少有一个是 string 类型的:

     

    string s1 = "hello"; // nopunctuation

    string s2 = "world";

    string s3 = s1 + ", "; // ok:adding a string and a literal

    string s4 = "hello" + ","; // error: no string operand

    string s5 = s1 + ", " +"world"; // ok: each + has string operand

    string s6 = "hello" + "," + s2; // error: can't add string literals

     

    s3 和 s4 的初始化仅仅用了一个单独的操作。在这些样例中,非常easy推断 s3 的初始化是合法的:把一个 string 对象和一个字符串字面值连接起来。而 s4 的初始化试图将两个字符串字面值相加,因此是非法的。

    s5 的初始化方法显得有点不可思议,但这样的使用方法和标准输入输出的串联效果是一样的(1.2 节)。本例中,string 标准库定义加操作返回一个 string 对象。这样,在对 s5 进行初始化时。子表达式 s1 + ", " 将返回一个新 string 对象,后者再和字面值 "world "连接。整个初始化过程能够改写为:

     

    string tmp = s1 + ", "; // ok: +has a string operand

    s5 = tmp + "world"; // ok: + hasa string operand

     

    而 s6 的初始化是非法的。依次来看每一个子表达式。则第一个子表达式试图

    把两个字符串字面值连接起来。

    这是不同意的,因此这个语句是错误的。

     

    从string 对象获取字符

     

     

    string 类型通过下标操作符([])来訪问 string 对象中的单个字符。下

    标操作符须要取一个 size_type 类型的值,来标明要訪问字符的位置。这个下

    标中的值通常被称为“下标”或“索引”(index)

     

    string 对象的下标从 0 開始。假设 s 是一个 string 对象且 s 不空。则s[0] 就是字符串的第一个字符, s[1] 就表示第二个字符(假设有的话),而 s[s.size() - 1] 则表示 s 的最后一个字符。

     

     

    引用下标时假设超出下标作用范围就会引起溢出错误。可用下标操作符分别取出string 对象的每一个字符,分行输出:

     

    string str("some string");

    for (string::size_type ix = 0; ix != str.size(); ++ix)

    cout << str[ix] << endl;

     

    每次通过循环,就从 str 对象中读取下一个字符,输出该字符并换行。

     

     

    下标操作可用作左值

    前面说过,变量是左值,且赋值操作的左操作的必须是左值。

    和变量一样,string 对象的下标操作返回值也是左值。因此,下标操作能够放

    于赋值操作符的左边或右边。通过以下循环把 str 对象的每个字符置

    为 ‘*’:

     

    for (string::size_type ix = 0; ix !=str.size(); ++ix)

    str[ix] = '*';

     

     

    计算下标值

     

     

    不论什么可产生整型值的表达式可用作下标操作符的索引。比如。如果 someval 和 someotherval 是两个整形对象。能够这样写:

     

    str[someotherval * someval] = someval;

     

    尽管不论什么整型数值都可作为索引,但索引的实际数据类型却是类型 unsigned 类型

     

    string::size_type。

     

    前面讲过。应该用 string::size_type 类型的变量接受 size 函数的返回值。在定义用作索引的变量时。出于相同的道理。string 对象的索引变量最好也用 string::size_type 类型。

     

     

    在使用下标索引 string 对象时。必须保证索引值“在上下界范围内”。“在上下界范围内”就是指索引值是一个赋值为 size_type 类型的值。其取值范围在 0 到 string 对象长度减 1 之间。使用 string::size_type 类型或其它 unsigned 类型,就仅仅须要检測它是否小于 string 对象的长度。标准库不要求检查索引值,所用索引的下标越界是未定义的。这样往往会导致严重的执行时错误。

     

    1.4 string 对象中字符的处理

    我们常常要对 string 对象中的单个字符进行处理,比如。通常须要知道某个特殊字符是否为空白字符、字母或数字。表 3.3 列出了各种字符操作函数,适用于 string 对象的字符(或其它不论什么 char 值)。

    这些函数都在 cctype 头文件里定义。

     

     

    isalnum(c) 假设 c 是字母或数字。则为 True。

    isalpha(c) 假设 c 是字母,则为 true。

    iscntrl(c) 假设 c 是控制字符,则为 true

    isdigit(c) 假设 c 是数字。则为 true。

    isgraph(c) 假设 c 不是空格,但可打印。则为 true。

    islower(c) 假设 c 是小写字母,则为 true。

    isprint(c) 假设 c 是可打印的字符,则为 true。

    ispunct(c) 假设 c 是标点符号,则 true。

    isspace(c) 假设 c 是空白字符。则为 true。

    isupper(c) 假设 c 是大写字母。则 true。

    isxdigit(c) 假设是 c 十六进制数,则为 true。

    tolower(c) 假设 c 大写字母,返回其小写字母形式,否则直接返回 c。

    toupper(c) 假设 c 是小写字母,则返回其大写字母形式,否则直接返回 c。

     

     

    表中的大部分函数是測试一个给定的字符是否符合条件,并返回一个 int 作为真值。假设測试失败,则该函数返回 0 ,否则返回一个(无意义的)非 0 ,表示被測字符符合条件。

    表中的这些函数,可打印的字符是指那些能够表示的字符,空白字符则是空格、制表符、垂直制表符、回车符、换行符和进纸符中的随意一种;标点符号则是除了数字、字母或(可打印的)空白字符(如空格)以外的其它可打印字符。

    这里给出一个样例。运用这些函数输出一给定 string 对象中标点符号的个数:

     

    string s("Hello World!!!");

    string::size_type punct_cnt = 0;

    // count number of punctuation charactersin s

    for (string::size_type index = 0; index !=s.size(); ++index)

    if (ispunct(s[index]))

    ++punct_cnt;

    cout << punct_cnt

    << " punctuation characters in" << s << endl;

    这个程序的输出结果是:

     

    3 punctuation characters in Hello World!!!

     

    和返回真值的函数不同的是,tolower 和 toupper 函数返回的是字符,返

    回实參字符本身或返回该字符对应的大写和小写字符。我们能够用 tolower 函数

    把 string 对象 s 中的字母改为小写字母,程序例如以下:

     

    // convert s to lowercase

    for (string::size_type index = 0; index !=s.size(); ++index)

    s[index] = tolower(s[index]);

    cout << s << endl;

    得到的结果为:

    hello world!!!

     

     

     

    建议:採用 C 标准库头文件的 C++ 版本号

     

    C++ 标准库除了定义了一些选定于C++ 的设施外。还包含 C 标准库。C++ 中的头文件 cctype 事实上就是利用了 C 标准库函数,这些库函数就定义在 C 标准库的 ctype.h 头文件里。

    C 标准库头文件命名形式为 name 而C++ 版本号则命名为 cname ,少了后缀,.h 而在头文件名称前加了 c 表示这个头文件源自 C 标准库。

    因此。cctype 与 ctype.h 文件的内容是一样的,仅仅是採用了更适合 C++程序的形式。

    特别地。cname 头文件里定义的名字都定义在命名空间 std 内。而 .h 版本号中的名字却不是这样。

    通常,C++ 程序中应採用 cname这样的头文件的版本号。而不採用 name.h 版本号,这样,标准库中的名字在命名空间 std 中保持一致。

    使用 .h 版本号会给程序猿带来负担,由于他们必须记得哪些标准库名字是从 C 继承来的,而哪些是 C++ 所特有的。

  • 相关阅读:
    Lua ip转整数
    纯lua实现Base64加密与解密
    lua之base64的解码和编码(三种方案实现)
    Lua 5.1 位操作(与,或,异或操作)
    Lua打印Table对象
    Lua 截取字符串(截取utf-8格式字符串)
    lua 截取字符,以及取字符个数(非字符串长度)
    lua 加密解密
    Openwrt与贝壳物联平台通讯示例
    php socket编程:使用socket_recv而不是socket_read
  • 原文地址:https://www.cnblogs.com/cxchanpin/p/7080909.html
Copyright © 2011-2022 走看看