kha*_*aos 23 c++ split string-literals std-ranges c++23
当std::views::split()获取未命名的字符串文字作为模式时,它不会分割字符串,但可以很好地处理未命名的字符文字。
#include <iomanip>
#include <iostream>
#include <ranges>
#include <string>
#include <string_view>
int main(void)
{
using namespace std::literals;
// returns the original string (not splitted)
auto splittedWords1 = std::views::split("one:.:two:.:three", ":.:");
for (const auto word : splittedWords1)
std::cout << std::quoted(std::string_view(word));
std::cout << std::endl;
// returns the splitted string
auto splittedWords2 = std::views::split("one:.:two:.:three", ":.:"sv);
for (const auto word : splittedWords2)
std::cout << std::quoted(std::string_view(word));
std::cout << std::endl;
// returns the splitted string
auto splittedWords3 = std::views::split("one:two:three", ':');
for (const auto word : splittedWords3)
std::cout << std::quoted(std::string_view(word));
std::cout << std::endl;
// returns the original string (not splitted)
auto splittedWords4 = std::views::split("one:two:three", ":");
for (const auto word : splittedWords4)
std::cout << std::quoted(std::string_view(word));
std::cout << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
请参阅@ godbolt.org直播。
我知道字符串文字始终是左值。但尽管如此,我还是遗漏了一些将一切联系在一起的重要信息。为什么我可以传递我想要拆分为未命名字符串文字的字符串,而当我对模式执行相同操作时它会失败(如:返回原始字符串的范围)?
康桓瑋*_*康桓瑋 34
字符串文字始终以空终止符结尾,因此":.:"实际上是最后一个元素为\0且大小为 的4范围。
由于原始字符串不包含这样的模式,因此不会被分割。
在处理 C++20 范围时,我强烈建议使用string_view原始字符串文字来代替,这<ranges>可以很好地处理并避免容易出错的空终止符问题。
Bar*_*rry 13
这个答案是完全正确的,我只想添加一些可能有趣的附加注释。
首先,如果您用于{fmt}打印,则更容易看到发生了什么,因为您也不必编写自己的循环。你可以这样写:
fmt::print("{}\n", rv::split("one:.:two:.:three", ":.:"));
Run Code Online (Sandbox Code Playgroud)
将输出(这是一系列字符范围的默认输出):
[[o, n, e, :, ., :, t, w, o, :, ., :, t, h, r, e, e, ]]
Run Code Online (Sandbox Code Playgroud)
在 C++23 中,将有一种方法可以直接指定此打印为字符串范围,但尚未添加{fmt}。同时,由于split保留了初始范围类别,因此您可以添加:
[[o, n, e, :, ., :, t, w, o, :, ., :, t, h, r, e, e, ]]
Run Code Online (Sandbox Code Playgroud)
进而:
auto to_string_views = std::views::transform([](auto sr){
return std::string_view(sr.data(), sr.size());
});
Run Code Online (Sandbox Code Playgroud)
印刷:
["one:.:two:.:three\x00"]
Run Code Online (Sandbox Code Playgroud)
请注意明显的尾随零。同样,接下来的三次尝试格式如下:
["one", "two", "three\x00"]
["one", "two", "three\x00"]
["one:two:three\x00"]
Run Code Online (Sandbox Code Playgroud)
事实上,我们可以清楚地看到\x00有助于追踪问题。
接下来,考虑以下之间的区别:
fmt::print("{}\n", std::views::split("one:.:two:.:three", ":.:") | to_string_views);
Run Code Online (Sandbox Code Playgroud)
和
["one:.:two:.:three\x00"]
Run Code Online (Sandbox Code Playgroud)
我们通常认为它们是等价的,但它们……并不完全是等价的。在后一种情况下,图书馆必须捕获并存储这些值 - 这涉及到衰减它们。在这种情况下,因为":.:"衰变成char const*,所以它不再是传入字符串文字的有效模式。所以上面的内容实际上并没有编译。
现在,如果它既能编译又能正常工作,那就太好了。不幸的是,在语言中无法区分字符串文字(您不想包含空终止符)和数组char(您想要包含整个数组)。因此,至少,使用后一种表述,您可能会得到无法编译的错误结果。至少 - “不编译”比“编译并做了一些与我预期完全不同的事情”更好?
演示。