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

Parse string of numbers into vector of structs

本文关键字:结构 向量 分解 数字      更新时间:2023-10-16

我想将一串数字串分解为元素向量。字符串由四个数字组成,由( ) : /隔开,每个块由;分开。

具体来说,字符串以这种格式: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并按数字读取并"切碎"字符之间?如代码样本中所做的吗?
  2. 此代码有一个错误,并试图读取一组额外的值,因为while(iss)仍然是正确的,在读取了最后一个字符之后。如何在每个iss>>之后终止此循环而不进行测试?或更一般地,如何从istringstream提取?
  3. 上循环

您的数据结构良好,您可以轻松地超载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:nn"
                << e.what() << ".nn";
      return EXIT_FAILURE;
  }
}