使用string和string_view(二)——数值转换、std::string_view和非标准字符串

发布时间:2026/11/13 10:34:45

使用string和string_view(二)——数值转换、std::string_view和非标准字符串 目录1. 数值转换1.1 高级数值转换函数1.1.1 数值转换为字符串1.1.2 字符串转换为数值1.2 低级数值转换函数1.2.1 数值转换为字符串1.2.2 字符串转换为数值2. std::string_view2.1 引入2.2 string_view转换成string2.3 std::string_view和临时字符串2.4 std::string_view字面量3. 非标准字符串参考1. 数值转换C标准库同时提供了高级数值转换函数和低级数值转换函数。1.1 高级数值转换函数1.1.1 数值转换为字符串下面的函数可用于将数值转换为字符串T可以是(unsigned) int、(unsigned) long、unsigned) long long、float、double以及long double。所有这些函数都负责内存分配它们会创建一个新的string对象并返回。std::string to_string(T val);这些函数的使用非常简单直观。例如下面的代码将long double值转换为字符串。longdoubled{3.14L};std::string s{std::to_string(d)};1.1.2 字符串转换为数值通过下面这组同样在std名称空间中定义的函数可以将字符串转换为数值。在这些函数原型中str表示要转换的字符串idx是一个指针这个指针接收第一个未转换的字符的索引base表示转换过程中使用的进制。idx指针可以是空指针如果是空指针则被忽略。如果不能执行任何转换这些函数会抛出invalid_argument异常。如果转换的值超出返回类型的范围则抛出out_of_range异常。int stoi(const string str, size_t* idx0, int base10);long stol(const string str, size_t* idx0, int base10);unsigned long stoul(const string str, size_t* idx0, int base10);long long stoll(const string str, size_t* idx0, int base10);unsigned long long stoull(const string str, size_t* idx0, int base10);float stof(const string str, size_t* idx0);double stod(const string str, size_t* idx0);long double stold(const string str, size_t* idx0);示例如下conststd::string toParse{ 123USD};size_t index{0};intvalue{stoi(toParse,index)};std::coutstd::format(Parse value: {}\n,value);std::coutstd::format(First non-parsed character: {}\n,toParse[index]);输出如下Parsed value:123First non-parsed character:Ustoi()、stol()、stoul()、stoll()和stoull()接收整数值并且有一个名为base的参数表明了给定的数值应该用什么进制来表示。base的默认值为10采用数字为0-9的十进制base为16表示采用16进制。如果base被设为0函数会按照以下规则自动计算给定数字的进制。1. 如果数字以0x或0X开头则被解析为十六进制数字。2. 如果数字以0开头则被解析为八进制数字。3. 其他情况下被解析为十进制数字。1.2 低级数值转换函数C也提供了许多低级数值转换函数这些都在charconv头文件中定义。这些函数不执行内存分配也不直接使用std::string而使用由调用者分配的缓存区。此外它们还针对性能进行了优化并且与语言环境无关。最终的结果是这些函数可以比其他高级数值转换函数快几个数量级。这些函数也是为所谓的完美往返而设计的这意味着将数值序列化为字符串表示然后将结果字符串反序列为数值结果与原始值完全相同。如果希望实现高性能、完美往返、独立于语言环境的转换则应当使用这些函数。例如在数值数据与人类可读格式(如JSON、XML等)之间进行序列化/反序列化。1.2.1 数值转换为字符串要将整数转换为字符可使用下面一组函数。to_chars_result to_chars(char* first, char* last, IntegerT value, int base10);这里IntegerT可以是任何有符号或者无符号的整数类型或char类型。结果是to_chars_result类型类型定义如下。structto_chars_result{char*ptr;std::errc ec;};如果转换成功ptr成员将等于所写入字符尾后一位置的指针。如果转换失败(即ecstd::errc::value_too_large)则它等于last。示例如下(out.data()返回的是指向out中第一个字符的指针)constsize_t BufferSize{50};std::stringout(BufferSize, );// a string of BufferSize space character.autoresult{std::to_chars(out.data(),out.data()out.size(),12345)};if(result.ecstd::errc{}){/* conversion successful. */std::coutout\n;}使用结构化绑定可以将其写成constsize_t BufferSize{50};std::stringout(BufferSize, );auto[ptr,error]{std::to_chars(out.data(),out.data()out.size(),12345)};if(result.ecstd::errc{}){/* conversion successful. */std::coutout\n;}类似地下面的一组转换函数可用于浮点类型。to_chars_resultto_chars(char*first,char*last,FloatT value);to_chars_resultto_chars(char*first,char*last,FloatT value,chars_format format);to_chars_resultto_chars(char*first,char*last,FloatT value,chars_format format,intprecision);这里FloatT可以是float、double或long double。可使用chars_format标志的组合指定格式enumclasschars_format{scientific,// Style: (-)d.ddde加减ddfixed,// Style: (-)ddd.dddhex,// Style: (-)h.hhhp加减dgeneralfixed|scientific//看下面};默认格式是chars_format::general这将导致to_chars()将浮点数转换为(-)ddd.ddd的十进制表示形式或(-)d.ddde加减dd形式的十进制指数表示形式得到最短的表示形式小数点前至少有一位数字(如果存在)。如果指定了格式但未指定精度将为给定格式自动确定最简短的表示形式最大精度为6个数字。例如constsize_t BufferSize{50};doublevalue{0.314};std::stringout(BufferSize, );auto[ptr,error]{std::to_chars(out.data(),out.data()out.size(),value)};if(errorstd::errc{}){/* conversion successful */std::coutout\n;}1.2.2 字符串转换为数值对于相反的转换即将字符串转换为数值可使用下面的一组函数。from_chars_resultfrom_chars(constchar*first,constchar*last,IntegerTvalue,intbase10);from_chars_resultfrom_chars(constchar*first,constchar*last,FloatTvalue,chars_format formatchars_format::general);from_chars_result的类型定义如下structfrom_chars_result{constchar*ptr;std::errc ec;};结果类型的ptr成员指向第一个未转换字符的指针如果所有字符转换成功则它等于last。如果所有字符都未转换则ptr等于first错误代码的值将为std::errc::invalid_argument。如果解析后的值过大无法由给定类型表示则错误代码的值将是std::errc::result_out_of_range。注意from_chars不会忽略任何前导空白。to_chars()和from_chars()的完美往返特性可以表示如下constsize_t BufferSize{50};doublevalue1{0.314};std::stringout(BufferSize, );auto[ptr1,error1]{std::to_chars(out.data(),out.data()out.size(),value1)};if(error1std::errc{}){std::coutout\n;}doublevalue2{};auto[ptr2,error2]{std::from_chars(out.data(),out.data()out.size(),value2)};if(error2std::errc{}){if(value1value2){std::coutPerfect roundtrip\n;}else{std::coutNo Perfect roundtrip?!?\n;}}2. std::string_view2.1 引入在C17之前为接收只读字符串的函数选择形参一直是一件进退两难的事情它应当是const char*吗那这样的话如果客户使用std::string则必须调用其上的c_str()或者data()来获取const char*。更糟糕的是函数将失去std::string良好的面向对象的方面以及其良好的辅助方法。或许形参应改用const std::string这种情况下始终需要std::string。如果传递一个字符串字面量编码器将默认创建一个临时字符串对象(其中包含字符串字面量的副本)并将该对象传递给函数因此会增加一点开销。有时人们会编写同一函数的多个重载版本一个接收const char*另一个接收const std::string但显然这并不是一个优雅的解决方案。在C17中通过引入std::string_view类解决了所有这些问题std::string_view类是std::basic_string_view类模板的实例化在string_view中定义。string_view基本上就是const std::string的简单替代品但不会产生开销。它从不复制字符串string_view支持与std::string类似的接口。一个例外是缺少c_str()但是data()是可用的。另外string_view添加了remove_prefix(size_t)和remove_suffix(size_t)方法前者将起始指针前移给定的偏移量来收缩字符串后者则将结尾指针倒退给定的偏移量来收缩字符串。如果知道如何使用std::string那么使用std::string_view将变得十分简单如下面的代码片段所示。extractExtension()函数提取给定文件名的扩展名(包括点号)并返回。注意通常按值传递string_view因为它的复制成本极低只包含指向字符串的指针以及字符串的长度。std::string_viewextractExtension(std::string_view filename){returnfilename.substr(filename.rfind(.));// rfind locates the last character . in the string.}该函数可用于所有类型的不同字符串usingnamespacestd::literals;std::string filename{R(c:\temp\my file.ext)};std::coutstd::format(C String: {}\n,extractExtension(filename));constchar*cString{R(c:\temp\my file.ext)};std::coutstd::format(C String: {}\n,extractExtension(cString));std::coutstd::format(Literal: {}\n,extractExtension(R(c:\temp\my file.ext)));在对extractExtension()的所有调用中没有进行一次复制。extractExtension()函数的filename()参数只是指针和长度该函数的返回类型也是如此这十分高效。还有一个string_view构造函数它接收任意原始缓冲区和长度。这可用于从字符缓冲区(并非以NUL终止)构建string_view。如果确实有一个以NUL终止的字符串缓冲区但你已经知道了字符串的长度构造函数不必再次统计字符数目这也是有用的。constchar*raw{/* ... */};size_t length{/* ... */};std::coutstd::format(Raw: {}\n,extractExtension({raw,length}));最后一行代码也可以i写成这样std::cout std::format(Raw: {}\n, extractExtension(std::string_view { raw, length } ));注意在每当函数需要将只读字符串作为一个参数时可使用std::string_view替代const std::string或const char*。2.2 string_view转换成string无法从string_view隐式构建一个string。要么使用一个显示的string构造函数要么使用string_view::data()成员。例如假设有以下接收const std::string的函数。voidhandleExtension(conststd::stringextension){/* ... */}不能采用如下方式调用该函数handleExtension(extractExtension(my file.ext));下面是两个可供使用的选项handleExtension(extractExtension(my file.ext).data());// data() methodhandleExtension(std::string{extractExtension(my file.ext)});// explicit constructor由于同样的原因无法连接一个string和string_view。下面的代码将无法编译std::string str{Hello};std::string_view sv{world};autoresult{strsv};你可以对string_view使用data()方法如下所示。auto result1 { str sv.data() };或者你可以使用append()std::string result2{str};result2.append(sv.data(),sv.size());警告返回字符串的函数应返回const std::string或std::string但不应该返回string_view。返回string_view会带来使返回的string_view无效的风险例如当它指向的字符串需要重新分配时。警告将const string或string_view存储为类的数据成员需要确保它们指向的字符串在对象的生命周期内保持有效状态。存储std::string更安全。2.3 std::string_view和临时字符串永远不要使用string_view保存临时字符串的视图考虑以下示例std::string s{Hello};std::string_view sv{s World!};std::coutsv\n;此代码段具有未定义的行为即运行代码时发生的情况取决于编译器和编译器设置。它可能会打印ello World!“等等。为什么是未定义的行为字符串视图sv的初始化表达式将生成一个临时字符串其中包含”Hello World!”。然后string_view存储指向此临时字符串的指针。在第二行代码的末尾这个临时字符串被销毁留下一个悬空指针的string_view。2.4 std::string_view字面量可使用标准用户定义的字面量sv将字符串字面量解释为std::string_view。例如auto sv { My string_viewsv };标准用户定义的字面量sv需要以下几条using命令之一usingnamespacestd::literals::string_view_literals;usingnamespacestd::string_view_literals;usingnamespacestd::literals;usingnamespacestd;3. 非标准字符串许多C程序员都不使用C风格的字符串这有几个原因。一些程序员只是不知道有string类因为它并不总是C规范的一部分。其他程序员发现CString没有提供他们需要的行为或他们不喜欢std::string对字符编码不感知这一事实所以开发了自己的字符串类型。也许最常见的原因是开发框架和操作系统有自己的表达字符串的方式例如Microsoft MFC中的CString类。它常用于向后兼容或解决遗留问题。在C中启动新项目时提前确定团队如何表达字符串是重要的。务必注意以下几点1. 不应当使用C风格的字符串表示。2. 可对自己所使用框架中可用的字符串功能进行标准化如MFC、QT内置的字符串功能。3. 如果为字符串使用std::string应当使用std::string_view将只读字符串作为参数传递给函数否则看一下你的框架是否支持类似于string_view的类。参考[比] 马克·格雷戈勒著 程序喵大人 惠惠 墨梵 译 C20高级编程(第五版)
延伸阅读

更多相关文章

2026/11/13 10:34:45

Fable 5.1深度解析:Playwright+TypeScript+Node一体化自动化升级指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/9 22:03:25

SSM框架电商平台开发实战与毕业设计指南

1. 项目概述:SSM框架下的电商平台设计与实现这个基于SSM(SpringSpringMVCMyBatis)框架的电子商务平台毕业设计项目,是Java Web开发领域的典型实战案例。作为一个完整的电商系统,它涵盖了用户管理、商品展示、购物车、订…

2026/11/13 10:33:59

云南电工证查询平台复审延期怎么办理?郑州报考避坑指南

云南电工证查询平台复审延期怎么办理?郑州报考避坑指南 怕考不过白交培训费?现在考电工证,很多人担心交了钱、花时间,结果考试挂了,钱白花了,时间也浪费了。特别是在南阳卧龙区的建筑工人,电工证是很多岗位的“入场券”,但不少朋友在报考过程中踩了坑…

2026/11/13 10:33:59

电工证现场考试题考前押题怎么找?证书过期了不知道怎么复审延期

电工证现场考试题考前押题怎么找?证书过期了不知道怎么复审延期 证书过期了不知道怎么复审延期,这个问题在很多电工同行身上都出现过,特别是那些工作几年后才意识到证书有效期的,往往在遇到工程项目时才发现证件已经失效,不仅影响工作,还可能面临处罚。…

2026/11/13 10:33:59

广州电工证怎么查在哪考别踩坑 一次过才不浪费培训费

广州电工证怎么查在哪考别踩坑 一次过才不浪费培训费 怕考不过白交培训费?广州电工证怎么查在哪考成了很多学员心头大石,尤其是像信阳浉河区这样的建筑工人群体,考个低压电工证或高压电工证不光是合规上岗的通行证,更是职业发展的跳板。别看现在电工证报…

2026/11/13 10:23:19

电工证到期可以调岗吗?怎么报名不踩坑

电工证到期可以调岗吗?怎么报名不踩坑 怕考不过白交培训费,很多电工朋友在证书快到期时才开始慌。尤其是刚拿到证的新人,遇到证书到期就面临能不能继续上岗的问题, 电工证到期可以调岗吗…

2026/11/12 20:35:13

电工证丢失还能补吗?千万别踩坑,丢了证等于失业

电工证丢失还能补吗?千万别踩坑,丢了证等于失业 你正在找工作,但手里的电工证突然丢了,这下怎么办?别急,先别慌, 电工证丢失还能补吗 ?答案是肯定的,但 千万别踩坑 ,补办流程如果不了解,可能会耽误你上岗、甚至影响到你的收入。…

2026/11/13 0:01:06

办上海高压电工证良心建议:别被中介坑了,自己看这篇就够了

办上海高压电工证良心建议:别被中介坑了,自己看这篇就够了 你是不是也像我一样,第一次听说“高压电工证”这个名词,就感觉一头雾水?其实很多人都跟我一样,不知道去哪报名、怕被中介坑,结果花了冤枉钱还考不上。今天我就从一个在商丘培训过的学员角度,…

2026/11/13 0:01:06

黎里考电工证在哪里值不值得考

黎里考电工证在哪里值不值得考 之前考的证在外省能不能转过来?这是很多在外地工作、准备回老家发展的朋友最关心的问题。特别是在黎里地区,很多从事电力相关工作的朋友,都面临这样一个难题: 外省的电工证能否在本地使用?值不值得考?…

2026/11/13 0:01:06

毕节哪里考电工证 怎么报名学历不高也能上手

毕节哪里考电工证 怎么报名学历不高也能上手 你是不是也和我一样,学历不高,想着考个电工证,但又担心自己条件不够,怕报不上名?其实不然,只要符合条件,学历不高也能考电工证。今天就来聊聊【毕节哪里考电工证】这个问题,还有怎么报名,让你一步到位不…

2026/11/12 22:10:13

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/12 9:51:42

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑