我需要一种方法来读取 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?
谢谢您的帮助!
问候莱昂22
可能的解决方案:
[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;
}
我相信还有
GetPrivateProfileSection()
可以提供帮助,但我同意 Zenwalker 的观点,有一些库可以帮助解决这个问题。 INI 文件并不难读:部分、键/值和注释就差不多了。
为什么不使用 IniReader 库来读取 INI 文件?这样更容易、更快。
这些例程将读取整个 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;
}
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)
我也需要一种方法来简单地从跨平台(Linux、Mac、Windows)的 ini 文件中读取值(不依赖于
Kernel32
和 GetPrivateProfileSection
)。
我最终使用了本文中的一些代码,但我将其简化了很多,并使其从 .NET Core (8.x) 控制台应用程序运行。
我还添加了一个
test.ini
文件,以便您可以下载并运行它并快速查看结果。
您可以获取一个源文件
IniFileReader.cs
并将其包含在任何项目中,以便您可以轻松地从 ini 文件中读取值。
这里是源代码的快速浏览,您可以在我的 github 存储库中获取:
只需传入 ini 文件(完整路径或相对路径)并新建一个 IniFileReader,它将解析整个文件,以便您可以获取任何值或迭代它们。
class IniFileReader
{
private string FileName {get;set;}
private Dictionary<string, Dictionary<string, string>> m_Sections = new Dictionary<string, Dictionary<string, string>>();
public IniFileReader(string fileName)
{
FileName = fileName;
ParseFile();
}
public void DisplayAllSections(){
foreach (string sectionKey in m_Sections.Keys){
Console.WriteLine($"[{sectionKey}]");
Dictionary<string,string> keyValuePairs = null;
m_Sections.TryGetValue(sectionKey, out keyValuePairs);
Console.WriteLine($"Values in section: {keyValuePairs.Count}");
foreach (string k in keyValuePairs.Keys){
string value = null;
keyValuePairs.TryGetValue(k,out value);
Console.WriteLine($"{k} : {value}");
}
Console.WriteLine();
}
}
private string ParseSectionName(string Line)
{
if (!Line.StartsWith("[")) return null;
if (!Line.EndsWith("]")) return null;
if (Line.Length < 3) return null;
return Line.Substring(1, Line.Length - 2);
}
private bool ParseKeyValuePair(string Line, ref string Key, ref string Value)
{
int i;
if ((i = Line.IndexOf('=')) <= 0) return false;
int j = Line.Length - i - 1;
Key = Line.Substring(0, i).Trim();
if (Key.Length <= 0) return false;
Value = (j > 0) ? (Line.Substring(i + 1, j).Trim()) : ("");
return true;
}
public string GetValue(string SectionName, string Key, string DefaultValue="")
{
// *** Check if the section exists ***
Dictionary<string, string> Section;
if (!m_Sections.TryGetValue(SectionName, out Section)) return DefaultValue;
// *** Check if the key exists ***
string Value;
if (!Section.TryGetValue(Key, out Value)) return DefaultValue;
// *** Return the found value ***
return Value;
}
public void ParseFile(){
StreamReader sr = null;
try
{
// *** Clear local cache ***
m_Sections.Clear();
// *** Open the INI file ***
try
{
sr = new StreamReader(FileName);
}
catch (FileNotFoundException)
{
return;
}
Dictionary<string, string> CurrentSection = null;
string s;
string SectionName;
string Key = null;
string Value = null;
while ((s = sr.ReadLine()) != null)
{
s = s.Trim();
SectionName = ParseSectionName(s);
if (SectionName != null)
{
// *** Only first occurrence of a section is loaded - duplicates ignored***
if (m_Sections.ContainsKey(SectionName))
{
CurrentSection = null;
}
else
{
CurrentSection = new Dictionary<string, string>();
m_Sections.Add(SectionName, CurrentSection);
}
}
else if (CurrentSection != null)
{
// *** Check for key+value pair ***
if (ParseKeyValuePair(s, ref Key, ref Value))
{
// *** Only first occurrence of a key is loaded - duplicates ignored ***
if (!CurrentSection.ContainsKey(Key))
{
CurrentSection.Add(Key, Value);
}
}
}
}
}
finally
{
if (sr != null) sr.Close();
sr = null;
}
}
}