该absl/strings
库提供了用于操作和比较字符串、将其他类型(例如整数)转换为字符串或为其他用途评估字符串的类和实用程序函数。此外,该 strings
库还包含用于将数据存储在连续内存中的“类字符串”类的实用程序函数。
本文档概述了strings
库的亮点和一般用例。有关特定类、函数和字段的更多详细信息,请参阅特定头文件中的源文档。
尽管“字符串”通常被认为是 C++ 中的标准类型,但它们并不是内置类型,而是通过std::string
类在标准库中提供 。从根本上说,一个字符串由一个大小和一个char
字符数组组成 。
absl::string_view
集装箱
通常,您需要访问字符串数据,但您不需要拥有它,也不需要修改它。出于这个原因,Abseil 定义了一个 absl::string_view
类,它指向一个连续的字符范围,通常是另一个std::string
双引号字符串文字、字符数组甚至另一个 的一部分或全部string_view
。A string_view
,顾名思义,提供其关联字符串数据的只读视图。
大多数 C++ 代码历来使用(较旧的)Cchar*
指针类型或 C++std::string
类来保存字符数据。希望使用这两种类型数据的方法如果想要避免复制数据,通常需要提供重载实现。Astring_view
还充当接受两种类型字符数据的 API 的包装器;方法可以简单地声明它们接受absl::string_view
。
// A common use of string_view: Foo() can accept both char* and std::string via
// implicit conversion to string_view.
void Foo(absl::string_view s) { ... }
string_view
对象非常轻量级,因此您应该始终在方法和函数中按值传递它们;不要通过const absl::string_view &
. (传递absl::string_view
而不是const absl::string_view &
具有相同的算法复杂度,但由于寄存器分配和参数传递规则,在这种情况下按值传递通常更快。)
如上所述,因为string_view
本身不拥有底层数据,所以它应该只用于只读数据。如果您需要向外部 API 用户提供字符串常量,例如:
// C++17: A read-only string in a header file.
inline constexpr absl::string_view kGreeting = "hi";
// C++11: A read-only string in a header file. Due to lifetime issues, a
// string_view is usually a poor choice for a return value (see below), but it's
// safe here because the static storage outlives it.
inline absl::string_view GetGreeting() {
static constexpr char kGreeting[] = "hi";
return kGreeting;
}
string_view
如果您知道底层对象的生命周期长于string_view
变量的生命周期,A也适用于局部 变量。但是,请注意将其绑定到临时值:
// BAD use of string_view: lifetime problem
absl::string_view sv = obj.ReturnAString();
// GOOD use of string_view: str outlives sv
std::string str = obj.ReturnAString();
absl::string_view sv = str;
由于生命周期问题,string_view
对于返回值来说 a 通常是一个糟糕的选择,对于数据成员来说几乎总是一个糟糕的选择。如果您确实以这种方式使用了一个,则您有责任确保string_view
不会超过它指向的对象。
Astring_view
可以代表整个字符串或只是字符串的一部分。例如,拆分字符串时,std::vector<absl::string_view>
是输出的自然数据类型。
注意:有关 的更多信息string_view
,请参阅 abseil.io/tips/1。
注意:有关常量的安全习语的更多信息,请参阅 abseil.io/tips/140。
absl::StrSplit()
用于拆分字符串
该absl::StrSplit()
函数提供了一种将字符串拆分为子字符串的简单方法。StrSplit()
接受要分割的输入字符串、分割字符串的定界符(例如逗号,
)和(可选)作为过滤器的谓词,以判断是否将分割元素包含在结果集中。 StrSplit()
还将返回的集合调整为调用者指定的类型。
例子:
// Splits the given string on commas. Returns the results in a
// vector of strings. (Data is copied once.)
std::vector<std::string> v = absl::StrSplit("a,b,c", ','); // Can also use ","
// v[0] == "a", v[1] == "b", v[2] == "c"
// Splits the string as in the previous example, except that the results
// are returned as `absl::string_view` objects, avoiding copies. Note that
// because we are storing the results within `absl::string_view` objects, we
// have to ensure that the input string outlives any results.
std::vector<absl::string_view> v = absl::StrSplit("a,b,c", ',');
// v[0] == "a", v[1] == "b", v[2] == "c"
StrSplit()
使用传递的Delimiter对象拆分字符串。(请参阅 下面的分隔符。)但是,在许多情况下,您可以简单地将字符串文字作为分隔符传递(它将被隐式转换为 absl::ByString
分隔符)。
例子:
// By default, empty strings are *included* in the output. See the
// `absl::SkipEmpty()` predicate below to omit them{#stringSplitting}.
std::vector<std::string> v = absl::StrSplit("a,b,,c", ',');
// v[0] == "a", v[1] == "b", v[2] == "", v[3] = "c"
// You can also split an empty string
v = absl::StrSplit("", ',');
// v[0] = ""
// The delimiter need not be a single character
std::vector<std::string> v = absl::StrSplit("aCOMMAbCOMMAc", "COMMA");
// v[0] == "a", v[1] == "b", v[2] == "c"
// You can also use the empty string as the delimiter, which will split
// a string into its constituent characters.
std::vector<std::string> v = absl::StrSplit("abcd", "");
// v[0] == "a", v[1] == "b", v[2] == "c", v[3] = "d"
适应返回类型
StrSplit()
API更有用的特性之一是它能够将其结果集调整为所需的返回类型。StrSplit()
返回的集合可能包含std::string
、absl::string_view
或任何可以从absl::string_view
. 这种模式适用于所有的标准STL容器,包括std::vector
,std::list
,std::deque
,std::set
, std::multiset
,std::map
,和std::multimap
,甚至std::pair
,这是不实际的容器。
例子:
// Stores results in a std::set<std::string>, which also performs de-duplication
// and orders the elements in ascending order.
std::set<std::string> s = absl::StrSplit("b,a,c,a,b", ',');
// s[0] == "a", s[1] == "b", s[3] == "c"
// Stores results in a map. The map implementation assumes that the input
// is provided as a series of key/value pairs. For example, the 0th element
// resulting from the split will be stored as a key to the 1st element. If
// an odd number of elements are resolved, the last element is paired with
// a default-constructed value (e.g., empty string).
std::map<std::string, std::string> m = absl::StrSplit("a,b,c", ',');
// m["a"] == "b", m["c"] == "" // last component value equals ""
// Stores first two split strings as the members in a std::pair. Any split
// strings beyond the first two are omitted because std::pair can hold only two
// elements.
std::pair<std::string, std::string> p = absl::StrSplit("a,b,c", ',');
// p.first = "a", p.second = "b" ; "c" is omitted
分隔符
该StrSplit()
API提供了许多“分隔符”,用于提供特殊的分隔符的行为。Delimiter 实现包含一个Find()
函数,该函数知道如何在给定的absl::string_view
. Delimiter 概念的模型表示特定类型的分隔符,例如单个字符、子字符串,甚至正则表达式。
以下 Delimiter 抽象作为StrSplit()
API 的一部分提供:
-
absl::ByString()
(std::string
参数的默认值) -
absl::ByChar()
(char
参数的默认值) -
absl::ByAnyChar()
(用于混合分隔符) -
absl::ByLength()
(用于应用定界符一定次数) -
absl::MaxSplits()
(用于拆分特定次数)
例子:
// Because a `string` literal is converted to an `absl::ByString`, the following
// two splits are equivalent.
std::vector<std::string> v = absl::StrSplit("a,b,c", ",");
std::vector<std::string> v = absl::StrSplit("a,b,c", absl::ByString(","));
// v[0] == "a", v[1] == "b", v[2] == "c"
// Because a `char` literal is converted to an `absl::ByChar`, the following two
// splits are equivalent.
std::vector<std::string> v = absl::StrSplit("a,b,c", ',');
// v[0] == "a", v[1] == "b", v[2] == "c"
std::vector<std::string> v = absl::StrSplit("a,b,c", absl::ByChar(','));
// v[0] == "a", v[1] == "b", v[2] == "c"
// Splits on any of the given characters ("," or ";")
vector<std::string> v = absl::StrSplit("a,b;c", absl::ByAnyChar(",;"));
// v[0] == "a", v[1] == "b", v[2] == "c"
// Uses the `absl::MaxSplits` delimiter to limit the number of matches a
// delimiter can have. In this case, the delimiter of a literal comma is limited
// to matching at most one time. The last element in the returned collection
// will contain all unsplit pieces, which may contain instances of the
// delimiter.
std::vector<std::string> v = absl::StrSplit("a,b,c", absl::MaxSplits(',', 1));
// v[0] == "a", v[1] == "b,c"
// Splits into equal-length substrings.
std::vector<std::string> v = absl::StrSplit("12345", absl::ByLength(2));
// v[0] == "12", v[1] == "34", v[2] == "5"
过滤谓词
谓词可以StrSplit()
通过确定结果元素是否包含在结果集中来过滤操作的结果。过滤谓词可以作为可选的第三个参数传递给StrSplit()
函数。
谓词必须是一元函数(或函子),它们接受单个 absl::string_view
参数并返回一个布尔值,指示该参数是应该被包含 ( true
) 还是排除 ( false
)。
一个使用谓词很有用的例子:过滤掉空子串。默认情况下,空子字符串可以StrSplit()
作为结果集中的单独元素返回,这类似于其他编程语言中拆分函数的工作方式。
// Empty strings *are* included in the returned collection.
std::vector<std::string> v = absl::StrSplit(",a,,b,", ',');
// v[0] == "", v[1] == "a", v[2] == "", v[3] = "b", v[4] = ""
这些空字符串可以通过简单地将提供的SkipEmpty()
谓词作为第三个参数传递给StrSplit()
函数来从结果集中过滤掉。SkipEmpty()
不认为包含所有空格的字符串为空。对于该行为,请使用SkipWhitespace()
谓词。
例子:
// Uses absl::SkipEmpty() to omit empty strings. Strings containing whitespace
// are not empty and are therefore not skipped.
std::vector<std::string> v = absl::StrSplit(",a, ,b,", ',', absl::SkipEmpty());
// v[0] == "a", v[1] == " ", v[2] == "b"
// Uses absl::SkipWhitespace() to skip all strings that are either empty or
// contain only whitespace.
std::vector<std::string> v = absl::StrSplit(",a, ,b,", ',',
absl::SkipWhitespace());
// v[0] == "a", v[1] == "b"
absl::StrCat()
和absl::StrAppend()
字符串连接
大多数关于 C++ 字符串使用的文档都提到,与其他语言不同,C++ 中的字符串是可变的;但是,修改字符串的成本可能很高,因为字符串通常包含大量数据,而且许多模式都涉及创建临时副本,这可能会产生大量开销。始终寻找减少创建此类临时对象的方法。
例如,以下代码效率低下:
// Inefficient code
std::string s1 = "A string";
s1 = s1 + " another string";
上面的赋值运算符创建一个临时字符串,复制s1
到该临时字符串中,连接该临时字符串,然后将其分配回s1
. 而是使用优化的+=
运算符进行此类连接:
// Efficient code
s1 += " another string";
好的编译器或许能够优化前面的低效代码。但是,涉及多个串联的操作通常无法避免临时性:
// Inefficient code
std::string s1 = "A string";
std::string another = " and another string";
s1 += " and some other string" + another;
出于这个原因,Abseil 提供了absl::StrCat()
和absl::StrAppend()
函数来有效地连接和附加字符串。absl::StrCat()
并且absl::StrAppend()
通常比诸如 之类的运算符更有效+=
,因为它们不需要创建临时std::string
对象,并且它们的内存在字符串构造期间预先分配。
// Inefficient code
std::string s1 = "A string";
std::string another = " and another string";
s1 += " and some other string" + another;
// Efficient code
std::string s1 = "A string";
std::string another = " and another string";
absl::StrAppend(&s1, " and some other string", another);
出于这个原因,您应该养成优先使用absl::StrCat()
或 absl::StrAppend()
不使用连接运算符的习惯。
absl::StrCat()
absl::StrCat()
将任意数量的字符串或数字合并为一个字符串,旨在成为从原始 C 字符串、absl::string_view
元素、std::string
值以及布尔值和数字值的混合中构造字符串的最快方法。StrCat()
通常在涉及多个二元运算符(例如a + b + c
或 )的 字符串连接时更有效a += b + c
,因为它们避免在字符串构造期间创建临时字符串对象。
// absl::StrCat() can merge an arbitrary number of strings
std::string s1;
s1 = absl::StrCat("A string ", " another string", "yet another string");
// StrCat() also can mix types, including std::string, string_view, literals,
// and more.
std::string s1;
std::string s2 = "Foo";
absl::string_view sv1 = MyFunction();
s1 = absl::StrCat(s2, sv1, "a literal");
StrCat()
为以下类型提供自动格式化:
std::string
absl::string_view
- 字符串文字
- 数值(浮点数、整数)
- 布尔值(转换为“0”或“1”)
- 通过使用
absl::Hex()
转换函数的十六进制值
浮点值使用与 STL 的 std::basic_ostream::operator« 相同的格式转换为字符串,即 6 位精度,当大小小于 0.001 或大于等于 1e+ 时使用“e”格式6.
您可以使用absl::Hex
类型转换为十六进制输出而不是十进制输出 。为此,请将其Hex(my_int)
作为参数传递给StrCat()
or StrAppend()
。你可能宽度使用指定最小六角字段 absl::PadSpec
枚举,所以等效的StringPrintf("%04x", my_int)
是 absl::StrCat(absl::Hex(my_int, absl::kZeroPad4))
。
absl::StrAppend()
为了清晰和性能,absl::StrCat()
在附加到字符串时不要使用。使用absl::StrAppend()
来代替。特别是,避免使用以下任何(反)模式:
str.append(absl::StrCat(...))
str += absl::StrCat(...)
str = absl::StrCat(str, ...)
absl::StrJoin()
用于连接字符串中的元素
虽然类似于absl::StrCat()
一些类似的用例,但 absl::StrJoin()
提供了更强大的实用程序,用于连接一系列元素、定义分隔符字符串以及将结果格式化为字符串。
范围通过使容器与指定的std::begin()
和std::end()
迭代器,容器特异性begin()
和end()
迭代器,一个支架初始化std::initializer_list
,或std::tuple
异质的对象。分隔符字符串指定为absl::string_view
.
因为默认格式化程序使用absl::AlphaNum
类, absl::StrJoin()
像absl::StrCat()
,将在字符串、整数、浮点数、双精度数等集合上开箱即用。
例子
std::vector<std::string> v = {"foo", "bar", "baz"};
std::string s = absl::StrJoin(v, "-");
// Produces the string "foo-bar-baz"
// Joins the values in the given `std::initializer_list<>` specified using
// brace initialization. This pattern also works with an initializer_list
// of ints or `absl::string_view` -- any `AlphaNum`-compatible type.
std::string s = absl::StrJoin({"foo", "bar", "baz"}, "-");
// Produces the string "foo-bar-baz"
// Joins a collection of ints. This pattern also works with floats,
// doubles, int64s -- any `absl::StrCat()`-compatible type.
std::vector<int> v = {1, 2, 3, -4};
std::string s = absl::StrJoin(v, "-");
// Produces the string "1-2-3--4"
// Joins a collection of pointer-to-int. By default, pointers are
// dereferenced and the pointee is formatted using the default format for
// that type; such dereferencing occurs for all levels of indirection, so
// this pattern works just as well for `std::vector<int**>` as for
// `std::vector<int*>`.
int x = 1, y = 2, z = 3;
std::vector<int*> v = {&x, &y, &z};
std::string s = absl::StrJoin(v, "-");
// Produces the string "1-2-3"
// Dereferencing of `std::unique_ptr<>` is also supported:
std::vector<std::unique_ptr<int>> v
v.push_back(absl::make_unique<int>(1));
v.push_back(absl::make_unique<int>(2));
v.push_back(absl::make_unique<int>(3));
std::string s = absl::StrJoin(v, "-");
// Produces the string "1-2-3"
// Joins a `std::map`, with each key-value pair separated by an equals
// sign. This pattern would also work with, say, a
// `std::vector<std::pair<>>`.
std::map<std::string, int> m = {{"a", 1}, {"b", 2}, {"c", 3}};
std::string s = absl::StrJoin(m, ",", absl::PairFormatter("="));
// Produces the string "a=1,b=2,c=3"
加入格式化程序
absl::StrJoin()
使用“格式化程序”来格式化要加入的元素(AlphaNumFormatter()
如果没有指定格式化程序,则默认为。格式化程序是一个函数对象,负责将其参数格式化为字符串并将其附加到给定的输出字符串。格式化程序可能是实现为函数对象、lambdas 或普通函数。您可以提供自己的 Formatter 以启用absl::StrJoin()
任意类型。
以下是一个自定义 Formatter 的示例,它仅用于 std::to_string()
将整数格式化为字符串:
struct MyFormatter {
void operator()(std::string* out, int i) const {
out->append(std::to_string(i));
}
};
您可以通过将其实例作为最终参数传递给上述格式化程序来使用absl::StrJoin()
:
std::vector<int> v = {1, 2, 3, 4};
std::string s = absl::StrJoin(v, "-", MyFormatter());
// Produces the string "1-2-3-4"
StrJoin()
API中提供了以下标准格式化程序:
-
AlphaNumFormatter()
(默认) -
StreamFormatter()
使用 « 运算符格式化其参数。 -
PairFormatter()``std::pair
通过在对的.first
和.second
成员之间放置一个给定的分隔符来格式化 a 。 -
DereferenceFormatter()
通过取消引用它然后应用给定的格式化程序来格式化它的参数。此格式化程序可用于格式化指向 T 的指针的容器。当在协议缓冲区中加入重复的字段时,经常会出现这种模式。
absl::Substitute()
用于字符串替换
格式化字符串以显示给用户通常有不同的需求。传统上,大多数 C++ 代码使用内置函数,例如sprintf()
和 snprintf()
;这些函数有一些问题,它们不支持 absl::string_view
并且必须管理格式化缓冲区的内存。
// Bad. Need to worry about buffer size and NUL-terminations.
std::string GetErrorMessage(char *op, char *user, int id) {
char buffer[50];
sprintf(buffer, "Error in %s for user %s (id %i)", op, user, id);
return buffer;
}
// Better. Using absl::StrCat() avoids the pitfalls of sprintf() and is faster.
std::string GetErrorMessage(absl::string_view op, absl::string_view user, int id) {
return absl::StrCat("Error in ", op, " for user ", user, " (", id, ")");
}
// Best. Using absl::Substitute() is easier to read and to understand.
std::string GetErrorMessage(absl::string_view op, absl::string_view user, int id) {
return absl::Substitute("Error in $0 for user $1 ($2)", op, user, id);
}
absl::Substitute()
结合的效率和类型安全性质 absl::StrCat()
中包含的常规功能参数结合 sprintf()
。absl::Substitute
使用包含由美元符号 ($) 指示的位置标识符和单个数字位置 ID 的格式字符串来指示在格式字符串中的该位置使用哪些替换参数。
std::string s = Substitute("$1 purchased $0 $2. Thanks $1!", 5, "Bob", "Apples");
// Produces the string "Bob purchased 5 Apples. Thanks Bob!"
std::string s = "Hi. ";
SubstituteAndAppend(&s, "My name is $0 and I am $1 years old.", "Bob", 5);
// Produces the string "Hi. My name is Bob and I am 5 years old."
但是请注意absl::Substitute()
,因为它需要在运行时解析格式字符串,所以 比absl::StrCat()
. 选择Substitute()
在 StrCat()
只有当代码清晰比速度更重要。
与 StringPrintf()
absl::Substitute
与StringPrintf()
以下方面不同:
- 格式字符串不标识参数的类型。相反,参数被隐式转换为字符串。
- 格式字符串中的替换由后跟单个数字的“$”标识。您可以乱序使用参数并多次使用相同的参数。
- 格式字符串中的 ‘$ ′ 序 列 意 味 着 输 出 文 字 ′ ' 序列意味着输出文字 ' ′序列意味着输出文字′’ 字符。
-
absl::Substitute()
明显快于StringPrintf()
. 对于非常大的字符串,它可能会快几个数量级。
支持的类型
absl::Substitute()
了解以下类型:
-
absl::string_view
,std::string
,const char*
(null 等价于“”) -
int32_t
,int64_t
,uint32_t
,uint64_t
-
float
,double
-
bool
(打印为“真”或“假”) - char* 以外的指针类型(打印为
0x<lower case hex string>
,除了 null 打印为“NULL”)
absl::StrContains()
用于字符串匹配
Abseil 字符串库还包含用于执行字符串匹配检查的简单实用程序。他们所有的函数的参数被指定为 absl::string_view
,这意味着这些功能都可以接受std::string
, absl::string_view
或NULL结尾的C风格的字符串。
// Assume "msg" is a line from a logs entry
if (absl::StrContains(msg, "ERROR")) {
*has_error = true;
}
if (absl::StrContains(msg, "WARNING")) {
*has_warning = true;
}
注意:这些函数中参数的顺序旨在模仿等效成员函数的顺序;例如s.Contains(x)
==> absl::StrContains(s, x)
。
与数字类型相互转换
在absl/strings
库中将字符串转换为数字类型的特殊函数 在numbers.h中定义 。以下功能特别有用:
-
absl::SimpleAtoi()
将字符串转换为整数类型。 -
absl::SimpleAtof()
将字符串转换为浮点数。 -
absl::SimpleAtod()
将字符串转换为双精度。 -
absl::SimpleAtob()
将字符串转换为布尔值。
要将数字类型转换为字符串,请使用absl::StrCat()
和 absl::StrAppend()
。您可以使用StrCat/StrAppend
转换int32
, uint32
,int64
,uint64
,float
,和double
类型为字符串:
std::string foo = StrCat("The total is ", cost + tax + shipping);