0

我正在使用 c++ 读取文件并通过键名获取键值。关键是这个文件中的键名可能会重复很多次。它们的结构如下。

数据文件格式

请记住,现在我对原始代码没有太多控制权,这就是这个数据文件的格式。

dictName
{
    keyA 9;
    keyB 3;
    keyC 5;

    subDictName
    {
        keyD 0.57;
        keyE 5.23;
    }
}

anotherDictName
{
    keyG 6;
    keyC 1;

    subDictName
    {
        keyF 0.17;
        keyE 2.21;
    }
}

我写了以下代码,但我发现它不够干净,有人知道在 C++ 中处理括号匹配的更好解决方案吗?

我写的代码

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

using namespace std;

int main()
{
    ifstream inf(fileName);
    istream_iterator<string> first(inf), last;
    vector<string> lines(first, last);

    for (unsigned i = 0; i < lines.size(); ++i)
    {
        if (size_t pos1 = lines[i].find(dictName) != string::npos)
        {
            size_t len1 = string(dictName).length();
            if (!isalnum(lines[i][pos1+len1+1]))
            {
                unsigned lineSta = 0;
                unsigned lineEnd = 0;
                for (unsigned j = i+1; j < lines.size(); ++j)
                {
                    if (lines[j].find("{") != string::npos)
                    {
                        lineSta = j+1;
                        break;
                    }
                }
                for (unsigned k = lineSta+1; k < lines.size(); ++k)
                {
                    if (lines[k].find("}") != string::npos)
                    {
                        lineEnd = k-1;
                        break;
                    }
                }
                for (unsigned l = lineSta; l <= lineEnd; ++l)
                {
                    if (size_t pos2 = lines[l].find(keyName) != string::npos)
                    {
                        size_t len2 = string(keyName).length();
                        if (!isalnum(lines[l][pos2+len2+1]))
                        {
                            outputStr = split(lines[l+1], ';')[0];
                            cout<< "outputStr = " << outputStr << endl;
                            break;
                        }
                    }
                }
            }
        }
    }

    return 0;
}
4

3 回答 3

1

由于这是一种现有格式,我将首先寻找一个现有的程序或库来读取它。否则,传统的解决方案将是使用 Bison 和 Flex。我确信也可以使用 Boost 和其他现代工具,但我更熟悉 Bison 和 Flex。

为了展示原理,这里有一个使用 Bison 和 Flex 的最小解决方案,可以解析您的示例输入文件。我可能误解了您输入的格式,因此请谨慎使用。

首先是 Bison 文件,它指定输入的语法:

%{
#include <iostream>
extern int yylex();
extern void yyerror(char* message);
%}

%token ID INT FLOAT

%%

dicts : dicts dict | /* empty */ ;
dict : ID '{' keys subdict '}' { std::cout << "Done a dict.\n"; } ;
keys : keys key | /* empty */ ;
key : ID INT ';' { std::cout << "Done a key.\n"; };
subdict : ID '{' subkeys '}' { std::cout << "Done a subdict.\n"; } ;
subkeys : subkeys subkey | /* empty */ ;
subkey : ID FLOAT ';' { std::cout << "Done a subkey.\n"; };

%%

void yyerror(char* message) {
    std::cout << "Error: " << message << "\n";
}

int main() {
    std::cout << "Staring parser...\n";
    yyparse();
    std::cout << "Parser done.\n";
    return 0;
}

然后是一个 Flex 文件,它定义了输入中各个标记的格式:

%{
#include "parser.tab.h"
%}

%%

[ \t\n]         { }
[0-9]+\.[0-9]+  { return FLOAT; }
[0-9]+          { return INT; }
[A-Za-z]+       { return ID; }
";"             { return ';'; }
"{"             { return '{'; }
"}"             { return '}'; }

%%

我为您的输入文件得到此输出:

Staring parser...
Done a key.
Done a key.
Done a key.
Done a subkey.
Done a subkey.
Done a subdict.
Done a dict.
Done a key.
Done a key.
Done a subkey.
Done a subkey.
Done a subdict.
Done a dict.
Parser done.
于 2013-04-09T08:22:33.353 回答
1

我不禁注意到,这与Boost::property_tree中的 INFO 解析器所支持的格式完全相同,但以分号结尾的值除外。以一种从键值中去除分号的方式包装它应该是微不足道的;无论如何,比自己编写输入/输出和处理函数要容易得多且不易出错。

于 2013-04-09T08:31:48.323 回答
0

您可能应该研究递归算法

这是一个 wiki 链接,它可能有助于递归下降解析器

实现起来比你的方法更复杂,但更干净、更强大

于 2013-04-08T17:32:23.963 回答