使用GetPrivateProfileString读取所有ini文件值

16

我需要一种方法将ini文件中所有的章节/键读取到一个StringBuilder变量中:

[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);

    ...
}

返回的字符串中只有第一个键值!如何一次性获取全部并将其写入StringBuilder和List中?

感谢您的帮助!

致以问候,leon22

5个回答

26
可能的解决方案:
[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终止符进行分割无法正常工作并返回单个字符。 - Ray
这并没有真正回答问题,问题明确提到想要使用StringBuilder类,以便我们不必猜测最大缓冲区大小。 - Erik

2

我相信还有一个GetPrivateProfileSection()可以帮助,但我同意Zenwalker的观点,有一些库可以帮助解决这个问题。INI文件并不难读取:分区、键/值和注释基本上就是它。


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

谢谢!你有这个库的示例或链接吗? - leon22
@zenwalker:“由于它们依赖于Windows API[...]”-该库本身使用Win32 API,因此不太可能更快。 - poke

0
这些例程将读取整个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 从未被调用,而且函数的参数不匹配。 - etalon11

0
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中字符串的长度。否则,如果由于缓冲区大小不足而导致此函数失败,则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个时钟周期。 - Vinod KC

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接