2009-07-22 8 views
1

Ich habe ein Problem beim Lesen von IniFiles mit verschiedenen Codierungen. Wenn ich eine Unicode-Datei lese, scheint GetPrivateProfileSectionNamesA über die erste Zeile zu stolpern. ASCII oder ANSI funktioniert gut. Ich habe ein kleines Programm geschrieben, um mein Problem zu illustrieren. Zuerst die Ausgabe, dann das Programm. Ich interessiere mich nicht wirklich für UTF7 und UTF32, aber was ich nicht bekomme, ist der UTF8-Teil. Muss ich eine andere Funktion verwenden, um Unicode IniFiles zu lesen? Tue ich etwas falsch? Hoffnung jemand kann mir helfen, dank Norbertkernel32.dll - GetPrivateProfileSectionNamesA

, was ich bekommen:

IniEntriesWithSectionInFirstLine 
first section using System.Text.ASCIIEncoding is FirstSectionInFirstLine 
first section using System.Text.Latin1Encoding is FirstSectionInFirstLine 
first section using System.Text.UTF7Encoding is 
first section using System.Text.UTF8Encoding is SecondSection 
first section using System.Text.UTF32Encoding is SecondSectio???????????? 

IniEntriesWithFirstLineEmpty 
first section using System.Text.ASCIIEncoding is FirstSectionInSecondLine 
first section using System.Text.Latin1Encoding is FirstSectionInSecondLine 
first section using System.Text.UTF7Encoding is 
first section using System.Text.UTF8Encoding is FirstSectionInSecondLine 
first section using System.Text.UTF32Encoding is FirstSectionInSecondLin???????? 

das Programm:

using System; 
using System.Collections.Generic; 
using System.IO; 
using System.Runtime.InteropServices; 
using System.Text; 

namespace TestIniRead 
{ 
    internal class Program 
    { 
     [DllImport("kernel32.dll", EntryPoint = "GetPrivateProfileSectionNamesA")] 
     private static extern int GetSectionNamesListA(
      byte[] lpszReturnBuffer, 
      int nSize, 
      string lpFileName); 

     private static readonly string[] IniEntriesWithSectionInFirstLine = { 
                  "[FirstSectionInFirstLine]", 
                  "value=firsValue", 
                  "", 
                  "[SecondSection]", 
                  "value=secondValue", 
                  "", 
                  "[ThirdSection]", 
                  "value=secondValue", 
                  "" 
                 }; 
     private static readonly string[] IniEntriesWithFirstLineEmpty = { 
                  "", 
                  "[FirstSectionInSecondLine]", 
                  "value=firsValue", 
                  "", 
                  "[SecondSection]", 
                  "value=secondValue", 
                  "", 
                  "[ThirdSection]", 
                  "value=secondValue", 
                  "" 
                 }; 

     private static void Main() 
     { 
      var fileInfo = new FileInfo("test.ini"); 
      Console.WriteLine("IniEntriesWithSectionInFirstLine"); 
      TestEncodings(fileInfo, IniEntriesWithSectionInFirstLine); 
      Console.WriteLine(""); 
      Console.WriteLine("IniEntriesWithFirstLineEmpty"); 
      TestEncodings(fileInfo, IniEntriesWithFirstLineEmpty); 
      Console.ReadLine(); 
     } 

     private static void TestEncodings(FileInfo fileInfo, IEnumerable<string> iniEntries) 
     { 
      TestEncoding(fileInfo, iniEntries, Encoding.ASCII); 
      TestEncoding(fileInfo, iniEntries, Encoding.GetEncoding("ISO-8859-1")); 
      TestEncoding(fileInfo, iniEntries, Encoding.UTF7); 
      TestEncoding(fileInfo, iniEntries, Encoding.UTF8); 
      TestEncoding(fileInfo, iniEntries, Encoding.UTF32); 
     } 

     private static void TestEncoding(FileInfo fileInfo, IEnumerable<string> iniEntries, Encoding encoding) 
     { 
      CreateIniFile(fileInfo, iniEntries, encoding); 
      if (fileInfo.Exists) 
      { 
       var buffer = new byte[fileInfo.Length]; 
       GetSectionNamesListA(buffer, (int) fileInfo.Length, fileInfo.FullName); 
       String s = encoding.GetString(buffer); 
       String[] names = s.Split('\0'); 

       Console.WriteLine("first section using {0} is {1}", encoding, names[0]); 
      } 
     } 

     private static void CreateIniFile(FileSystemInfo fileInfo, IEnumerable<string> iniEntries, Encoding encoding) 
     { 
      using (var sw = new StreamWriter(File.Open(fileInfo.FullName, FileMode.Create), encoding)) 
      { 
       foreach (string line in iniEntries) 
       { 
        sw.WriteLine(line); 
       } 
      } 
     } 
    } 
} 

Reaktion auf die ersten drei Antworten:

Sie sind natürlich richtig. Ich sollte GetPrivateProfileSectionNamesW für Unicode-Dateien verwenden. Ich fügte eine Methode hinzu, um die Codierung der IniFile zu erhalten, und verwendete A oder W entsprechend. Das Problem bleibt gleich. Die Funktion erhält den ersten Abschnitt nicht. Unter sehen Sie neuen Code nur für UTF8.

, was ich bekommen:

IniEntriesWithSectionInFirstLine 
first section using System.Text.UTF8Encoding is SecondSection 

das Programm:

using System;                           
using System.Collections.Generic; 
using System.IO; 
using System.Runtime.InteropServices; 
using System.Text; 

namespace TestIniRead 
{ 
    internal class Program 
    { 
     [DllImport("kernel32.dll", EntryPoint = "GetPrivateProfileSectionNamesA")] 
     private static extern int GetSectionNamesListA(
       byte[] lpszReturnBuffer, 
       int nSize, 
       string lpFileName); 

     [DllImport("kernel32", EntryPoint = "GetPrivateProfileSectionNamesW", CharSet = CharSet.Unicode)] 
     private static extern int GetSectionNames 
      (
      [MarshalAs(UnmanagedType.LPWStr)] string szBuffer, 
      int nlen, 
      string filename 
      ); 

     private static readonly string[] IniEntriesWithSectionInFirstLine = { 
                   "[FirstSectionInFirstLine]", 
                   "value=firsValue", 
                   "", 
                   "[SecondSection]", 
                   "value=secondValue", 
                   "", 
                   "[ThirdSection]", 
                   "value=secondValue", 
                   "" 
                 }; 

     private static void Main() 
     { 
      var fileInfo = new FileInfo("test.ini"); 
      Console.WriteLine("IniEntriesWithSectionInFirstLine"); 
      TestEncodings(fileInfo, IniEntriesWithSectionInFirstLine); 
      Console.WriteLine(""); 
      Console.ReadLine(); 
     } 

     private static void TestEncodings(FileInfo fileInfo, IEnumerable<string> iniEntries) 
     { 
      TestEncoding(fileInfo, iniEntries, Encoding.UTF8); 
     } 

     private static readonly char[] separator = { '\0' }; 

     private static void TestEncoding(FileInfo fileInfo, IEnumerable<string> iniEntries, Encoding encoding) 
     { 
      CreateIniFile(fileInfo, iniEntries, encoding); 
      if (fileInfo.Exists) 
      { 
       int len = (int)fileInfo.Length; 
       var buffer = new string('\0', len); 
       int nlen = GetSectionNames(buffer, len, fileInfo.FullName); 
       if (nlen <= 0) 
       { 
        Environment.Exit(nlen); 
       } 

       String[] names = buffer.Substring(0, nlen).Split(separator); 
       Console.WriteLine("first section using {0} is {1}", encoding, names[0]); 
      } 
     } 

     private static void CreateIniFile 
      (
      FileSystemInfo fileInfo, 
      IEnumerable<string> iniEntries, 
      Encoding encoding) 
     { 
      using (var sw = new StreamWriter(File.Open(fileInfo.FullName, FileMode.Create), encoding)) 
      { 
       foreach (string line in iniEntries) 
       { 
        sw.WriteLine(line); 
       } 
      } 
     } 
    } 
} 

Antwort

1

Die ersten paar Bytes einer Unicode-Datei, um die Byte-Reihenfolge Markierungen enthalten. Der von Ihnen verwendete Texteditor speichert die Unicode-Datei und enthält Byte-Reihenfolgezeichen. Diese verwechseln dann die API-Funktion.

Haben Sie versucht, stattdessen GetPrivateProfileSectionNamesW aufzurufen? (Das A gibt die ANSI-Version einer API-Funktion an, das W für Wide gibt eine Unicode-Version an)

Oder Sie können Ihren Texteditor einfach so einstellen, dass die Datei ohne Byte-Reihenfolgezeichen gespeichert wird.

+0

Sie haben natürlich Recht. Ich sollte GetPrivateProfileSectionNamesW für Unicode-Dateien verwenden. Ich fügte eine Methode hinzu, um die Codierung der IniFile zu erhalten, und verwendete A oder W entsprechend. Das Problem bleibt gleich. Die Funktion erhält den ersten Abschnitt nicht. Siehe Änderungen oben. –

+0

Ich vermute immer noch, dass die API-Funktion Probleme beim Verarbeiten der Byte-Reihenfolgezeichen am Anfang der Datei hat. Versuchen Sie, Ihren Texteditor nicht einzuschließen. Sie können überprüfen, ob sie vorhanden sind, indem Sie die Datei in einem Hex-Editor öffnen. (Textpad kann Textdateien in Hex-Ansicht öffnen und Sie können steuern, was, wenn Byte-Reihenfolge Marken enthalten sind) – pipTheGeek

0
  1. Haben Sie versucht GetPrivateProfileSectionNamesW?
  2. Können Sie nur sicherstellen, dass die INI-Datei in ASCII gespeichert ist? Aus der MSDN-Dokumentation:

    Hinweis Diese Funktion ist nur für die Kompatibilität mit 16-Bit-Windows-basierten Anwendungen vorgesehen ist.

  3. Die .NET settings files sind erheblich besser als die INI-Dateien. Wenn Sie nicht mit älteren Systemen arbeiten, empfehle ich dringend, den neuen Weg zu nutzen.

+0

Sie haben natürlich recht. Ich sollte GetPrivateProfileSectionNamesW für Unicode-Dateien verwenden. Ich fügte eine Methode hinzu, um die Codierung der IniFile zu erhalten, und verwendete A oder W entsprechend. Das Problem bleibt gleich. Die Funktion erhält den ersten Abschnitt nicht. Ich muss IniFiles lesen, konnte nicht in XML ändern. Siehe Änderungen oben. –

0

Ich habe eigentlich das Gleiche gesehen, aber ohne die Prüfung zu tun Sie haben (ich gerade gemacht, dass eine leere Zeile am Anfang der INI-Datei haben).

Ich schrieb ursprünglich die Inifile mithilfe der IO-Funktionen in .NET Framework, und wenn ein anderes Programm in Oldmode C++ gelesen wurde, fehlte die erste Zeile. Ich habe mich am Ende verändert.NET-Code, um die ISO-8859-1-Codierung zu verwenden, die der grundlegenden Schreibweise von Textdateien am ehesten entspricht, bevor Unicode kam ... Die Standardcodierung in .NET ist UTF8. In vielen Fällen wäre Encodings.ASCII wahrscheinlich in Ordnung, aber das schließt nur die ersten 127 Zeichen ein.

In den meisten Fällen, denke ich, die Encodings.Default wäre gut zu verwenden, weil dies die Standard-Codepage auf der runnnenden Windows-Instanz verwendet, die in meinem Fall (und wahrscheinlich in Ihrem Fall) auf die ISO-8859 zugeordnet -1 Codierung. In anderen Teilen der Welt wird es anderen Teilmengen des ISO-8859-Standards zugeordnet.

+0

Ich konvertiere tatsächlich die IniFiles zu ISO-8859-1. Aber ich denke, ich sollte das nicht tun müssen. –