C++ 将数字字符串解析为结构向量

C++ 将数字字符串解析为结构向量,c++,c++11,C++,C++11,我想把一串数字解析成元素向量。字符串由四个数字块组成,用():/分隔,每个块用分隔 具体来说,字符串的格式是:int(int):float/float,请参见下面的代码示例。我想我可以使用正则表达式,但是由于数据是如此结构化,我确信一定有一种更容易接近的方法来解析这样的字符串。我用的是istringstream,但感觉有点笨拙 std::string line = "0(0):0/0;1(2):0.01/0.02;2(4):0.02/0.04;3(6):0.03/0.06;" struct El

我想把一串数字解析成元素向量。字符串由四个数字块组成,用
():/
分隔,每个块用
分隔

具体来说,字符串的格式是:
int(int):float/float,请参见下面的代码示例。我想我可以使用正则表达式,但是由于数据是如此结构化,我确信一定有一种更容易接近的方法来解析这样的字符串。我用的是istringstream,但感觉有点笨拙

std::string line = "0(0):0/0;1(2):0.01/0.02;2(4):0.02/0.04;3(6):0.03/0.06;"
struct Element {
  int a;
  int b;
  int c;
  int d;
};

std::vector<Element> = parse(line);


std::vector<Element> parse(std::string line)
{
  std::vector<Element> elements;
  std::istringstream iss(line);
  while(iss) {
    char dummy;
    Element element;

    iss >> element.a;
    iss.read(&dummy,sizeof(dummy)); // (
    iss >> element.b;
    iss.read(&dummy,sizeof(dummy)); // )
    iss.read(&dummy,sizeof(dummy)); // :
    iss >> element.c;
    iss.read(&dummy,sizeof(dummy)); // /
    iss >> element.d;
    iss.read(&dummy,sizeof(dummy)); // ;

    if (!iss) {break;}

    elements.push_back(element);
  }
  return elements;
}
std::string line=“0(0):0/0;1(2):0.01/0.02;2(4):0.02/0.04;3(6):0.03/0.06;”
结构元素{
INTA;
int b;
INTC;
int d;
};
std::vector=parse(行);
std::向量解析(std::字符串行)
{
std::向量元素;
标准::istringstream iss(线);
while(国际空间站){
模拟字符;
元素;
iss>>元素a;
iss.read(&dummy,sizeof(dummy));//(
iss>>元素b;
iss.read(&dummy,sizeof(dummy));/)
iss.read(&dummy,sizeof(dummy));/:
iss>>元素c;
iss.read(&dummy,sizeof(dummy));///
iss>>元素d;
iss.read(&dummy,sizeof(dummy));/;
如果(!iss){break;}
元素。推回(元素);
}
返回元素;
}
我的问题是:

  • 什么是解析的好方法?我是否应该使用
    std::stringstream
    并逐个读取数字,然后“切掉”中间的字符?和代码示例中一样
  • 此代码有一个错误,并试图读取一组额外的值,因为在读入最后一个字符后,
    while(iss)
    仍然为真。如何在每次
    iss>>
    之后在不进行测试的情况下终止此循环?或者更一般地说,如何循环从istringstream提取

  • 您的数据结构良好,可以轻松重载
    运算符>>
    std::ifstream
    提取类成员,然后继续从
    istringstream
    或文件流读取它们

    下面是一个可能的实现:

    #include <iostream>
    #include <vector>
    #include <string>
    #include <sstream>
    #include <fstream>
    #include <iterator>
    #include <stdexcept>
    
    class Element
    {
    public:
        Element() {}
        Element(int aa, int bb, float cc, float dd) : a{aa}, b{bb}, c{cc}, d{dd} {}
    
        friend std::istream &operator>> (std::istream &in, Element &e);
        friend std::ostream &operator<< (std::ostream &out, Element const &e);
    
    private:
        int a;
        int b;
        float c;
        float d;
    };
    
    std::istream &operator>> (std::istream &in, Element &e)
    {
        char delimiter;
        if ( not ( in >> e.a >> delimiter  and  delimiter == '(' and
                   in >> e.b >> delimiter  and  delimiter == ')' and
                   in >> delimiter         and  delimiter == ':' and
                   in >> e.c >> delimiter  and  delimiter == '/' and
                   in >> e.d >> delimiter  and  delimiter == ';' )
             and not in.eof() )
        {
            in.setstate(std::ios_base::failbit);
        }
        return in;
    
    }
    
    std::ostream &operator<< (std::ostream &out, Element const &e)
    {
        return out << e.a << '(' << e.b << "):" << e.c << '/' << e.d << ';';
    }
    
    std::vector<Element> read_Elements_from(std::istream &in)
    {
        std::vector<Element> tmp (
            std::istream_iterator<Element>{in},
            std::istream_iterator<Element>{}
        );
        if ( not in.eof() )
            throw std::runtime_error("Wrong format");
    
        return tmp;
    }
    
    int main()
    {
      try
      {
        using std::cout;
        std::istringstream iss {
            "0(0):0/0;1(2):0.01/0.2;2(4):0.02/0.04;3(6):0.03/0.06;"
        };
    
        auto els_s = read_Elements_from(iss);
    
        cout << "Elements read from the string:\n";
        for ( auto const &i : els_s )
        {
            cout << i << '\n';
        }
    
        // assuming a file which lines are similar to the string provided
        std::ifstream input_file {"input_data.txt"};
        if ( not input_file )
            throw std::runtime_error("Can't open input file");
    
        auto els_f = read_Elements_from(input_file);
    
        cout << "\nElements read from the file:\n";
        for ( auto const &i : els_f )
        {
            cout << i << '\n';
        }
      }
      catch ( std::exception const &e )
      {
          std::cerr << "\nAn unexpected problem cause this application to end:\n\n"
                    << e.what() << ".\n\n";
          return EXIT_FAILURE;
      }
    }
    
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    类元素
    {
    公众:
    元素(){}
    元素(int-aa,int-bb,float-cc,float-dd):a{aa},b{bb},c{cc},d{dd}
    friend std::istream&operator>>(std::istream&in,Element&e);
    friend std::ostream&operator>(std::istream&in,Element&e)
    {
    字符分隔符;
    如果(不是)(在>>e.a>>中)分隔符和分隔符=='('和
    在>>e.b>>中,分隔符和分隔符==')'和
    在>>中,分隔符和分隔符==':'和
    在>>e.c>>中,分隔符和分隔符=='/'和
    在>>e.d>>中,分隔符和分隔符==';')
    而不是在.eof()中)
    {
    in.setstate(std::ios_base::failbit);
    }
    返回;
    }
    
    std::ostream&operator
    1*sizeof(char)
    保证为1。在表达式
    1*sizeof(char)
    中,值
    1
    是一个幻数,似乎是指
    sizeof(char)中的字符数
    似乎指的是
    dummy
    的类型。如果出于表达性或可维护性而选择不提供常量1,为什么不直接使用
    sizeof(dummy)
    ?当前表单并不比直接提供1更好。验证字符串的格式是否重要,或者是否知道它是正确的?如果已知它是有效的,您可以简单地在任何非数字字符序列上标记整个字符串。您将得到一个数字列表,从中可以很容易地构造
    Element
    。您是否想过序列化程序?读取数据结构是一个常见的问题,通常是一个常见的解决方案。感谢您对
    1*sizeof(char)的评论
    。还使
    parse
    函数返回
    std::vector
    。我将研究boost/tokenizer,看看能做什么。@Klaus,你能推荐一个特定的序列化程序吗?最好,序列化的数据应该是文本,而不是二进制的,即人类可读的。非常酷,非常感谢这个例子。这很好用,非常好ks相当完整!