📅  最后修改于: 2023-12-03 14:59:51.349000             🧑  作者: Mango
字符串插值是C++中的一种字符串拼接方式,它能够将变量的值和字符串连接起来形成一个新的字符串。C++中的字符串插值使用的是std::format
函数实现的。
字符串插值使用的格式化语法类似于python中的格式化字符串,使用大括号括起来的变量会被自动替换为相应的值。举个例子,假设我们有一个字符串"hello {}"
,我们可以通过插值的方式将其转换为"hello world"
:
std::string hello = "world";
std::string message = std::format("hello {}", hello);
上面的代码将"world"
插入到了大括号内,输出结果为"hello world"
。
在大括号内还可以添加更多的格式化选项,例如:
{:d}
:将值格式化为十进制整数{:x}
:将值格式化为十六进制整数{:f}
:将值格式化为浮点数{:s}
:将值格式化为字符串例如,"{:d} + {:d} = {:d}"
可以转换为"2 + 2 = 4"
。
在现实应用中,我们可能有多个需要插入到字符串中的变量。我们可以使用位置参数来传递这些变量,位置参数用数字表示,从0开始。例如:
std::string hello = "world";
int n = 10;
std::string message = std::format("hello {0}, n={1}", hello, n);
上面的代码中,hello
和n
会按照位置参数的顺序被插入到字符串中,输出结果为"hello world, n=10"
。
使用位置参数的方式有时会让代码难以理解,特别是当有多个参数并且位置混乱时。因此,C++中支持使用命名参数的方式来传递参数。命名参数使用花括号括起来,并在花括号中使用name=value
的格式来定义。例如:
std::string hello = "world";
int n = 10;
std::string message = std::format("hello {name}, n={num}",
std::arg("name", hello),
std::arg("num", n));
上面的代码中,hello
和n
被分别放到了name
和num
中,输出结果与之前相同。
同时,我们也可以使用std::format_args
结构来定义一组命名参数,并将其传递给std::format
函数:
std::string hello = "world";
int n = 10;
auto args = std::format_args(std::arg("name", hello),
std::arg("num", n));
std::string message = std::format("hello {name}, n={num}", args);
当我们需要对特殊的类型进行特殊处理时,我们可以使用自定义格式化函数。自定义格式化函数有两种方式:重载std::format_to
和 std::formatter
。
std::format_to
重载std::format_to
函数可以让我们自定义对某种类型的格式化方式。std::format_to
的用法与std::format
类似,只不过它将输出结果写入到指定的输出流中。例如:
struct Person {
std::string name;
int age;
};
void std::format_to(std::ostream& os, const Person& p) {
os << "name=" << p.name << ", age=" << p.age;
}
int main() {
Person p{"Tom", 18};
std::cout << std::format("person: {}", p) << std::endl;
return 0;
}
上面的代码中,我们为Person
类型实现了自定义的格式化功能,输出结果为"person: name=Tom, age=18"
。
std::formatter
C++20中引入了std::formatter
,它是一种更加强大的自定义格式化方式。std::format_to
只能输出到流中,而std::formatter
可以直接输出到字符串中。同时,std::formatter
还支持更多的格式化选项,例如:
align
:对齐方式fill
:填充字符width
:设置输出宽度precision
:设置浮点数输出精度例如,我们可以使用std::formatter
重写之前的Person
类型的格式化:
struct Person {
std::string name;
int age;
};
template <typename Char>
struct my_formatter : std::formatter<Person, Char> {
template <typename FormatContext>
auto format(const Person& p, FormatContext& ctx) {
return std::format_to(ctx.out(), "name={:{}}", p.name, ctx.width());
}
};
template <typename Char>
struct my_formatter<std::tuple_element_t<0, typename FormatContext::type>> :
std::formatter<Person, Char> {
template <typename FormatContext>
auto format(const Person& p, FormatContext& ctx) {
auto& out = ctx.out();
auto width = ctx.width();
auto fill = ctx.fill();
auto align = ctx.align();
if (align == std::format_align::right) {
out << std::setw(width) << std::setfill(fill) << p.name;
} else if (align == std::format_align::left) {
out << p.name << std::setw(width) << std::setfill(fill);
} else {
out << p.name;
}
return out;
}
};
int main() {
Person p{"Tom", 18};
std::cout << std::format("person: {:{}}", my_formatter<char>{}, p, 10) << std::endl;
std::cout << std::format("person: {:{<10}}", my_formatter<std::tuple<char, int>>{}, p) << std::endl;
return 0;
}
上面的代码中,我们为Person
类型实现了两个版本的自定义格式化器。第一个版本my_formatter<char>
只提供了最基本的格式化功能,将name插入到format上下文中指定的位置,并使用宽度选项进行格式化。第二个版本my_formatter<std::tuple<char, int>>
在第一个版本的基础上增加了更多的格式化选项,例如填充字符、对齐方式等。
字符串插值是一种非常方便的C++字符串拼接方式,它使用std::format
函数实现,语法类似于python中的格式化字符串,支持位置参数、命名参数以及自定义格式化器。掌握字符串插值对于C++开发者来说是非常有帮助的。