【问题标题】:Parse an INI file解析 INI 文件
【发布时间】:2012-05-07 15:12:32
【问题描述】:

我在 Visual Studio 2008 中使用 C++/CLI 构建程序,我一直在尝试将我的默认用户目录设置保存在 txt 文件中。到目前为止,我设法保存它,使其在 txt 文件中看起来像这样。

Input Directory=C:\Input
Output Directory=C:\Output

问题是,我的程序如何检测“输入目录=”这个词并且只接受它后面的词?

有没有办法在文本文件中搜索特定单词并在 C++/CLI 中替换它?所以我可以只搜索“C:\Input”并将其替换为“D:\Input”,例如。

【问题讨论】:

  • 它是一种INI文件吗?可以导入原生函数吗?
  • 不,它保存在我创建的txt文件中。
  • 是的,我的意思是如果语法类似于 INI 文件。
  • 一种简单的方法是忽略“输入目录=”并依赖于您从第一行读取的任何内容。否则使用 String::IndexOf("=") 和 SubString 很简单。

标签: .net visual-studio visual-studio-2008 c++-cli


【解决方案1】:

您可以通过多种方式读取文件。使用 c++\cli 的好处之一是您可以使用非常传统的 .net 接口。

所以你可以使用File::ReadAllLines 然后扫描每一行的字符串“输入目录”

【讨论】:

  • 例如,如果我使用 ReadAllLines 然后String::Contains 方法来搜索“输入目录”,但我应该使用什么方法来获取字符串“C:\Input”?
【解决方案2】:

我想它是一种类似 INI 的语法,但没有部分,要解析它,请执行以下操作。

在您将用于解析文件的类的构造函数(例如)中:

  • 创建一个Dictionary<string, string>,您将在那里保留解析的值(为键指定一个不区分大小写的比较器)。
  • 阅读文件的每一行,跳过空行(或注释行,如果你想支持的话)。
  • 在每个字符串行中搜索第一个“=”字符。左边部分是选项的名称,右边部分是值。
  • 将它们保存在字典中。

GetValue() 方法中这样做:

  • 在字典中搜索给定键并返回它。
  • 如果没有任何具有给定值的键,只需返回默认值。

这是一个简单 INI 解析器的可能实现(如果您不需要节名,只需传递一个空字符串即可)。

using namespace System;
using namespace System::Text;
using namespace System::IO;
using namespace System::Globalization;
using namespace System::Collections::Generic;

namespace Testy
{
    ref class IniParser
    {
    public:
        IniParser()
        {
            _values = gcnew Dictionary<String^, Dictionary<String^, String^>^>(
                StringComparer::InvariantCultureIgnoreCase);
        }

        IniParser(String^ path) 
        {
            _values = gcnew Dictionary<String^, Dictionary<String^, String^>^>(
                StringComparer::InvariantCultureIgnoreCase);

            Load(path);
        }

        void Load(String^ path)
        {
            String^ currentSection = "";
            for each (String^ line in File::ReadAllLines(path))
            {
                if (String::IsNullOrWhiteSpace(line))
                    continue;

                if (line->StartsWith(L";", StringComparison::InvariantCultureIgnoreCase))
                    continue;

                if (line->StartsWith(L"[", StringComparison::InvariantCultureIgnoreCase) && line->EndsWith(L"]", StringComparison::InvariantCultureIgnoreCase))
                {
                    String^ sectionName = line->Substring(1, line->Length - 2);
                    if (String::IsNullOrWhiteSpace(sectionName))
                        continue;

                    currentSection = sectionName;
                }

                array<String^>^ parts = line->Split(gcnew array<wchar_t>(2) { '=' }, 2);
                if (parts->Length == 1)
                    SetString(currentSection, parts[0]->Trim(), "");
                else
                    SetString(currentSection, parts[0]->Trim(), parts[1]->Trim());
            }
        }

        bool ContainsSection(String^ sectionName)
        {
            return _values->ContainsKey(sectionName);
        }

        bool ContainsValue(String^ sectionName, String^ valueName)
        {
            Dictionary<String^, String^>^ values = GetSection(sectionName);
            if (values == nullptr)
                return false;

            return values->ContainsKey(valueName);
        }

        void Clear()
        {
            _values->Clear();
        }

        void SetString(String^ sectionName, String^ valueName, String^ value)
        {
            Dictionary<String^, String^>^ values = GetSection(sectionName);
            if (values == nullptr)
            {
                values = gcnew Dictionary<String^, String^>(StringComparer::InvariantCultureIgnoreCase);
                _values->Add(sectionName, values);
            }

            if (values->ContainsKey(valueName))
                values[valueName] = value;
            else
                values->Add(valueName, value);
        }

        String^ GetString(String^ sectionName, String^ valueName, String^ defaultValue)
        {
            Dictionary<String^, String^>^ values = GetSection(sectionName);
            if (values == nullptr)
                return defaultValue;

            if (values->ContainsKey(valueName))
                return values[valueName];

            return defaultValue;
        }

    private:
        Dictionary<String^, Dictionary<String^, String^>^>^ _values;

        Dictionary<String^, String^>^ GetSection(String^ sectionName)
        {
            if (_values->ContainsKey(sectionName))
                return _values[sectionName];

            return nullptr;
        }
    };
}

您应该添加错误检查,并且可以省略不需要的内容(例如节和 cmets)。要“读取”不同的值类型,您可以实现更多 GetXYZ() 方法来将结果从 GetString() 解析为所需值(例如 float)。

另一种解决方案可能是...只需导入本机函数来解析 INI。他们工作得很好!

【讨论】:

  • 谢谢,但是你能举一个实现的例子吗?在步骤 3 中检测到“=”之后,我如何获取字符串的一部分?
  • 谢谢,但作为初学者,您发布的内容超出了我的范围。谢谢你的努力,我真的很感激。
【解决方案3】:

在为寻找简单的解决方案苦苦挣扎一整天后,我决定按照@Hans Passant 的建议省略“输入目录 =”,并通过将第一行作为输入读取并将第二行作为输出文件夹设置来做一个粗略的解决方法,使用类似于this的技术。

感谢所有回复的人。

【讨论】:

    猜你喜欢
    • 2012-11-18
    • 2019-03-09
    • 1970-01-01
    • 2014-09-03
    • 2015-10-12
    • 2021-01-06
    • 2018-08-30
    • 2012-09-24
    • 2010-09-29
    相关资源
    最近更新 更多