C/C++教程

一枝独秀的字符串:C++也能处理文本?

本文主要是介绍一枝独秀的字符串:C++也能处理文本?,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

string 的接口比较复杂,除了字符串操作,还有 size()、begin()、end()、push_back() 等类似容器的操作,这很容易让人产生“联想”,把它当成是一个“字符容器”。但我不建议你这样做。字符串和容器完全是两个不同的概念。
字符串是“文本”,里面的字符之间是强关系,顺序不能随便调换,否则就失去了意义,通常应该视为一个整体来处理。而容器是“集合”,里面的元素之间没有任何关系,可以随意增删改,对容器更多地是操作里面的单个元素。理解了这一点,把每个字符串都看作是一个不可变的实体,你才能在 C++ 里真正地用好字符串。但有的时候,我们也确实需要存储字符的容器,比如字节序列、数据缓冲区,这该怎么办呢?这个时候,我建议你最好改用vector,它的含义十分“纯粹”,只存储字符,没有 string 那些不必要的成本,用起来也就更灵活一些。接下来我们再看看 string 的一些小技巧。

字面量后缀

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++也能处理文本?的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!