将数字串解析为结构的向量

时间:2017-05-03 18:13:23

标签: 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 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;
}

我的问题:

  1. 什么是解析的好方法?我应该使用std::stringstream并按号码读取数字并且&#39;砍掉&#39;两者之间的人物?正如在代码示例中所做的那样?
  2. 此代码有一个错误并尝试读取一组额外的值,因为在读入最后一个字符后while(iss)仍然为真。如何在每个{{1}之后终止此循环而不进行测试}?或者更一般地说,如何从istringstream循环提取?

1 个答案:

答案 0 :(得分:1)

您的数据结构合理,您可以轻松地重载operator>>以从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;
  }
}