使用 GetPrivateProfileString 读取所有 ini 文件值

Posted

技术标签:

【中文标题】使用 GetPrivateProfileString 读取所有 ini 文件值【英文标题】:Read all ini file values with GetPrivateProfileString 【发布时间】:2011-10-28 17:41:18 【问题描述】:

我需要一种方法来读取 StringBuilder 变量中 ini 文件的所有部分/键:

[DllImport("kernel32.dll")]
private static extern int GetPrivateProfileString(string lpAppName, string lpKeyName, string lpDefault, StringBuilder lpReturnedString, int nSize, string lpFileName);

...

private List<string> GetKeys(string iniFile, string category)

    StringBuilder returnString = new StringBuilder(255);            

    GetPrivateProfileString(category, null, null, returnString, 32768, iniFile);

    ...

在returnString中只是第一个键值!如何一次获取所有内容并将其写入 StringBuilder 和 List?

感谢您的帮助!

问候 leon22

【问题讨论】:

【参考方案1】:

为什么不使用 IniReader 库来读取 INI 文件?这样更容易,更快捷。

【讨论】:

谢谢!你有这个库的例子或链接吗? @zenwalker “因为他们依赖于 Windows API […]” - 该库使用 Win32 API 本身,因此速度不太可能更快。【参考方案2】:

我相信还有 GetPrivateProfileSection() 可以提供帮助,但我同意 Zenwalker 的观点,有些库可以帮助解决这个问题。 INI 文件并不难阅读:section、key/value 和 cmets 就差不多了。

【讨论】:

【参考方案3】:

可能的解决方案:

[DllImport("kernel32.dll")]
private static extern int GetPrivateProfileSection(string lpAppName, byte[] lpszReturnBuffer, int nSize, string lpFileName);

private List<string> GetKeys(string iniFile, string category)


    byte[] buffer = new byte[2048];

    GetPrivateProfileSection(category, buffer, 2048, iniFile);
    String[] tmp = Encoding.ASCII.GetString(buffer).Trim('\0').Split('\0');

    List<string> result = new List<string>();

    foreach (String entry in tmp)
    
        result.Add(entry.Substring(0, entry.IndexOf("=")));
    

    return result;

【讨论】:

不完全正确:不是整行,只返回键(例如,带有“=”的拆分是无用的,因为没有返回值)。此外,它可能会返回 unicode 字符串,因此使用 0 终止符的拆分无法正常工作并返回单个字符。 这并没有真正回答这个问题,它特别提到想要使用 StringBuilder 类,所以我们不必猜测最大缓冲区大小。【参考方案4】:

这些例程将读取整个 INI 部分,并将该部分作为原始字符串的集合返回,其中每个条目是 INI 文件中的单行(如果您使用的是 INI 结构但不一定有一个 =),另一个返回该部分中所有条目的键值对集合。

    [DllImport("kernel32.dll")]
    public static extern uint GetPrivateProfileSection(string lpAppName, IntPtr lpReturnedString, uint nSize, string lpFileName);

    // ReSharper disable once ReturnTypeCanBeEnumerable.Global
    public static string[] GetIniSectionRaw(string section, string file) 
        string[] iniLines;
        GetPrivateProfileSection(section, file, out iniLines);
        return iniLines;
    

    /// <summary> Return an entire INI section as a list of lines.  Blank lines are ignored and all spaces around the = are also removed. </summary>
    /// <param name="section">[Section]</param>
    /// <param name="file">INI File</param>
    /// <returns> List of lines </returns>
    public static IEnumerable<KeyValuePair<string, string>> GetIniSection(string section, string file) 
        var result = new List<KeyValuePair<string, string>>();
        string[] iniLines;
        if (GetPrivateProfileSection(section, file, out iniLines)) 
            foreach (var line in iniLines) 
                var m = Regex.Match(line, @"^([^=]+)\s*=\s*(.*)");
                result.Add(m.Success
                               ? new KeyValuePair<string, string>(m.Groups[1].Value, m.Groups[2].Value)
                               : new KeyValuePair<string, string>(line, ""));
            
        

        return result;
    

【讨论】:

此代码似乎不起作用。永远不会调用 GetIniSectionRaw 并且函数的参数不匹配。【参考方案5】:
Dim MyString    As String = String.Empty
Dim BufferSize As Integer = 1024 
Dim PtrToString As IntPtr = IntPtr.Zero
Dim RetVal As Integer
RetVal = GetPrivateProfileSection(SectionName, PtrToString, BufferSize, FileNameAndPah)

如果我们的函数调用成功,我们将在 PtrToString 内存地址中得到结果,而 RetVal 将包含 PtrToString 中字符串的长度。否则,如果此函数由于缺少足够的 BufferSize 而失败,则 RetVal 将包含 BufferSize - 2。因此我们可以检查它并使用更大的 BufferSize 再次调用此函数。

'现在,我们可以从内存地址中获取字符串。

MyString = Marshal.PtrToStringAuto(PtrToString, RetVal - 1)

'这里我使用“RetVal - 1”来避免多余的空字符串。

' 现在,我们需要拆分空字符所在的字符串。

Dim MyStrArray() As String = MyString.Split(vbNullChar)

因此,此数组包含该特定部分中的所有键值对。 并且不要忘记释放内存

Marshal.FreeHGlobal(PtrToString)

【讨论】:

很明显,这个api函数比原生文件读取函数慢。我用托管代码编写了一个函数,它以 3350 个刻度读取整个部分。使用这个非托管代码,读取相同的部分需要 10650 个滴答声。

以上是关于使用 GetPrivateProfileString 读取所有 ini 文件值的主要内容,如果未能解决你的问题,请参考以下文章

在使用加载数据流步骤的猪中,使用(使用 PigStorage)和不使用它有啥区别?

今目标使用教程 今目标任务使用篇

Qt静态编译时使用OpenSSL有三种方式(不使用,动态使用,静态使用,默认是动态使用)

MySQL db 在按日期排序时使用“使用位置;使用临时;使用文件排序”

使用“使用严格”作为“使用强”的备份

Kettle java脚本组件的使用说明(简单使用升级使用)