在C ++中解析期间对文本进行标记,这是一个新手的考验

时间:2017-09-02 15:31:42

标签: c++ parsing text boost

我有一个文本文件,其中包含一些我需要解析并放入适当数组结构的信息(我猜这些是C ++中的向量)。

我试图解析的文本文件包含我想忽略的双精度,整数和空格。

我有使用Python的经验,使用Python可以使用split()方法。

在C ++中,我可以使用boost tokenizer或许多其他工具集。我已经尝试了很多这些方法,但在我最好的尝试中,我最终得到一个像这样的数字“-97.653632.431542”,我甚至不知道C ++如何允许两个小数点。 (我知道问题是我缺乏经验,而不是C ++!)

所以现在就好了。首先让我们定义文本文件的内容,其中包含可变数量的空格,所以我会先插入几行代码,开头包含X个空格,(我知道我可以使用{{1但是,为了示例完整性,我想在这里包含它们)。

注意:我可以使用我想要的任何库,但是如果可能的话,我希望看到一个规范的解决方案(所以我可以学习规范的C ++方式)以及更多使用C ++中常见的任何其他库的实用解决方案,因此我也可以学习如何以更实际的方式完成它。

文本文件内容:

boost::trim_left()

现在解析文件的代码:

FILE_DESCRIPTION
523459 45267393
         1  -91.1960210000   30.4248000000    6.9067078000
         2  -91.1936990000   30.4238730000    0.2607690100
         3  -91.1983420000   30.4257270000   11.4345030000
         4  -91.2006640000   30.4266540000    8.2591810000
         5  -91.2029850000   30.4275810000    2.2204340000
         6  -91.2043510000   30.4258950000    3.0012660000
         7  -91.1962610000   30.4231880000   13.4529710000
         8  -91.1941710000   30.4215120000    7.8915730000

编码风格的评论也很受欢迎,因为我是C ++的新手,对我来说,一切都是粗糙的。请注意,为了简单起见,我已从示例中删除了文件打开检查语句和while {} EOF语句。

编辑1: 所以看起来之前发生的事情是相关的。我编辑了上面的问题,以反映同一问题的更准确版本。

2 个答案:

答案 0 :(得分:3)

您已经拥有“规范C ++”方法。这是一个使用Boost Spirit Qi的演示:

<强> Live On Coliru

#include <fstream>
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/qi.hpp>

struct Fort14 {
    std::string description;
    int n_edges, n_points;

    struct Point { double x,y,z; };
    std::vector<Point> points;
};

BOOST_FUSION_ADAPT_STRUCT(Fort14::Point, x, y, z)
BOOST_FUSION_ADAPT_STRUCT(Fort14, description, n_edges, n_points, points)

Fort14 parse_fort14(std::istream& is) {
    using It = boost::spirit::istream_iterator;
    using namespace boost::spirit::qi;

    rule<It, std::string()> description = *(char_ - eol);
    It f(is >> std::noskipws), l;

    Fort14 data;
    if (phrase_parse(f, l, 
                description >> eol >>
                int_ >> int_ >> eol >>        // NE, NP
                (omit[int_] >> auto_) % eol,  // point data
            blank, data))
    {
        return data;
    }

    auto frag = f;
    for (int i = 10; i>0 && frag!=l; --i)
        ++frag;

    throw std::runtime_error("Parse error at " + std::string(f, frag) + "...");
}

int main() {
    std::ifstream ifs("input.txt");
    auto parsed = parse_fort14(ifs);

    std::cout << "Description: '" << parsed.description << "'\n";
    std::cout << "n_edges: "      << parsed.n_edges     << "\n";
    std::cout << "n_points: "     << parsed.n_points    << "\n";
    for (auto& p : parsed.points)
        std::cout << " - point { " << p.x << ", " << p.y << ", " << p.z << " }\n";
}

打印

Description: 'FILE_DESCRIPTION'
n_edges: 523459
n_points: 45267393
 - point { -91.196, 30.4248, 6.90671 }
 - point { -91.1937, 30.4239, 0.260769 }
 - point { -91.1983, 30.4257, 11.4345 }
 - point { -91.2007, 30.4267, 8.25918 }
 - point { -91.203, 30.4276, 2.22043 }
 - point { -91.2044, 30.4259, 3.00127 }
 - point { -91.1963, 30.4232, 13.453 }
 - point { -91.1942, 30.4215, 7.89157 }

我做了一些假设,但没有太多(你没有解释任何输入格式......)。 NE / NP可能是“边数,点数”的缩写(没有线索,真的)。

请注意

  1. 如果要验证点的索引是否依次增加,您可以:

    (omit[int_(boost::phoenix::ref(counter)++)] >> auto_) % eol,
    
  2. 如果你想接受 n 点(例如n_points或你的'NP'),你会写

    repeat(n_points) [omit[int_] >> auto_ >> eol],
    
  3. 同时查看合并 Live On Coliru (其中只读取输入中的6个点,因为n_points为6)。

答案 1 :(得分:2)

如果文件内容在每一行中具有相同的格式(int,double,double,double),则可以使用stream运算符,如下所示:

int no;
double d1, d2, d3;
filei >> no >> d1 >> d2 >> d3;

要读取整个文件,只需一个简单的while循环即可:

int main()
{
    std::ifstream filei("/path/to/myInfo.txt");
    int no;
    double d1, d2, d3;
    while (filei >> no >> d1 >> d2 >> d3) {
        // do something with data
    }
}

不需要花哨的解析。

要将行数据组合在一起,您可以使用结构代替并使用这些结构的向量:

struct row {
    int no;
    double x, y, z;
};

std::vector<row> rows;
// ...
row r;
while (filei >> r.no >> r.d1 >> r.d2 >> r.d3) {
    rows.push_back(r);
}

下一步可能是为行结构实现流操作符:

std::istream &operator>>(std::istream &f, row &r)
{
    return f >> r.no >> r.d1 >> r.d2 >> r.d3;
}

并在循环中使用它:

row r;
while (filei >> r) {
    rows.push_back(r);
}

当您想要读取给定数量的行时,例如NP行:

for (int i = 0; i < NP; ++i) {
    // read and process line
}

虽然这是C ++ 101,你应该从书本或在线资源中学到它。