一枝独秀的字符串:C++也能处理文本?
2022/4/17 9:13:47
本文主要是介绍一枝独秀的字符串:C++也能处理文本?,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
string 的接口比较复杂,除了字符串操作,还有 size()、begin()、end()、push_back() 等类似容器的操作,这很容易让人产生“联想”,把它当成是一个“字符容器”。但我不建议你这样做。字符串和容器完全是两个不同的概念。
字符串是“文本”,里面的字符之间是强关系,顺序不能随便调换,否则就失去了意义,通常应该视为一个整体来处理。而容器是“集合”,里面的元素之间没有任何关系,可以随意增删改,对容器更多地是操作里面的单个元素。理解了这一点,把每个字符串都看作是一个不可变的实体,你才能在 C++ 里真正地用好字符串。但有的时候,我们也确实需要存储字符的容器,比如字节序列、数据缓冲区,这该怎么办呢?这个时候,我建议你最好改用vector
字面量后缀
C++11 为方便使用字符串,新增了一个字面量的后缀“s”,明确地表示它是 string 字符串类型,而不是 C 字符串,这就可以利用 auto 来自动类型推导,而且在其他用到字符串的地方,也可以省去声明临时字符串变量的麻烦,效率也会更高:
using namespace std::literals::string_literals; //必须打开名字空间 auto str = "std string"s; // 后缀s,表示是标准字符串,直接类型推导
为了避免与用户自定义字面量的冲突,后缀“s”不能直接使用,必须用 using 打开名字空间才行,这是它的一个小缺点。
原始字符串
C++11 还为字面量增加了一个“原始字符串”(Raw string literal)的新表示形式,比原
来的引号多了一个大写字母 R 和一对圆括号,就像下面这样:
auto str = R"(nier:automata)"; // 原始字符串:nier:automata
这种形式初看上去显得有点多余,它有什么好处呢?你一定知道,C++ 的字符有“转义”的用法,在字符前面加上一个“\”,就可以写出“\n”“\t”来表示回车、跳格等不可打印字符。但这个特性也会带来麻烦,有时我们不想转义,只想要字符串的“原始”形式,在 C++ 里写起来就很难受了。特别是在用正则表达式的时候,由于它也有转义,两个转义效果“相乘”,就很容易出错。比如说,我要在正则里表示“$”,需要写成"\$",而在 C++ 里需要对“\”再次转义,就是“\\\$”,你能数出来里面到底有多少个“\”吗?如果使用原始字符串的话,就没有这样的烦恼了,它不会对字符串里的内容做任何转义,完全保持了“原始风貌”,即使里面有再多的特殊字符都不怕:
auto str1 = R"(char""'')"; // 原样输出:char""'' auto str2 = R"(\r\n\t\")"; // 原样输出:\r\n\t\" auto str3 = R"(\\\$)"; // 原样输出:\\\$ auto str4 = "\\\\\\$"; // 转义后输出:\\\$
不过,想要在原始字符串里面写引号 + 圆括号的形式该怎么办呢?对于这个问题,C++ 也准备了应对的办法,就是在圆括号的两边加上最多 16 个字符的特别“界定符”(delimiter),这样就能够保证不与字符串内容发生冲突:
auto str5 = R"==(R"(xxx)")==";// 原样输出:R"(xxx)"
字符串转换函数
在处理字符串的时候,我们还会经常遇到与数字互相转换的事情,以前只能用 C 函数atoi()、atol(),它们的参数是 C 字符串而不是 string,用起来就比较麻烦,于是,C++11就增加了几个新的转换函数:
stoi()、stol()、stoll() 等把字符串转换成整数;
stof()、stod() 等把字符串转换成浮点数;
to_string() 把整数、浮点数转换成字符串。
这几个小函数在处理用户数据、输入输出的时候,非常方便:
assert(stoi("42") == 42); // 字符串转整数 assert(stol("253") == 253L); // 字符串转长整数 assert(stod("2.0") == 2.0); // 字符串转浮点数 assert(to_string(1984) == "1984"); // 整数转字符串
字符串视图类
再来说一下 string 的成本问题。它确实有点“重”,大字符串的拷贝、修改代价很高,所以我们通常都尽量用 const string&,但有的时候还是无法避免(比如使用 C 字符串、获取子串)。如果你对此很在意,就有必要找一个“轻量级”的替代品。
在 C++17 里,就有这么一个完美满足所有需求的东西,叫 string_view。顾名思义,它是一个字符串的视图,成本很低,内部只保存一个指针和长度,无论是拷贝,还是修改,都非常廉价。唯一的遗憾是,它只出现在 C++17 里,不过你也可以参考它的接口,自己在 C++11 里实现一个简化版本。下面我给你一个简单的示范,你可以自己扩展:
class my_string_view final // 简单的字符串视图类,示范实现 { public: using this_type = my_string_view; // 各种内部类型定义 using string_type = std::string; using string_ref_type = const std::string&; using char_ptr_type = const char*; using size_type = size_t; private: char_ptr_type ptr = nullptr; // 字符串指针 size_type len = 0; // 字符串长度 public: my_string_view() = default; ~my_string_view() = default; my_string_view(string_ref_type str) noexcept : ptr(str.data()), len(str.length()) {} public: char_ptr_type data() const // 常函数,返回字符串指针 { return ptr; } size_type size() const // 常函数,返回字符串长度 { return len; } };
正则表达式
说了大半天,其实我们还是没有回答这节课开头提出的疑问,也就是“在 C++ 里该怎么处理文本”。string 只是解决了文本的表示和存储问题,要对它做大小写转换、判断前缀后缀、模式匹配查找等更复杂的处理,要如何做呢?
C++11 在标准库里加入了正则表达式库 regex,利用它的强大能力,你就能够任意操作文本、字符串。
C++ 正则表达式主要有两个类。
regex:表示一个正则表达式,是 basic_regex 的特化形式;
smatch:表示正则表达式的匹配结果,是 match_results 的特化形式。
C++ 正则匹配有三个算法,注意它们都是“只读”的,不会变动原字符串。
regex_match():完全匹配一个字符串;
regex_search():在字符串里查找一个正则匹配;
regex_replace():正则查找再做替换。
所以,你只要用 regex 定义好一个表达式,然后再调用匹配算法,就可以立刻得到结果,用起来和其他语言差不多。不过,在写正则的时候,记得最好要用“原始字符串”,不然转义符绝对会把你折腾得够呛。
下面我举个例子:
这篇关于一枝独秀的字符串:C++也能处理文本?的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23增量更新怎么做?-icode9专业技术文章分享
- 2024-11-23压缩包加密方案有哪些?-icode9专业技术文章分享
- 2024-11-23用shell怎么写一个开机时自动同步远程仓库的代码?-icode9专业技术文章分享
- 2024-11-23webman可以同步自己的仓库吗?-icode9专业技术文章分享
- 2024-11-23在 Webman 中怎么判断是否有某命令进程正在运行?-icode9专业技术文章分享
- 2024-11-23如何重置new Swiper?-icode9专业技术文章分享
- 2024-11-23oss直传有什么好处?-icode9专业技术文章分享
- 2024-11-23如何将oss直传封装成一个组件在其他页面调用时都可以使用?-icode9专业技术文章分享
- 2024-11-23怎么使用laravel 11在代码里获取路由列表?-icode9专业技术文章分享
- 2024-11-22怎么实现ansible playbook 备份代码中命名包含时间戳功能?-icode9专业技术文章分享