当前位置: 首页 > 知识库问答 >
问题:

为什么 std::views::split() 编译但不使用未命名的字符串文字作为模式进行拆分?

孟树
2023-03-14

当<code>std::views::split()</code>获取一个未命名的字符串文本作为模式时,它不会拆分字符串,但可以很好地处理未命名的字符文本。

#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;
}

看现场@ godbolt.org。

我理解字符串文字总是左值。但即使如此,我还是错过了一些将一切联系在一起的重要信息。为什么当我对模式执行同样的操作时,我可以将要拆分为未命名字符串的字符串传递给它,而它却失败了(如:返回原始字符串的范围)?

共有2个答案

陆宇航
2023-03-14
匿名用户

这个答案是完全正确的,我只是想补充几个可能有趣的注意事项。

首先,如果您使用 {fmt} 进行打印,则更容易看到正在发生的事情,因为您也不必编写自己的循环。你可以这样写:

fmt::print("{}\n", rv::split("one:.:two:.:three", ":.:"));

它将输出(这是一系列字符的默认输出):

[[o, n, e, :, ., :, t, w, o, :, ., :, t, h, r, e, e, ]]

在C23中,有一种html" target="_blank">方法可以直接指定此打印为字符串范围,但尚未将其添加到{fmt}中。同时,由于拆分保留了初始范围类别,您可以添加:

auto to_string_views = std::views::transform([](auto sr){
    return std::string_view(sr.data(), sr.size());
});

然后:

fmt::print("{}\n", std::views::split("one:.:two:.:three", ":.:") | to_string_views);

印刷品:

["one:.:two:.:three\x00"]

注意明显的尾随零。同样,接下来的三次尝试格式为:

["one", "two", "three\x00"]
["one", "two", "three\x00"]
["one:two:three\x00"]

我们可以清楚地看到\x00这一事实有助于跟踪问题。

接下来,考虑以下两者之间的区别:

std::views::split("one:.:two:.:three", ":.:")

"one:.:two:.:three" | std::views::split(":.:")

我们通常认为这些是等价的,但它们是...不完全是。在后一种情况下,库必须捕获并保存这些值——这涉及到衰减它们。在这种情况下,因为<代码> ":。:" 衰减为< code>char const*,这不再是传入字符串文字的有效模式。所以上面实际上不编译。

现在,如果它既能正确编译又能正常工作,那就太好了。不幸的是,在语言中无法区分字符串文字(不希望包含空终止符)和char数组(希望包含整个数组)。因此,至少,使用后一种公式,您可以得到错误的东西,不编译。至少,“不编译”比“编译并做一些与我预期完全不同的事情”要好吗?

演示。

鲁辉
2023-03-14

字符串文字总是以空终止符结尾,因此":.:"实际上是一个范围,最后一个元素为\0,大小为4

由于原始字符串不包含这样的模式,因此不会拆分。

在处理C 20范围时,我强烈建议使用string_view而不是原始字符串文字,这与配合得很好

 类似资料:
  • 问题内容: 我有以下python代码。 我得到以下日志打印输出。 如果我使用split(),btw代替split(’‘),则会得到相同的日志打印输出。 为什么split不将结果分成6个条目的列表?我想问题是涉及到http请求,因为我在gae交互式控制台中的测试获得了预期的结果。 问题答案: 不修改字符串。它返回拆分列表。如果要使用该列表,则需要使用来将其分配给某些内容。

  • 假设我们有以下代码: 集会示威 全球抄送 4.7.2, 叮当声 3.2, 硬拷贝 13.0.1 未定义对`void foo的引用 MSVC-11.0 无法解析的外部符号" void __cdecl foo 注意第一个输出中的,第二个输出中是。 为什么?谁是对的?你能引用标准吗?

  • 问题内容: 如果我有一个字节变量: 为什么以下工作: …但这不是吗? 编译器第一理解为第二吗? [编辑] 我知道演员,但我想提请您注意 我认为它们是平等的,那么为什么编译器会有所不同呢?之间有什么区别 问题答案: 因为是的等效项,而的类型被提升为(JLS§5.6.2二进制数值提升),因此,如果不进行显式转换,则不能将其结果分配给它。 根据JLS,§15.26.2复合赋值运算符: 形式为E1 op

  • 在过去的几个月里,我一直在学习C语言并使用终端。我的代码使用g和c11编译并运行得很好,但在过去几天里它开始出现错误,此后我在编译时遇到了问题。我唯一可以编译和运行的程序依赖于旧的C标准。 我第一次遇到的错误包括 尝试使用ecg$g-o stoi_试验stoi_试验编译。cpp-std=c 11 大堆cpp:13:22:错误:命名空间“std”中没有名为“stoi”的成员;你是说“阿托伊”吗?in

  • 看完这些讨论——问题1,问题2,文章 我对Java字符串常量池有以下理解(如果我错了,请纠正我): 编译源代码时,编译器会在我们的程序中查找所有字符串文字(放在双引号中的那些),并在堆区域中创建不同的(无重复)对象,并在称为字符串常量池(方法区域内的区域)的特殊内存区域中维护它们的引用。任何其他字符串对象都是在运行时创建的。 假设我们的代码有以下语句: 当编译上述代码时, 第1行:在堆中创建一个S

  • 代码如下: 运行时,这是错误消息: 错误:不匹配'运算符*'(操作数类型是'std::__cxx11::字符串{aka std::__cxx11::basic_string 如何修复此错误并使程序正确运行?