Cpp之旅(学习笔记)第10章 字符串和正则表达式
10.1 引言
- Cpp标准库提供了 string 类型,使用程序员不必在使用C风格的文本处理方式——通过指针来处理字符数组。
- Cpp标准库还提供了 string_view 类型,允许程序以容器方式访问字符序列( std::string 或者 char[] )。
- 还提供了正则表达式匹配功能以查找文本中的模式。
10.2 字符串
string 是用于管理不同字符类型字符序列的 regular 类型。string 类型提供了很多有用的字符串处理操作:
例如:连接操作
string compose(const string& name, const string& domain) {return name + '@' + domain;
}
auto addr = compose("dmr", "bell-labs.com");
// 结果:dmr@bell-labs.com
函数 compose 中的字符串“加法”表示连接操作。
标准库 string 定义了一个移动构造函数,因此,即使是以传值方式而不是传引用方式返回一个很长的 string 也会很高效。
连接操作最常见的用法是在一个 string 的末尾追加一些内容。这可以直接通过 += 操作来实现。
void m2(string& s1, string& s2) {s1 = s1 + '\n';s2 += '\n';
}
除此之外,string 还支持下标操作(使用[])和提取子串操作。
string name = "Niels Stroustrup";
void m3() {string s = name.substr(6,10); // s = "Stroustrup"name.replace(0,5,"nicholas"); // name 变成 "nicholas Stroustrup"name[0] = toupper(name[0]); // name 变成 "Nicholas Stroustrup"
}
substr()
操作返回 string ,保存其参数指定的子字符串的拷贝。
substr()参数:
- 第一个参数是指向 string 中某个位置的下标。
- 第二个参数是指出所需子串的长度。
replace()
操作替换子串内容。在本例中,要替换的是从0开始、长度为5的子串,即Niels,它被替换为nicholas。最后将首字母变为大写。
注意:替换的内容和被替换的子串不必一样长。
如果你需要一个C风格的字符串(一个以0结尾的 char 数组),string 提供了对其包含的 C 风格字符串进行只读访问的接口
void print(const string& s) {// s.c_str()返回一个指向s所拥有的字符的指针printf("For people who like printf: %s\n",s.c_str());cout << "For people who like streams: " << s << '\n';
}
根据定义,字符串字面量的类型是 const char *。
要想获得 std::string 类型的字面量可以加上 s 后缀。
要想使用s后缀,需要使用命名空间 `std::literals::string_literals。
例如:
auto cat = "Cat"s; // std::string类型
auto dog = "Dog"; // 一个C风格字符串:const char* 类型
10.3 字符串视图
字符串视图(string_view)本质上就是一个(指针,长度)对,表明了一个字符串序列。
string_view 类型可以作为一个范围定义,因此可以用它来遍历字符。
void print_lower(string_view sv1)
{for(char ch : sv1)cout << tolower(ch);
}
string_view 类型的显著限制就在于它是只读的。
例如,如果函数需要将参数内容修改为小写。就不能使用 string_view 来传递字符串。这种情况下,需要考虑使用 span
string_view bad(){string s = "Once upon a time";return {&s[5],4}; // 糟糕,返回了局部数据的指针
}
10.4 正则表达式
标准库定义了 std::regex 类及其支持的函数,提供对正则表达式的支持。
regex pat {R"(\w{2}\s*\d{5}(-\d{4})?)"}; // 美国邮政编码模式:XXddddd-dddd// 以及其他变种
它指定了一个以两个字母开始的模式 \w{2},后面是任意个空白符 \s*,在接下来是五个数字 \d{5},然后是可选的一个破折号和四个数字 -\d{4}。
书中使用了原始字符串字面量,它以 R”( 开始,以 )“ 结束。
原始字符串字面量的好处是可以直接包含反斜线和引号而无需转义,因此非常适合表示正则表达式。
正则表达式常常包含大量反斜线,如果使用常规字符串,模式定义如下:
regex pat {"\\w{2}\\s*\\d{5}(-\\d{4})?"};
在<regex>
中,标准库为正则表达式提供了如下支持:
- regex_match():将正则表达式与一个(已知长度的)字符串进行匹配。
- regex_search():在一个(任意长的)数据流中搜索与正则表达式匹配的字符串。
- regex_replace():在一个(任意长的)数据流中搜索与正则表达式匹配的字符串并将其替换。
- regex_iterator:遍历匹配结果和子匹配。
- regex_token_iterator:遍历未匹配部分。
10.5 正则表达式的符号表示
正则表达式的特殊字符 | 正则表达式的特殊字符 |
---|---|
. 任意单个字符(”通配符“) | \ 下一个字符有特殊意义 |
[ 字符集开始 | * 零或多次重复(后缀操作符) |
] 字符集结束 | + 一次或多次重复(后缀操作符) |
{ 指定重复次数开始 | ? 可选零或一次(后缀操作符) |
} 指定重复次数结束 | | 二选一(或) |
( 分组开始 | ^ 行开始;非 |
( 分组结束 | $ 行结束 |
10.6 建议
- 使用 std::string 来保护字符序列;
- 优先选择 string 操作而不是C风格的字符串函数;
- 使用 string 声明变量和成员,而不要将它作为基类;
- 返回 string 应采用传值方式(依赖移动语义和拷贝消除);
- 直接或间接使用 substr() 读子字符串,使用 replace() 写子字符串;
- string 在需要的时候会自动扩展或收缩;
- 当需要范围检测时,应使用 at() 而不是迭代器或 [];
- 当需要优化性能时,应使用迭代器或 [] ,而不是 at();
- 只有迫不得已时,才使用 c_str() 或 data() 获得 string 的 C 风格字符串表示;
- 使用 stringstream 或通过的值提取函数(如
to<x>
)将字符串转换为数值; - 可用 basic_string 构造任意类型字符组成的字符串;
- 字符串加上 s 后缀用来表示标准库 string;