【问题标题】:C++ - Parsing a .txt-file with mulltiple delimiters, display stringC++ - 解析具有多个分隔符的 .text 文件,显示字符串
【发布时间】:2023-03-30 13:38:01
【问题描述】:

大家! 我是 C++ 新手,唉,我犯了愚蠢的错误。 这是 .txt 文件内容的 sn-p:

<tag attr1="value1" attr2="value2" ... >

我想要完成的是解析 .txt 文件,生成以下输出:

Tag: tag
name: attr1
value: value1
name: attr2
value: value2

到目前为止我所做的没有用(我的问题是分隔符):

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

using namespace std;

struct tagline{
string tag;
string attributeN;
string attributeV;

};

int main(){
vector<tagline> information;
string line;
tagline t;

ifstream readFile("file.txt");
    while(getline(readFile,line)){
    stringstream in(line);
    getline(in,t.tag);
    getline(in,t.attributeN,'=');
    getline(in,t.attributeV,'"');
    information.push_back(t);

}

vector<tagline>::iterator it = information.begin();

for(; it != information.end(); it++){
cout << "Tag: " << (*it).tag << " \n"
     << "name: " << (*it).attributeN << " \n"
     << "value: " << (*it).attributeV << " \n";

}
return 0;

}

我得到的只是 sn-p 的简单显示,因为它在 .txt 文件中格式化:

<tag attr1="value1" attr2="value2" ... >

如果有人可以提供帮助,我会很高兴。谢谢!

【问题讨论】:

  • 这是因为您在一条线上多次排队。您可能希望 getline 进入缓冲区,然后根据行索引将其分配给成员。更好的解决方案是重载operator&gt;&gt;
  • 我不太明白的是如何使用多个分隔符执行缓冲区方法。你介意发布一个代码示例吗?如果不是太麻烦的话。 :)
  • 是否可以选择使用 xml 解析器库?
  • 看来我误解了问题陈述。在这种情况下,我会灌输一个新的cctype。值是否包含空格?如果没有,这对cctype 来说是小菜一碟。
  • @Stephan Lechner 我还没有真正使用过解析器库(不过我确实知道一些 XML),所以我不知道如何立即实现它。

标签: c++ string parsing variables output


【解决方案1】:

使用 HTML/XML 解析器会更好地处理(取决于您的文件实际包含的内容)。

话虽如此,您没有正确解析这些行。

您对getline(in,t.tag); 的第一次调用没有指定分隔符,因此它会读取整行,而不仅仅是第一个单词。您必须改用getline(in, t.tag, ' ');

此外,您的标签可以有多个属性,但您只读取和存储第一个属性,而忽略其余的。您需要一个循环来读取所有这些内容,并需要一个 std::vector 来将它们全部存储到其中。

尝试类似的方法:

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

using namespace std;

struct tagattribute {
    string name;
    string value;
};

struct tagline {
    string tag;
    vector<tagattribute> attributes;
};

int main() {
    vector<tagline> information;
    string line;

    ifstream readFile("file.txt");
    while (getline(readFile, line)) {
        istringstream in(line);

        tagline t;
        tagattribute attr;

        in >> ws;

        char ch = in.get();
        if (ch != '<')
            continue;

        if (!(in >> t.tag))
            continue;

        do
        {
            in >> ws;

            ch = in.peek();
            if (ch == '>')
                break;

            if (getline(in, attr.name, '=') &&
                in.ignore() &&
                getline(in, attr.value, '"'))
            {
                t.attributes.push_back(attr);
            }
            else
                break;
        }
        while (true);

        information.push_back(t);
    }

    vector<tagline>::iterator it = information.begin();
    for(; it != information.end(); ++it) {
        cout << "Tag: " << it->tag << "\n";

        vector<tagattribute>::iterator it2 = it->attributes.begin();
        for(; it2 != it->attributes.end(); ++it2) {
            cout << "name: " << it2->name << "\n"
            << "value: " << it2->value << "\n";
        }

        cout << "\n";
    }

    return 0;
}

Live demo

或者,考虑编写一些自定义operator&gt;&gt; 来帮助解析,例如:

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

using namespace std;

struct tagattribute {
    string name;
    string value;
};

istream& operator>>(istream &in, tagattribute &attr)
{
    getline(in, attr.name, '=');
    in.ignore();
    getline(in, attr.value, '"');
    return in;
}

struct tagline {
    string tag;
    vector<tagattribute> attributes;
};

istream& operator>>(istream &in, tagline &t)
{
    tagattribute attr;

    in >> ws;

    char ch = in.get();
    if (ch != '<')
    {
        in.setstate(ios_base::failbit);
        return in;
    }

    if (!(in >> t.tag))
        return in;

    do
    {
        in >> ws;

        ch = in.peek();
        if (ch == '>')
        {
            in.ignore();
            break;
        }

        if (!(in >> attr))
            break;

        t.attributes.push_back(attr);
    }
    while (true);

    return in;
}

int main() {
    vector<tagline> information;
    string line;

    ifstream readFile("file.txt");
    while (getline(readFile, line)) {
        istringstream in(line);
        tagline t;     

        if (in >> t)
            information.push_back(t);
    }

    vector<tagline>::iterator it = information.begin();
    for(; it != information.end(); ++it) {
        cout << "Tag: " << it->tag << "\n";

        vector<tagattribute>::iterator it2 = it->attributes.begin();
        for(; it2 != it->attributes.end(); ++it2) {
            cout << "name: " << it2->name << "\n"
            << "value: " << it2->value << "\n";
        }

        cout << "\n";
    }

    return 0;
}

Live demo

【讨论】:

  • 哦,非常感谢! :) 我按照您建议的方式尝试了它,使用“getline(in, t.tag, ' ');”,但我得到了一个错误,尽管我认为它也与代码中其他地方的错误有关.非常感谢您的努力! :)
【解决方案2】:

好吧,我会尝试做这样的事情using this wonderful answer

struct xml_skipper : std::ctype<char> {
    xml_skipper() : ctype(make_table()) { }
private:
    static mask* make_table() {
        const mask* classic = classic_table();
        static std::vector<mask> v(classic, classic + table_size);
        v[','] |= space;
        v['"'] |= space;
        v['='] |= space;
        v['<'] |= space;
        v['>'] |= space;
        return &v[0];
    }
};

那么,你能做的就是继续阅读:

ifstream readFile("file.txt");
while(getline(readFile,line)){
    istringstream in(line);
    in.imbue(std::locale(in.getloc(), new xml_skipper));
    in >> t.tag >> t.attributeN >> t.attributeV;
    information.push_back(t);
}
//...

请注意,如果值或属性名称包含空格,则会中断。


如果你想要更严肃的东西,你将需要编写词法分析器、语法树构建器和语义树构建器。


完整代码

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

using namespace std;

struct tagline{
    string tag;
    string attributeN;
    string attributeV;
};

struct xml_skipper : std::ctype<char> {
    xml_skipper() : ctype(make_table()) { }
private:
    static mask* make_table() {
        const mask* classic = classic_table();
        static std::vector<mask> v(classic, classic + table_size);
        v[','] |= space;
        v['"'] |= space;
        v['='] |= space;
        v['<'] |= space;
        v['>'] |= space;
        return &v[0];
    }
};

int main(){
    vector<tagline> information;
    string line;
    tagline t;
    std::istringstream readFile{"<tag attr1=\"value1\" attr2=\"value2\" ... >"};
    while(getline(readFile,line)){
        istringstream in(line);
        in.imbue(std::locale(in.getloc(), new xml_skipper));
        in >> t.tag >> t.attributeN >> t.attributeV;
        information.push_back(t);
    }


    vector<tagline>::iterator it = information.begin();

    for(; it != information.end(); it++){
        cout << "Tag: " << (*it).tag << " \n"
             << "name: " << (*it).attributeN << " \n"
             << "value: " << (*it).attributeV << " \n";
    }
}

Live on Wandbox.

【讨论】:

  • 你是个魔术师! :D 它有效!抱歉花了这么长时间,我正在执行您在完整代码上方发布的代码 sn-ps,同时查找您使用的方法并卡在 YouTube 上。非常感谢你!感谢您教我 cctype 方法!
  • @mariechen,不客气。我将ctype 与C 的cctype 标头混淆了,所以这种方法基本上是新的语言环境方法(我不太确定它是如何调用的)。此外,另一个答案有一些重要的观点。你只需要第一个属性吗?
  • 我还需要显示其他的。但我会自己弄清楚。你已经帮了我很多了,谢谢! :)
【解决方案3】:

如果您的输入可能在 xml 规范的范围内有所不同,那么 XML 解析器可能是比“手动”解析字符串更好的方法。 只是为了展示它的外观,请参见以下代码。它基于tinyxml2,它只需要在您的项目中包含一个.cpp / .h-文件。当然,您也可以使用任何其他 xml 库;这只是为了演示目的:

#include <iostream>
#include "tinyxml2.h"
using namespace tinyxml2;

int main()
{
    const char* test = "<tag attr1='value1' attr2 = \"value2\"/>";
    XMLDocument doc;
    doc.Parse(test);
    XMLElement *root = doc.RootElement();
    if (root) {
        cout << "Tag: " << root->Name() << endl;
        const XMLAttribute *attrib = root->FirstAttribute();
        while (attrib) {
            cout << "name: " << attrib->Name() << endl;
            cout << "value : " << attrib->Value() << endl;
            attrib = attrib->Next();
        }
    }
}

【讨论】:

  • 感谢您的建议! :) 所以它基本上就像在 JavaScript 中使用 jQuery 以减少代码量并使事情更简单,我猜?这太酷了!就像我说的,我是 C++ 的新手,所以我还没有真正做到这一点。但学习新东西总是有帮助的。非常感谢! :D
猜你喜欢
  • 2015-03-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-31
相关资源
最近更新 更多