首页 > 其他分享 >3.2 标准库类型string

3.2 标准库类型string

时间:2024-04-04 19:59:24浏览次数:21  
标签:std 字符 string 初始化 对象 字符串 3.2 类型

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

 

 

 

 

 

 

 

 

 

 

标签:std,字符,string,初始化,对象,字符串,3.2,类型
From: https://blog.csdn.net/tang7mj/article/details/137379911

相关文章

  • D. Non-Palindromic Substring
    D.Non-PalindromicSubstringAstring$t$issaidtobe$k$-goodifthereexistsatleastonesubstring$^\dagger$oflength$k$whichisnotapalindrome$^\ddagger$.Let$f(t)$denotethesumofallvaluesof$k$suchthatthestring$t$is$k$-good.Yo......
  • 【Java初阶(八)】String类
    ❣博主主页:33的博客❣▶文章专栏分类:Java从入门到精通◀......
  • golang 接口类型断言失败时,有哪些方式可以优雅地处理panic?
    在Go语言中,接口类型断言失败会导致运行时panic。为了避免程序因为类型断言失败而意外终止,可以采取以下几种优雅处理panic的方法:1.使用recover函数recover函数可以用来捕获panic,并从中恢复。这通常与defer语句一起使用。在进行类型断言的代码块之后,可以添加一......
  • golang反射与接口类型断言有什么区别?
    在Go语言中,反射(Reflection)和接口类型断言(InterfaceTypeAssertion)是两种不同的机制,它们各自有不同的用途和行为。以下是对它们的比较和区别的详细解释。反射(Reflection)反射是一种在运行时检查和操作程序内容的能力。在Go语言中,反射主要通过reflect包来实现。反射使......
  • 全局统一返数据类型封装记录
    全局统一返回值封装​在SpringBoot中,实现全局统一返回值封装是一种常见的做法,它有助于保持API的一致性,并简化前端对响应数据的处理。创建一个响应体类,包含状态码、消息、数据等字段。这个类可以作为所有控制器返回值的通用格式。​下面通过三种类型的统一返回响应体来......
  • 格式占位符与数据类型的使用
    格式占位符与数据类型总结:1、char类型可以用%c,也可以用%d【由于char(字符类型)本质就是一个字节的整数】格式占位符1、不同进制输出格式占位符%d:十进制整数。%x:十六进制整数。%#x:显示前缀0x的十六进制整数。——x的大小写%#X:显示前缀0X的十六进制整数。......
  • C语言02-常量、二进制、数据类型
    第4章常量1.常量特点程序运行时,其值不能改变的量,即为常量。习惯上常量名使用大写,方便与变量区分。2.常量分类​ **字面量常量**:直接使用的常量,不需要定义或声明,包括整数常量、浮点数常量、字符常量。注:有1,2,3等 ——顾名思义,就是数字整数 1.2,1.3,1.4等 ——也就......
  • 枚举类型
    枚举类型目录枚举类型1.定义2.枚举元素的值2.1默认2.2全部赋值2.3部分赋值3.枚举变量的定义方式3.1先定义枚举类型,再定义枚举变量3.2同时定义枚举类型和枚举变量3.3忽略枚举名,直接定义枚举变量3.4结合typedef关键字4.总结1.定义枚举是用来代表整数常量的符号默认......
  • Python变量类型
    Python 变量类型变量存储在内存中的值。这就意味着在创建变量时会在内存中开辟一个空间。基于变量的数据类型,解释器会分配指定内存,并决定什么数据可以被存储在内存中。因此,变量可以指定不同的数据类型,这些变量可以存储整数,小数或字符。变量赋值Python中的变量不需要声明,变量......
  • 元组、布尔、集合内置方法以及数据类型内置方法总结
    昨日内容回顾【一】列表类型内置方法(一)类型强制转换字符串可以转换成列表字符串中的每一个元素字典转换为列表以后是字典的键元组转换为列表集合转换为列表集合的去重性和无序性--->一旦转换成功这个列表就定死(二)按照索引取值正向:从0开始反向:从-1开始可......