c++ - 用户定义的字符串文字和模式与 sscanf 匹配

标签 c++ string c++14 scanf

我之前发过一篇关于这个的帖子,但是那个没有很好的解释,所以我删除了它,希望这个会更好。现在我对以下代码有两个问题

#include <vector>
#include <sstream>
#include <iostream>
#include <cstdio>
#include <string>
using std::cerr;
using std::cout;
using std::cin;
using std::endl;
using std::string;
using std::vector;

int main() {

    // the tests
    vector<string> tests {"1.2 when 102"s, "1.2 1.2 1.2"s};

    // format and the storage variables
    string format {"%d %4s %d"};
    int input_1 {-1};
    char char_arr[5];
    int input_2 {-1};

    for (const auto& str : tests) {
        cout << "Number of elements matched : ";
        cout << std::sscanf(str.c_str(), format.c_str(), &input_1, char_arr,
                &input_2) << endl;
        cout << input_1 << endl;
        cout << char_arr << endl;
        cout << input_1 << endl;
    }

    return 0;
}

当我在 Mac 上使用 clang (clang-703.0.29) 编译代码时出现以下错误

test.cpp:16:41: error: no matching literal operator for call to 'operator""s' with
      arguments of types 'const char *' and 'unsigned long', and no matching literal
      operator template

我认为用户定义的字符串文字已在 C++14 中完全实现。为什么这段代码不能编译?我可能在这里做了一些非常愚蠢的事情......

如果我在删除文字后的 s 后运行我的代码,那么我会得到以下输出

Number of elements matched : 2
1
.2
1
Number of elements matched : 3
1
.2
1

为什么 input_2 在第一种情况下是 1?它不应该正确匹配,如果不是,那么为什么它是 1 而不是 -1

此外,如果我想在 sscanf 调用中将 float 视为无效,那么我应该将哪个转义字符或标志放入 format 字符串中?

最佳答案

您的 s 字符串文字不起作用,因为该运算符驻留在命名空间 std::literals::string_literals 中。添加适当的 using 指令可以解决这个问题。

我不相信使用 sscanf 等可以实现您的要求,但是如果您正在寻找一种高效、紧凑的方法来进行此解析并考虑 float 无效,那么我建议 Boost.Spirit .这是使用 Spirit.X3 的快速尝试:

#include <tuple>
#include <string>
#include <vector>
#include <iostream>
#include <boost/fusion/adapted/std_tuple.hpp>
#include <boost/spirit/home/x3.hpp>

int main()
{
    using namespace std::string_literals;
    namespace x3 = boost::spirit::x3;

    auto const format =
        x3::int_ >> ' ' >> x3::repeat(4)[x3::print] >> ' ' >> x3::int_ >> x3::eoi;

    std::vector<std::string> const tests{"1.2 when 102"s, "1.2 1.2 1.2"s,
                                         "12 when 142"s,  "12 foo 142"s};
    for (auto const& str : tests)
    {
        int input_1 = -1;
        std::string chars;
        int input_2 = -1;

        auto attr = std::tie(input_1, chars, input_2);
        auto const success = x3::parse(cbegin(str), cend(str), format, attr);
        std::cout
            << '"' << str << "\" :: parse " << (success ? "succeeded" : "failed") << '\n'
            << input_1 << '\n'
            << chars << '\n'
            << input_2 << "\n\n";
    }
}

Online Demo

这个解析器非常严格——它要求字段之间只有一个空格,字符串字段要求正好有四个可打印字符,并且不允许前导或尾随空格。使用 Spirit 可以非常轻松地放宽所有这些要求,如果发生错误,则具有编译时安全性,而不是在运行时发生 UB/内存损坏。


编辑:使用 Spirit 甚至可以避免复制字符串字段的数据,这与 sscanf 等不同,只要输入字符串数据稳定:

#include <tuple>
#include <string>
#include <vector>
#include <iostream>
#include <boost/fusion/adapted/std_tuple.hpp>
#include <boost/range/iterator_range.hpp>
#include <boost/spirit/home/x3.hpp>

int main()
{
    using namespace std::string_literals;
    namespace x3 = boost::spirit::x3;

    auto const format =
        x3::int_ >> ' ' >> x3::raw[x3::repeat(4)[x3::print]] >> ' ' >> x3::int_ >> x3::eoi;

    std::vector<std::string> const tests{"1.2 when 102"s, "1.2 1.2 1.2"s,
                                         "12 when 142"s,  "12 foo 142"s};
    for (auto const& str : tests)
    {
        int input_1 = -1;
        boost::iterator_range<std::string::const_iterator> chars;
        int input_2 = -1;

        auto attr = std::tie(input_1, chars, input_2);
        auto const success = x3::parse(cbegin(str), cend(str), format, attr);
        std::cout
            << '"' << str << "\" :: parse " << (success ? "succeeded" : "failed") << '\n'
            << input_1 << '\n'
            << chars << '\n'
            << input_2 << "\n\n";
    }
}

Online Demo

关于c++ - 用户定义的字符串文字和模式与 sscanf 匹配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36972767/

相关文章:

c++ - 启用 dll 的 gflags 完整堆不起作用

c++ - 同时专门化外部和嵌套类

C++ - Linux/shell 脚本运行子进程并获取它们的返回状态

c++ - 好主意/坏主意我应该重新实现大部分 C++ 吗?

python - 为什么在Python的交互式解释器中有时会输出转义字符?

java - 转换前后字节数组长度不同

c++ - decltype(auto) 和 decltype(returning expr) 作为返回类型有什么区别?

c++ - 函数模板重载解析困惑

java - 使用 toString java 返回一个字符串

cmake - 将 Googletest 添加到现有的 CMake 项目