在 C 中创建一个简单的配置文件和解析器

新手上路,请多包涵

我正在尝试创建一个看起来像这样的简单配置文件

url = http://mysite.com
file = main.exe
true = 0

当程序运行时,我希望它将配置设置加载到下面列出的程序变量中。

 string url, file;
bool true_false;

我做了一些研究, 这个 链接似乎有帮助(nucleon’s post),但我似乎无法让它工作,而且我理解起来太复杂了。有没有一种简单的方法可以做到这一点?我可以使用 ifstream 加载文件,但这是我自己能做到的。谢谢!

原文由 llk 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 667
2 个回答

一般来说,解析这样典型的配置文件最容易分两个阶段:首先读取行,然后逐行解析。

在 C++ 中,可以使用 std::getline() 从流中读取行。虽然默认情况下它会读取到下一个 '\n' (它会消耗,但不会返回),你也可以传递一些其他的分隔符,这使它成为读取最高 -一些字符,例如 = 在您的示例中。

为简单起见,以下假设 = 没有 被空格包围。如果您想在这些位置允许空格,则必须在读取值并从键中删除尾随空格之前战略性地放置 is >> std::ws 。但是,IMO 语法中增加的一点点灵活性对于配置文件阅读器来说是不值得的。

 const char config[] = "url=http://example.com\n"
                      "file=main.exe\n"
                      "true=0";

std::istringstream is_file(config);

std::string line;
while( std::getline(is_file, line) )
{
  std::istringstream is_line(line);
  std::string key;
  if( std::getline(is_line, key, '=') )
  {
    std::string value;
    if( std::getline(is_line, value) )
      store_line(key, value);
  }
}

(添加错误处理留给读者作为练习。)

原文由 sbi 发布,翻译遵循 CC BY-SA 3.0 许可协议

没有人提到 <regex> 。我更喜欢它们,因为它们非常易于阅读且不易出错。 MWE:

 #include <fstream>
#include <iostream>
#include <regex>
#include <string>

struct config_t
{
    // (define variables here)

    void read_from(const std::string& fname)
    {
        std::ifstream cfg_file(fname);
        if(!cfg_file.good())
            throw std::runtime_error("Cannot open file: " + fname);

        std::string line;
        while(std::getline(cfg_file, line))
        {
            std::regex re(R"XXX(^(\s*(\S+)\s*=\s*(\S+))?\s*(#.*)?$)XXX",
                std::regex::optimize);
            std::smatch match;
            if(std::regex_search(line, match, re))
            {
                if(match.length(2))
                {
                    std::string key = match.str(2),
                        value = match.str(3);
                    std::cout << "key-value-pair: " << key << " -> " << value << std::endl;
                    // (fill variables here)
                }
            }
            else
                throw std::runtime_error("Invalid line: " + line);
        }
    }
};

int main(int argc, char** argv)
{
    int rval = EXIT_SUCCESS;
    try
    {
        config_t cfg;
        if (argc != 2)
            throw std::runtime_error("Expecting exactly one argument");
        cfg.read_from(argv[1]);
    }
    catch (const std::exception& e)
    {
        std::cerr << "Error: " << e.what() << std::endl;
        rval = EXIT_FAILURE;
    }
    return rval;
}

原文由 Johannes 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
logo
Stack Overflow 翻译
子站问答
访问
宣传栏