在C 中解析期间的象征性文本,新手的磨难

Tokenizing text during parsing in C++, a newbies ordeal

本文关键字:文本 象征性 新手      更新时间:2023-10-16

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

我要解析的文本文件包含我想忽略的双打,int和Whiteves。

我在Python方面有一定的经验,而Python将使用split()方法。

在C 中,我可以使用Boost令牌或许多其他集合工具。我尝试了许多此类方法,但是根据我的最佳尝试,我最终得到了这样的数字" -97.653632.431542",我什至不知道C 如何允许两个小数点。(我知道问题是我的经验不足,而不是C !)

所以现在就好东西了。首先,让我们定义文本文件的内容,其中包含可变数量的Whitespaces,因此我将在开始时放上几行以进行插图,并在开始时包含X数字(我知道我可以摆脱使用boost::trim_left(),但是为了示例完整性,我想在此处包括它们)。

注意:我可以使用我想要的任何库,但是如果可能使用C 常见的任何其他库的实用解决方案,因此我也可以学习如何以更实际的方式进行操作。

文本文件内容:

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

现在解析文件的代码:

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>

class myParserClass{
    public:
        std::string path;
        std::vector<int> NODE_INDEX;
        std::vector<double> X, Y, Z;
        std::string DESCRIPTION;
        int NE, NP;
        myParserClass(){};
        myParserClass(std::string path){
            parse_file(path)};
        void parse_fort14(std::string path){
            std::ifstream filei(path.c_str());
            std::string DESCRIPTION;
            getline(filei, DESCRITION);
            this->DESCRIPTION=DESCRIPTION;
            int NP, NE;
            stream >> NE >> NP;
            this->NE=NE;
            this->NP=NP;
            stream.str("");
            stream.clear();
            for(int x=0, x=NP, x++){
            // I'm having trouble here....
            this->NODE_INDEX=NODE_INDEX.push_back(node_index);
            this->X=X.push_back(x);
            this->Y=Y.push_back(y);
            this->Z=Z.push_back(z);};
        };
int main (){myParserClass myInfo("/path/to/myInfo.txt");}

编码样式评论也受到欢迎,因为我在C 上是如此新,并且一切都很粗糙。请注意,我已经从示例中脱掉了文件打开检查语句,而while {} eof语句为简单起见。

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

您已经拥有"典范C "方法。这是使用Boost Spirit Qi的演示:

活在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],
    

请参阅两个组合 live live live coliru (在其中仅读取6个点,因为 n_points是6)。

如果文件内容在每一行中具有相同的格式(int,double,double,double),则可以将流操作员使用:

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

和读取整个文件,一个简单的循环就足够了:

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,您应该从书或在线资源中学习。