3.2 标准库类型string

3.2 标准库类型string

标准库类型string代表了一个可变长度的字符序列,是C++中用于处理文本数据的主要方式。为了使用string类型,首先必须包含string头文件:

#include <string>
using std::string;

这种类型作为标准库的一部分,被定义在std命名空间中,这意味着你可以使用std::string来声明字符串变量,或者通过上述的using声明来简化代码。

在本节中,我们将探讨最常用的string操作。第9.5节还将介绍更多高级功能。

3.2.1 定义和初始化string对象

初始化string对象的方式多样,依赖于类定义时提供的构造函数。一个类可能提供多种构造函数来允许不同方式的初始化,这些方式可能基于初始值的数量或类型的不同。下面是一些初始化string对象最常见的方式:

  • 默认初始化:string s1; // s1是一个空字符串
  • 拷贝初始化:string s2 = s1; // s2是s1的副本
  • 通过字符串字面值直接初始化:string s3 = "hiya"; // s3是该字符串字面值的副本
  • 通过字符重复初始化:string s4(10, 'c'); // s4的内容是连续的十个'c'

直接初始化和拷贝初始化

C++提供了不同的初始化方式,通过使用string类,我们可以更清晰地理解直接初始化和拷贝初始化之间的区别:

  • 拷贝初始化:使用等号(=)进行初始化,如string s5 = "hiya"; 这里,编译器会将等号右侧的初始值拷贝到新创建的对象中。
  • 直接初始化:不使用等号,如string s6("hiya");string s7(10, 'c'); 这种方式在初始值有多个时通常是必需的。

在只有一个初始值的情况下,两种初始化方式都是可行的。如果初始化需要多个值(如s7的例子),通常只能使用直接初始化的方式。

对于多值初始化,虽然也可以通过显式创建一个临时对象进行拷贝初始化(如string s8 = string(10, 'c');),但这种方式的可读性较差,没有明显的优势。

通过上述示例和解释,我们可以看到string类型提供了灵活的初始化选项,以适应不同的使用场景。理解这些基础概念对于有效使用C++标准库中的string类型至关重要。

 

3.2.2 string对象上的操作

在C++标准库中,string类型不仅定义了如何初始化对象,还提供了一系列丰富的操作来处理字符串数据。这些操作包括从基本的输入输出到复杂的字符串处理功能。本节深入探讨string对象可执行的各种操作,提供了一个全面的指南。

读写string对象

C++标准库允许直接使用输入输出流(iostream)读写string对象,这与处理内置数据类型(如intdouble)的方式类似。这些基本操作提供了方便的途径来接收输入和显示输出。

输入操作

使用>>运算符可以从输入流中读取string,自动忽略开头的空白字符(空格、换行、制表符等),并从第一个非空白字符开始,直到下一个空白字符为止。

std::string s;
std::cin >> s; // 读取字符串,遇到空白停止
std::cout << s << std::endl; // 输出字符串

输出操作

与输入操作相对应,可以使用<<运算符将string对象写入到输出流。这样,就可以在终端显示字符串内容。

读取未知数量的字符串

通过结合使用循环和>>运算符,可以连续读取未知数量的string对象,直到遇到文件结束符(EOF)。

std::string word;
while (std::cin >> word) {
    std::cout << word << std::endl;
}

使用getline读取一整行

如果需要保留输入字符串中的空白符,应该使用std::getline()函数。这个函数会读取输入流直到遇到换行符为止,换行符被读取但不存储在结果字符串中。

std::string line;
while (std::getline(std::cin, line)) {
    std::cout << line << std::endl;
}

基本属性和操作

string类定义了多种方法来查询和操作字符串内容。

查询操作

  • empty():检查字符串是否为空。
  • size()length():返回字符串中的字符数量。

访问单个字符

  • 使用下标操作[n]访问字符串中的特定字符,下标从0开始。

字符串连接

  • 使用+运算符可以连接两个字符串。

字符串比较

  • string类重载了比较运算符(==!=<<=>>=),提供了基于字典顺序的字符串比较功能,这些比较对字母大小写敏感。

操作的广度与深度

C++的string类不仅仅提供了上述操作,还包括一系列复杂的成员函数,用于执行更高级的操作,如查找子串、替换、插入、删除等。通过这些操作,可以轻松实现复杂的字符串处理逻辑。

例如,find()函数可以在字符串中查找子串或字符的首次出现位置;replace()函数可以替换字符串中的某部分;substr()函数可以获取字符串的子串。

这些操作的存在显著增强了string类型的能力,使得处理文本数据变得异常灵活和强大。了解和掌握这些操作对于进行有效的字符串处理是非常关键的。

 

深入探索string的empty和size操作

在C++的string类型中,emptysize是两个基本而强大的成员函数,它们提供了检查字符串状态的简便方法。了解这些操作的内部工作原理不仅可以帮助我们更高效地处理字符串,还能让我们避免在程序中出现一些常见的错误。

使用empty判断字符串是否为空

empty()函数的用途直接而简单:它检查string对象是否为空,即是否不包含任何字符。如果字符串为空,empty()返回true;否则,返回false。这个功能尤其在处理用户输入或文件读取时非常有用,可以有效地帮助我们过滤掉空行或未初始化的字符串。

std::string line;
while (std::getline(std::cin, line)) {
    if (!line.empty()) {
        std::cout << line << std::endl; // 只输出非空行
    }
}

使用size获取字符串长度

empty()函数相辅相成的是size()函数,它返回字符串中字符的数量。这一点对于需要根据字符串长度执行不同操作的场景尤为重要,比如只想处理长度超过一定阈值的字符串。

std::string line;
while (std::getline(std::cin, line)) {
    if (line.size() > 80) {
        std::cout << line << std::endl; // 只输出长度超过80个字符的行
    }
}

理解string::size_type

size()函数返回的类型是string::size_type,这是一个无符号整型,足够大以存储任何string对象的长度。这种类型设计体现了C++标准库追求类型安全和独立于机器的原则。使用string::size_type而不是普通的intunsigned int进行字符串长度的存储和计算,可以避免因类型转换而引发的潜在问题。

在C++11及以后的版本中,可以使用auto关键字让编译器自动推断出size()函数返回值的类型,这样可以使代码更加简洁易读。

auto len = line.size(); // len的类型自动被推断为string::size_type

注意与整数类型的混用

一个需要特别注意的点是,由于size()返回的是无符号整数,直接将其结果与有符号整数(如int)进行比较可能会引发意料之外的行为。比如,如果比较的整数是负数,那么这个负数会被转换为一个很大的无符号数,这可能会导致比较结果与预期不符。

为了避免这种类型不匹配导致的问题,推荐始终使用string::size_type来处理与字符串长度相关的数值。这不仅能保持类型一致,还可以提高代码的可读性和安全性。

if (line.size() < static_cast<string::size_type>(n)) {
    // 正确处理比较,其中n是一个整数
}

通过深入了解emptysize操作,我们能够更加精准地控制和处理字符串数据。这些基础操作虽然简单,但在日常编程中的应用却非常广泛和重要。

 

比较和操作string对象

在C++标准库中,string类提供了一系列的运算符来比较两个字符串对象,以及进行字符串的赋值和连接操作。这些功能的设计旨在使得string类的使用尽可能接近于内置类型的直观性和灵活性。

比较string对象

string类定义的比较运算符允许我们按照字典顺序(并且对大小写敏感)来比较两个字符串对象。这包括了相等性运算符(==!=)以及关系运算符(<<=>>=)。

  • 相等性运算符 (==!=):检查两个string对象是否长度相同且所含字符完全相同。
  • 关系运算符 (<<=>>=):基于字符在字典中的顺序来比较,对字符的大小写敏感。

比较规则如下:

  1. 如果两个string对象的长度不同,且较短的string对象的每个字符都与较长string对象对应位置上的字符相同,则认为较短的string对象小于较长的string对象。
  2. 如果两个string对象在某些对应位置上的字符不同,则比较结果实际上是第一对不同字符比较的结果。

赋值操作

string类支持赋值操作,允许将一个string对象的值赋给另一个string对象。

std::string st1(10, 'c'), st2; // st1内容是"cccccccccc"; st2是空字符串
st1 = st2; // 赋值后,st1和st2都变成了空字符串

字符串相加

string对象之间可以使用+运算符进行连接,得到的结果是一个新的string对象,它包含了两个操作数串联起来的内容。

std::string s1 = "hello, ", s2 = "world\n";
std::string s3 = s1 + s2; // s3的内容是"hello, world\n"

此外,+=运算符可以将右侧的string对象追加到左侧的string对象的后面。

字面值和string对象相加

虽然string对象可以与字符字面值或字符串字面值直接相加,但当进行这种操作时,至少有一个操作数必须是string对象,以确保表达式的类型安全。

std::string s1 = "hello", s2 = "world";
std::string s3 = s1 + ", " + s2 + '\n'; // 正确,每个+操作符的两边至少有一个是string

错误示例:

// 错误:尝试直接将两个非string类型的字面值相加
std::string s4 = "hello" + ","; // 错误
std::string s5 = "hello" + "," + "world"; // 错误

要正确连接字面值和string对象,应保证加法运算中至少有一个操作数是string类型。这是因为字符串字面值本身是C风格的字符串,它们并不直接支持+运算符进行连接操作。

注意事项

由于历史原因和为了与C语言兼容,C++中的字符串字面值不是标准库类型string的对象。因此,字符串字面值与string对象是两种不同的类型,这一点在进行字符串操作时必须特别注意。

 

3.2.3 处理string对象中的字符

在使用C++进行编程时,经常需要单独处理string对象中的字符。这可能包括检查字符串中是否包含空白字符、转换字母的大小写、或者判断某个特定的字符是否出现在字符串中。为了实现这些操作,我们需要深入理解如何获取和处理string对象中的单个字符。

获取字符本身

处理string中的字符通常涉及到遍历字符串,并对每个字符执行特定的操作。C++提供了几种方式来实现这一点,包括使用范围for循环、普通的for循环以及其他迭代器或指针技术。

std::string str = "Hello, World!";
// 使用范围for循环访问每个字符
for (char c : str) {
    // 处理字符c
}

字符特性的改变

要改变字符的特性(如大小写),C++标准库提供了一组函数,这些函数定义在cctype头文件中(或在C语言标准中的ctype.h),它们可以对字符执行各种检查或转换操作。下面是一些常用的函数及其功能:

  • isalpha(c): 检查c是否是字母。
  • isdigit(c): 检查c是否是数字。
  • isspace(c): 检查c是否是空白字符(如空格、制表符等)。
  • isupper(c): 检查c是否是大写字母。
  • islower(c): 检查c是否是小写字母。
  • toupper(c): 如果c是小写字母,则转换为对应的大写字母。
  • tolower(c): 如果c是大写字母,则转换为对应的小写字母。

使用这些函数时,通常需要包含cctype(或ctype.h)头文件。为了更好地与C++的其他部分兼容,建议使用cctype而不是ctype.h,并且使用std命名空间。

#include <cctype>
#include <iostream>
#include <string>

int main() {
    std::string s = "Hello, World!";
    for (char &c : s) {
        if (std::islower(c)) {
            c = std::toupper(c);
        } else if (std::isupper(c)) {
            c = std::tolower(c);
        }
    }
    std::cout << s << std::endl; // 输出转换后的字符串
    return 0;
}

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

C++为了兼容C语言的标准库,提供了C库的版本,其中的头文件以c开头,去除了.h后缀,并将定义的名字纳入std命名空间中。因此,推荐使用如<cctype>这样的头文件而不是<ctype.h>,这样做不仅使得代码看起来更加C++化,而且有助于保持类型安全和命名空间的清晰。

通过使用这些技术和标准库函数,可以有效地处理string对象中的字符,无论是执行简单的字符转换还是进行更复杂的文本分析处理。

 

处理string对象中的字符:使用基于范围的for语句

在C++中,对string对象中的每个字符执行操作是一项常见的任务。C++11引入的范围for(range-based for)语句提供了一个简单而强大的方法来遍历string(或其他容器)中的每个元素,并对它们执行某种操作。

范围for语句的基本用法

范围for语句的语法如下:

for (declaration : expression)
    statement
  • expression是要遍历的序列,例如string对象。
  • declaration定义了一个变量,该变量将被用于访问序列中的基本元素。

每次迭代,声明部分的变量会被初始化为expression部分的下一个元素值。

示例:遍历字符串中的每个字符

std::string str("some string");
// 每行输出str中的一个字符
for (auto c : str)
    std::cout << c << std::endl;

这个示例中,变量c在每次循环中都被初始化为字符串str中的下一个字符。

示例:统计字符串中标点符号的个数

#include <iostream>
#include <string>
#include <cctype>

std::string s("Hello World!!!");
decltype(s.size()) punct_cnt = 0;
for (auto c : s) {
    if (std::ispunct(c)) {
        ++punct_cnt;
    }
}
std::cout << punct_cnt << " punctuation characters in " << s << std::endl;

在这个例子中,使用了decltype来确保计数器punct_cnt的类型与string::size_type相匹配。

使用范围for语句修改字符串中的字符

如果需要修改string对象中的字符,循环变量必须被声明为引用类型:

std::string s("Hello World!!!");
// 转换成大写形式
for (auto &c : s)
    c = std::toupper(c);
std::cout << s << std::endl;

这里,c是对string对象s中每个字符的引用。因此,对c的任何修改都会反映在string对象s上。

注意

  • 当使用范围for语句处理字符串时,如果要修改字符串中的字符,确保使用引用类型作为循环变量。
  • 在访问字符串的特定字符,如第一个字符或第一个单词的第一个字符时,可以使用下标操作符[]。然而,务必确保在使用下标前,字符串不为空,以避免未定义行为。
  • 转换字符串中的部分字符(例如,仅首字母或首个单词)大写时,除了使用下标外,还可以使用迭代器进行更复杂的操作,这将在后续章节中介绍。

 

 

 

 

 

 

 

 

 

 

  • 26
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夏驰和徐策

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值