C++ 使用Boost-Spirit-Qi解析特定字符串

C++ 使用Boost-Spirit-Qi解析特定字符串,c++,boost,boost-spirit,boost-spirit-qi,C++,Boost,Boost Spirit,Boost Spirit Qi,我是Boost Spirit的新手,正在努力创建一个合适的表达式来解析以下输入(实际上是某个命令stdout的结果): 我需要将其解析为一组字符串和整数,并记录在变量中。行的大部分应该被解析为适当类型的变量(string或int)。最后,我得到: string: "^+", "line-17532.dyn.kponet.fi", "+1503us", "+9103us", "55ms" int : 2, 7, 377, 1 一对 +1503us[+9103us] 也可以用空格

我是Boost Spirit的新手,正在努力创建一个合适的表达式来解析以下输入(实际上是某个命令stdout的结果):

我需要将其解析为一组字符串和整数,并记录在变量中。行的大部分应该被解析为适当类型的变量(string或int)。最后,我得到:

string:  "^+", "line-17532.dyn.kponet.fi", "+1503us", "+9103us", "55ms"
int   :   2, 7, 377, 1 
一对

+1503us[+9103us] 
也可以用空格

+503us[ +103us] 
我需要在方括号之前和方括号中的东西分别放在不同的字符串中

此外,时间指定可以表示为

ns, ms, us, s
我很欣赏关于如何处理它的例子,因为可用的文档非常稀少,没有连贯性


一大块日志,以及描述各个字段的标题:

MS Name/IP address         Stratum Poll Reach LastRx Last sample               
===============================================================================
^+ ns2.sdi.fi                    2   9   377   381  -1476us[-1688us] +/-   72ms
^+ line-17532.dyn.kponet.fi      2  10   377   309   +302us[ +302us] +/-   59ms
^* heh.fi                        2  10   377   319  -1171us[-1387us] +/-   50ms
^+ stara.mulimuli.fi             3  10   377   705  -1253us[-1446us] +/-   73ms

由于始终我从绘制有用的AST开始:

namespace AST {
    using clock = std::chrono::high_resolution_clock;

    struct TimeSample {
        enum Direction { up, down } direction; // + or -
        clock::duration value;
    };

    struct Record {
        std::string prefix; // "^+"
        std::string fqdn;   // "line-17532.dyn.kponet.fi"
        int a, b, c, d;     // 2, 7, 377, 1
        TimeSample primary, braced;
        clock::duration tolerance;
    };
}
现在我们知道了要解析的内容,我们主要只是用规则模仿AST,比如:

using namespace qi;

start     = skip(blank) [record_];

record_   = prefix_ >> fqdn_ >> int_ >> int_ >> int_ >> int_ >> sample_ >> '[' >> sample_ >> ']' >> tolerance_;

prefix_   = string("^+"); // or whatever you need to match here
fqdn_     = +graph; // or whatever additional constraints you have
sample_   = direction_ >> duration_;
duration_ = (long_ >> units_) [ _val = _1 * _2 ];
tolerance_= "+/-" >> duration_;
当然,有趣的是单位和方向:

struct directions : qi::symbols<char, AST::TimeSample::Direction> {
    directions() { add("+", AST::TimeSample::up)("-", AST::TimeSample::down); }
} direction_;
struct units : qi::symbols<char, AST::clock::duration> {
    units() {
        using namespace std::literals::chrono_literals;
        add("s", 1s)("ms", 1ms)("us", 1us)("µs", 1us)("ns", 1ns);
    }
} units_;
演示 把它们放在一起,使用它:

int main() {
    std::istringstream iss(R"(^+ line-17532.dyn.kponet.fi      2   7   377     1   +1503us[+9103us] +/-   55ms
)");

    std::string line;

    while (getline(iss, line)) {
        auto f = line.cbegin(), l = line.cend();
        AST::Record record;
        if (parse(f, l, parser<>{}, record))
            std::cout << "parsed: " << boost::fusion::as_vector(record) << "\n";
        else
            std::cout << "parse error\n";

        if (f!=l)
            std::cout << "remaining unparsed input: '" << std::string(f,l) << "'\n";
    }
}
(下面是调试输出)

完整代码:

parsed: (^+ line-17532.dyn.kponet.fi 2 7 377 1 +0.001503s +0.009103s 0.055s)
#define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/fusion/adapted.hpp>
#include <sstream>
#include <chrono>

namespace std { namespace chrono {
    // for debug
    std::ostream& operator<<(std::ostream& os, duration<double> d) { return os << d.count() << "s"; }
} }

namespace AST {
    using clock = std::chrono::high_resolution_clock;

    struct TimeSample {
        enum Direction { up, down } direction; // + or -
        clock::duration value;

        // for debug:
        friend std::ostream& operator<<(std::ostream& os, Direction d) {
            char const* signs[] = {"+","-"};
            return os << signs[d];
        }
        friend std::ostream& operator<<(std::ostream& os, TimeSample const& sample) {
            return os << sample.direction << std::chrono::duration<double>(sample.value).count() << "s";
        }
    };

    struct Record {
        std::string prefix; // "^+"
        std::string fqdn;   // "line-17532.dyn.kponet.fi"
        int a, b, c, d;     // 2, 7, 377, 1
        TimeSample primary, braced;
        clock::duration tolerance;
    };
}

BOOST_FUSION_ADAPT_STRUCT(AST::Record, prefix, fqdn, a, b, c, d, primary, braced, tolerance)
BOOST_FUSION_ADAPT_STRUCT(AST::TimeSample, direction, value)

namespace qi = boost::spirit::qi;

template <typename It = std::string::const_iterator>
struct parser : qi::grammar<It, AST::Record()> {
    parser() : parser::base_type(start) {
        using namespace qi;

        start     = skip(blank) [record_];

        record_   = prefix_ >> fqdn_ >> int_ >> int_ >> int_ >> int_ >> sample_ >> '[' >> sample_ >> ']' >> tolerance_;

        prefix_   = string("^+"); // or whatever you need to match here
        fqdn_     = +graph; // or whatever additional constraints you have
        sample_   = direction_ >> duration_;
        duration_ = (long_ >> units_) [ _val = _1 * _2 ];
        tolerance_= "+/-" >> duration_;

        BOOST_SPIRIT_DEBUG_NODES(
                (start)(record_)
                (prefix_)(fqdn_)(sample_)(duration_)(tolerance_)
            )
    }
  private:
    struct directions : qi::symbols<char, AST::TimeSample::Direction> {
        directions() { add("+", AST::TimeSample::up)("-", AST::TimeSample::down); }
    } direction_;
    struct units : qi::symbols<char, AST::clock::duration> {
        units() {
            using namespace std::literals::chrono_literals;
            add("s", 1s)("ms", 1ms)("us", 1us)("µs", 1us)("ns", 1ns);
        }
    } units_;

    using Skipper = qi::blank_type;
    qi::rule<It, AST::Record()> start;
    qi::rule<It, AST::Record(), Skipper> record_;
    qi::rule<It, AST::TimeSample(), Skipper> sample_;
    qi::rule<It, AST::clock::duration(), Skipper> duration_, tolerance_;
    // lexemes:
    qi::rule<It, std::string()> prefix_;
    qi::rule<It, std::string()> fqdn_;
};

int main() {
    std::istringstream iss(R"(^+ line-17532.dyn.kponet.fi      2   7   377     1   +1503us[+9103us] +/-   55ms
)");

    std::string line;

    while (getline(iss, line)) {
        auto f = line.cbegin(), l = line.cend();
        AST::Record record;
        if (parse(f, l, parser<>{}, record))
            std::cout << "parsed: " << boost::fusion::as_vector(record) << "\n";
        else
            std::cout << "parse error\n";

        if (f!=l)
            std::cout << "remaining unparsed input: '" << std::string(f,l) << "'\n";
    }
}
#定义BOOST\u SPIRIT\u DEBUG
#包括
#包括
#包括
#包括
#包括
名称空间std{namespace chrono{
//用于调试
std::ostream&operator sample_>>'['>>sample_>>']'>>公差;
前缀=字符串(“^+”;//或任何需要在此处匹配的内容
fqdn \=+graph;//或任何其他约束
样本=方向>持续时间;
持续时间=(长>单位)[u val=\u 1*\u 2];
公差\=“+/-”>>持续时间\;
BOOST_SPIRIT_DEBUG_节点(
(开始)(记录)
(前缀)(fqdn)(样本)(持续时间)(公差)
)
}
私人:
结构方向:qi::符号{
方向(){add(“+”,AST::TimeSample::up)(“-”,AST::TimeSample::down);}
}方向;;
结构单位:qi::符号{
单位(){
使用名称空间std::literals::chrono_literals;
添加(“s”,1s)(“ms”,1ms)(“us”,1us)(“µs”,1us)(“ns”,1ns);
}
}单位;;
使用Skipper=qi::blank\u类型;
qi::规则开始;
规则记录;
qi::规则样本;
qi::规则持续时间,公差;
//词素:
qi::规则前缀;
qi::规则fqdn;
};
int main(){
标准::istringstream iss(R“(^+line-17532.dyn.kponet.fi 2 7 377 1+1503us[+9103us]+/-55ms
)");
std::字符串行;
while(getline(iss,line)){
自动f=line.cbegin(),l=line.cend();
AST::记录;
if(解析(f,l,解析器{},记录))

STD::CUT

注释:这个答案显示了一个更简单的方法,为SEHE所示的附加技术奠定了基础。 序言 让我们启用Spirit调试输出,以便在开发解析时跟踪解析的进度

#define BOOST_SPIRIT_DEBUG 1

#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/adapt_struct.hpp>

namespace qi = boost::spirit::qi;
调整数据结构 为了能够使用该结构作为Spirit语法的属性,我们需要将其调整为一个融合元组(更多信息参见),这是通过使用来实现的

对数行文法 接下来,我们定义日志条目的语法。由于各个条目可能由空格分隔,因此我们希望使用短语解析,因此需要指定跳过解析器。是一个合适的跳过器,因为它只匹配空格和制表符

然而,所有的元素都应该被视为词素,我们没有为它们的规则指定任何跳过符

template <typename Iterator>
struct log_line_parser
    : qi::grammar<Iterator, log_entry_t(), qi::blank_type>
{
    typedef qi::blank_type skipper_t;

    log_line_parser()
        : log_line_parser::base_type(log_line)
    {
        element_0 %= qi::string("^+");
        element_1 %= qi::raw[(+qi::char_("-a-zA-Z0-9") % qi::char_('.'))];
        element_2 %= qi::uint_;
        element_3 %= qi::uint_;
        element_4 %= qi::uint_;
        element_5 %= qi::uint_;
        element_6 %= qi::raw[qi::char_('+') >> qi::uint_ >> time_unit];
        element_7 %= qi::raw[qi::char_('+') >> qi::uint_ >> time_unit];
        element_8 %= qi::raw[qi::uint_ >> time_unit];

        time_unit %= -qi::char_("nmu") >> qi::char_('s');

        log_line
            %=  element_0
            >>  element_1
            >>  element_2
            >>  element_3
            >>  element_4
            >>  element_5
            >>  element_6
            >>  qi::lit('[') >> element_7 >> qi::lit(']')
            >>  qi::lit("+/-")
            >>  element_8
            ;

        init_debug();
    }

    void init_debug()
    {
        BOOST_SPIRIT_DEBUG_NODE(element_0);
        BOOST_SPIRIT_DEBUG_NODE(element_1);
        BOOST_SPIRIT_DEBUG_NODE(element_2);
        BOOST_SPIRIT_DEBUG_NODE(element_3);
        BOOST_SPIRIT_DEBUG_NODE(element_4);
        BOOST_SPIRIT_DEBUG_NODE(element_5);
        BOOST_SPIRIT_DEBUG_NODE(element_6);
        BOOST_SPIRIT_DEBUG_NODE(element_7);
        BOOST_SPIRIT_DEBUG_NODE(element_8);

        BOOST_SPIRIT_DEBUG_NODE(time_unit);

        BOOST_SPIRIT_DEBUG_NODE(log_line);
    }

private:
    qi::rule<Iterator, std::string()> element_0;
    qi::rule<Iterator, std::string()> element_1;
    qi::rule<Iterator, uint32_t()> element_2;
    qi::rule<Iterator, uint32_t()> element_3;
    qi::rule<Iterator, uint32_t()> element_4;
    qi::rule<Iterator, uint32_t()> element_5;
    qi::rule<Iterator, std::string()> element_6;
    qi::rule<Iterator, std::string()> element_7;
    qi::rule<Iterator, std::string()> element_8;

    qi::rule<Iterator, std::string()> time_unit;

    qi::rule<Iterator, log_entry_t(), skipper_t> log_line;
};
主要功能 最后,让我们在解析器上运行一些肯定和否定测试

int main()
{
    bool result(true);
    result &= test("^+ line-17532.dyn.kponet.fi      2   7   377     1   +1503us[+9103us] +/-   55ms");
    result &= test("^+ line-17532.dyn.kponet.fi      2   7   377     1   +1503us[ +9103us] +/-   55ms");
    result &= test("^+ line-17532.dyn.kponet.fi      2   7   377     1   +1503ms[+9103ns] +/-   55s");

    result &= !test("^- line-17532.dyn.kponet.fi      2   7   377     1   +1503us[+9103us] +/-   55ms");
    result &= !test("^+ line-17532.dyn.kponet.fi      2   7   377     1   +1503us[+9103us] +/-   55 ms");
    result &= !test("^+ line-17532.dyn.kponet.fi      2   7   377     1   + 1503us[+9103us] +/-   55ms");
    result &= !test("^+ line-17532.dyn.kponet.fi      2   7   +377     1   +1503us[+9103us] +/-   55ms");
    result &= !test("^+ line-17532.dyn.kponet.fi      2   7   3 77     1   +1503us[+9103us] +/-   55ms");
    result &= !test("^+ line-17532.dyn.kponet.fi      2   7   -377     1   +1503us[+9103us] +/-   55ms");


    std::cout << "Test result = " << result << "\n";

    return 0;
}


< P>这是我几乎可以对那些声称C++只是增加了复杂性的人感到同情,而C确实更好。它确实失去了一些类型安全性之类的东西,但是考虑一下C的代码> SCANF:

什么样的阅读。
struct record {
    char prefix[256];
    char url[256];
    int a, b, c, d;
    char time1[256];
    char time2[256];
    char time3[256];
};

sscanf(input, 
       "%255s %255s %d %d %d %d %255[^[][ %255[^]]] +/- %255s",
       r.prefix, r.url, &r.a, &r.b, &r.c, &r.d, r.time1, r.time2, r.time3);
当然,这确实有一些潜在的责任:

  • 它读入字符数组,而不是
    std::string
    s
  • scanf
    和堂兄弟姐妹的类型不安全
  • 它不会尝试验证times上的后缀
  • 基于Spirit的解析器可能很容易至少要快一些

  • 如果其中任何一项对于您的目的来说确实是一个严重的问题,您可能真的需要一种不同的方法。考虑到代码的目的可能是什么,它们中的任何一项都不可能立即导致真正的问题。

    您能描述一下您显示的行的结构吗?请说出各个元素的名称并定义应捕获的数据类型。是否有任何部分可选?单位(us和ms)是否始终如您所示(前两个单位为us,后两个单位为ms),或者是否存在一些可能的变化?很明显,但我更新了问题。我需要将+1503us识别为字符串(在所有地方都可以是我们、ms、s、ns,所以不,它们不是固定的)。是的,有些事情是显而易见的,但最好不要假设:)我也希望为每个条目取一些有意义的名称,所以我不必称它们为“element_0”-“element_8”,但没关系。(故意相当详细)我正在写一个答案。我发现你发现了我的另一个问题。我想把它删掉为非特异性?“丹玛·埃库赫,我知道你会打我的,我正在写它的时候:D好的,通常的。嗯。科利鲁出了什么问题。演示在WangBox上。(我更喜欢Qi中的凤凰演员…)对于某些任务来说还不错:)我总是认为人们会想使用解析的信息,这对INT来说真的很舒服,依我看。但是,是的,它确实有一些优雅。@sehe:它仍然会产生NUL终止的字节序列,所以如果(例如)你真的需要在输出中使用
    std::string
    ,构建它们非常简单(尽管这样做显然会增加更多的代码)
    struct log_entry_t
    {
        std::string element_0;
        std::string element_1;
        uint32_t element_2;
        uint32_t element_3;
        uint32_t element_4;
        uint32_t element_5;
        std::string element_6;
        std::string element_7;
        std::string element_8;
    };
    
    BOOST_FUSION_ADAPT_STRUCT(
        log_entry_t
        , (std::string, element_0)
        , (std::string, element_1)
        , (uint32_t, element_2)
        , (uint32_t, element_3)
        , (uint32_t, element_4)
        , (uint32_t, element_5)
        , (std::string, element_6)
        , (std::string, element_7)
        , (std::string, element_8)
    )
    
    template <typename Iterator>
    struct log_line_parser
        : qi::grammar<Iterator, log_entry_t(), qi::blank_type>
    {
        typedef qi::blank_type skipper_t;
    
        log_line_parser()
            : log_line_parser::base_type(log_line)
        {
            element_0 %= qi::string("^+");
            element_1 %= qi::raw[(+qi::char_("-a-zA-Z0-9") % qi::char_('.'))];
            element_2 %= qi::uint_;
            element_3 %= qi::uint_;
            element_4 %= qi::uint_;
            element_5 %= qi::uint_;
            element_6 %= qi::raw[qi::char_('+') >> qi::uint_ >> time_unit];
            element_7 %= qi::raw[qi::char_('+') >> qi::uint_ >> time_unit];
            element_8 %= qi::raw[qi::uint_ >> time_unit];
    
            time_unit %= -qi::char_("nmu") >> qi::char_('s');
    
            log_line
                %=  element_0
                >>  element_1
                >>  element_2
                >>  element_3
                >>  element_4
                >>  element_5
                >>  element_6
                >>  qi::lit('[') >> element_7 >> qi::lit(']')
                >>  qi::lit("+/-")
                >>  element_8
                ;
    
            init_debug();
        }
    
        void init_debug()
        {
            BOOST_SPIRIT_DEBUG_NODE(element_0);
            BOOST_SPIRIT_DEBUG_NODE(element_1);
            BOOST_SPIRIT_DEBUG_NODE(element_2);
            BOOST_SPIRIT_DEBUG_NODE(element_3);
            BOOST_SPIRIT_DEBUG_NODE(element_4);
            BOOST_SPIRIT_DEBUG_NODE(element_5);
            BOOST_SPIRIT_DEBUG_NODE(element_6);
            BOOST_SPIRIT_DEBUG_NODE(element_7);
            BOOST_SPIRIT_DEBUG_NODE(element_8);
    
            BOOST_SPIRIT_DEBUG_NODE(time_unit);
    
            BOOST_SPIRIT_DEBUG_NODE(log_line);
        }
    
    private:
        qi::rule<Iterator, std::string()> element_0;
        qi::rule<Iterator, std::string()> element_1;
        qi::rule<Iterator, uint32_t()> element_2;
        qi::rule<Iterator, uint32_t()> element_3;
        qi::rule<Iterator, uint32_t()> element_4;
        qi::rule<Iterator, uint32_t()> element_5;
        qi::rule<Iterator, std::string()> element_6;
        qi::rule<Iterator, std::string()> element_7;
        qi::rule<Iterator, std::string()> element_8;
    
        qi::rule<Iterator, std::string()> time_unit;
    
        qi::rule<Iterator, log_entry_t(), skipper_t> log_line;
    };
    
    bool test(std::string const& log)
    {
        std::cout << "Parsing: " << log << "\n\n";
    
        std::string::const_iterator iter(log.begin());
        std::string::const_iterator end(log.end());
    
        log_line_parser<std::string::const_iterator> g;
    
        log_entry_t entry;
    
        bool r(qi::phrase_parse(iter, end, g, qi::blank, entry));
    
        std::cout << "-------------------------\n";
    
        if (r && (iter == end)) {
            std::cout << "Parsing succeeded\n";
            std::cout << entry.element_0 << "\n"
                << entry.element_1 << "\n"
                << entry.element_2 << "\n"
                << entry.element_3 << "\n"
                << entry.element_4 << "\n"
                << entry.element_5 << "\n"
                << entry.element_6 << "\n"
                << entry.element_7 << "\n"
                << entry.element_8 << "\n";
        } else {
            std::string::const_iterator some = iter + 30;
            std::string context(iter, (some > end) ? end : some);
            std::cout << "Parsing failed\n";
            std::cout << "stopped at: \": " << context << "...\"\n";
        }
    
        return r;
    }
    
    int main()
    {
        bool result(true);
        result &= test("^+ line-17532.dyn.kponet.fi      2   7   377     1   +1503us[+9103us] +/-   55ms");
        result &= test("^+ line-17532.dyn.kponet.fi      2   7   377     1   +1503us[ +9103us] +/-   55ms");
        result &= test("^+ line-17532.dyn.kponet.fi      2   7   377     1   +1503ms[+9103ns] +/-   55s");
    
        result &= !test("^- line-17532.dyn.kponet.fi      2   7   377     1   +1503us[+9103us] +/-   55ms");
        result &= !test("^+ line-17532.dyn.kponet.fi      2   7   377     1   +1503us[+9103us] +/-   55 ms");
        result &= !test("^+ line-17532.dyn.kponet.fi      2   7   377     1   + 1503us[+9103us] +/-   55ms");
        result &= !test("^+ line-17532.dyn.kponet.fi      2   7   +377     1   +1503us[+9103us] +/-   55ms");
        result &= !test("^+ line-17532.dyn.kponet.fi      2   7   3 77     1   +1503us[+9103us] +/-   55ms");
        result &= !test("^+ line-17532.dyn.kponet.fi      2   7   -377     1   +1503us[+9103us] +/-   55ms");
    
    
        std::cout << "Test result = " << result << "\n";
    
        return 0;
    }
    
    Parsing: ^+ line-17532.dyn.kponet.fi      2   7   377     1   +1503us[+9103us] +/-   55ms
    
    <log_line>
      <try>^+ line-17532.dyn.kp</try>
      <element_0>
        <try>^+ line-17532.dyn.kp</try>
        <success> line-17532.dyn.kpon</success>
        <attributes>[[^, +]]</attributes>
      </element_0>
      <element_1>
        <try>line-17532.dyn.kpone</try>
        <success>      2   7   377   </success>
        <attributes>[[l, i, n, e, -, 1, 7, 5, 3, 2, ., d, y, n, ., k, p, o, n, e, t, ., f, i]]</attributes>
      </element_1>
      <element_2>
        <try>2   7   377     1   </try>
        <success>   7   377     1   +</success>
        <attributes>[2]</attributes>
      </element_2>
      <element_3>
        <try>7   377     1   +150</try>
        <success>   377     1   +1503</success>
        <attributes>[7]</attributes>
      </element_3>
      <element_4>
        <try>377     1   +1503us[</try>
        <success>     1   +1503us[+91</success>
        <attributes>[377]</attributes>
      </element_4>
      <element_5>
        <try>1   +1503us[+9103us]</try>
        <success>   +1503us[+9103us] </success>
        <attributes>[1]</attributes>
      </element_5>
      <element_6>
        <try>+1503us[+9103us] +/-</try>
        <time_unit>
          <try>us[+9103us] +/-   55</try>
          <success>[+9103us] +/-   55ms</success>
          <attributes>[[u, s]]</attributes>
        </time_unit>
        <success>[+9103us] +/-   55ms</success>
        <attributes>[[+, 1, 5, 0, 3, u, s]]</attributes>
      </element_6>
      <element_7>
        <try>+9103us] +/-   55ms</try>
        <time_unit>
          <try>us] +/-   55ms</try>
          <success>] +/-   55ms</success>
          <attributes>[[u, s]]</attributes>
        </time_unit>
        <success>] +/-   55ms</success>
        <attributes>[[+, 9, 1, 0, 3, u, s]]</attributes>
      </element_7>
      <element_8>
        <try>55ms</try>
        <time_unit>
          <try>ms</try>
          <success></success>
          <attributes>[[m, s]]</attributes>
        </time_unit>
        <success></success>
        <attributes>[[5, 5, m, s]]</attributes>
      </element_8>
      <success></success>
      <attributes>[[[^, +], [l, i, n, e, -, 1, 7, 5, 3, 2, ., d, y, n, ., k, p, o, n, e, t, ., f, i], 2, 7, 377, 1, [+, 1, 5, 0, 3, u, s], [+, 9, 1, 0, 3, u, s], [5, 5, m, s]]]</attributes>
    </log_line>
    -------------------------
    Parsing succeeded
    ^+
    line-17532.dyn.kponet.fi
    2
    7
    377
    1
    +1503us
    +9103us
    55ms
    
    Test result = 1
    
    struct record {
        char prefix[256];
        char url[256];
        int a, b, c, d;
        char time1[256];
        char time2[256];
        char time3[256];
    };
    
    sscanf(input, 
           "%255s %255s %d %d %d %d %255[^[][ %255[^]]] +/- %255s",
           r.prefix, r.url, &r.a, &r.b, &r.c, &r.d, r.time1, r.time2, r.time3);