absl教程(四):Strings Library

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::stringabsl::string_view或任何可以从absl::string_view. 这种模式适用于所有的标准STL容器,包括std::vectorstd::liststd::dequestd::setstd::multisetstd::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::SubstituteStringPrintf()以下方面不同:

  • 格式字符串不标识参数的类型。相反,参数被隐式转换为字符串。
  • 格式字符串中的替换由后跟单个数字的“$”标识。您可以乱序使用参数并多次使用相同的参数。
  • 格式字符串中的 ‘$ ′ 序 列 意 味 着 输 出 文 字 ′ ' 序列意味着输出文字 ' ′序列意味着输出文字′’ 字符。
  • 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::stringabsl::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转换int32uint32int64uint64float,和double类型为字符串:

std::string foo = StrCat("The total is ", cost + tax + shipping);
上一篇:判断是否字符串 和去除多余前后空格


下一篇:Winform中使用mysqldump实现选择部分表定期备份mysql数据库