2009-07-22 9 views
1

J'ai un problème lors de la lecture d'IniFiles avec différents encodages. Si je lis un fichier Unicode, GetPrivateProfileSectionNamesA semble trébucher sur la première ligne. ASCII ou ANSI fonctionne bien. J'ai écrit un petit programme pour illustrer mon problème. D'abord la sortie, puis le programme. Je ne me soucie pas vraiment de UTF7 et UTF32, mais ce que je ne comprends pas, c'est la partie UTF8. Dois-je utiliser une fonction différente pour lire Unicode IniFiles? Est-ce que je fais quelque chose de mal? quelqu'un L'espoir peut me aider, grâce Norbertkernel32.dll - GetPrivateProfileSectionNamesA

ce que je reçois:

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???????? 

programme:

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

Réaction aux trois premières réponses:

Vous sont bien sûr bien. Je devrais utiliser GetPrivateProfileSectionNamesW pour les fichiers Unicode. J'ai inclus une méthode pour obtenir l'encodage de l'IniFile et utilisé A ou W en conséquence. Le problème reste le même. La fonction n'obtient pas la première section. Ci-dessous, voir le nouveau code uniquement pour UTF8.

ce que je reçois:

IniEntriesWithSectionInFirstLine 
first section using System.Text.UTF8Encoding is SecondSection 

programme:

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

Répondre

1

Les premiers octets d'un fichier unicode peut contenir les marques d'ordre d'octets. Quel que soit l'éditeur de texte que vous utilisez, vous enregistrez le fichier Unicode et incluez les marques d'ordre des octets. Ceux-ci confondent alors la fonction d'API.

Avez-vous essayé d'appeler GetPrivateProfileSectionNamesW à la place? (Le A indique la version ANSI d'une fonctionnalité API, le W pour large indique une version Unicode)

Ou vous pouvez simplement définir votre éditeur de texte pour enregistrer le fichier sans les marques de commande d'octet.

+0

Vous avez bien sûr raison. Je devrais utiliser GetPrivateProfileSectionNamesW pour les fichiers Unicode. J'ai inclus une méthode pour obtenir l'encodage de l'IniFile et utilisé A ou W en conséquence. Le problème reste le même. La fonction n'obtient pas la première section. Voir les changements ci-dessus. –

+0

Je suspecte toujours que la fonction API rencontre des problèmes pour traiter les marques d'ordre des octets au début du fichier. Essayez de ne pas en inclure dans votre éditeur de texte. Vous pouvez vérifier s'ils sont présents en ouvrant le fichier dans un éditeur hexadécimal. (Textpad peut ouvrir des fichiers texte en mode hexadécimal et vous permet de contrôler si des marques d'ordre d'octet sont incluses) – pipTheGeek

0
  1. Avez-vous essayé GetPrivateProfileSectionNamesW?
  2. Pouvez-vous simplement vous assurer que le fichier ini est stocké en ASCII? De la documentation MSDN:

    Remarque Cette fonction est fournie uniquement pour la compatibilité avec les applications Windows 16 bits.

  3. Les .NET settings files sont largement supérieurs aux fichiers INI. Si vous n'écrivez pas quelque chose pour interopérer avec les systèmes existants, je recommande fortement d'utiliser la nouvelle méthode.

+0

Vous avez bien sûr raison. Je devrais utiliser GetPrivateProfileSectionNamesW pour les fichiers Unicode. J'ai inclus une méthode pour obtenir l'encodage de l'IniFile et utilisé A ou W en conséquence. Le problème reste le même. La fonction n'obtient pas la première section. Je dois lire IniFiles, ne pouvait pas changer en XML. Voir les changements ci-dessus. –

0

J'ai réellement vu la même chose, mais sans faire les tests que vous avez (je viens de faire sûr d'avoir une ligne vide au début du fichier ini). À l'origine, j'écrivais l'inifile en utilisant les fonctions IO dans le framework .NET, et quand un autre programme écrit en oldfashion C++ le lisait, la première ligne manquait. J'ai fini par changer mon.Code NET pour utiliser le codage ISO-8859-1, qui est probablement le plus proche de l'écriture de base de texte avant l'arrivée de l'Unicode ... L'encodage par défaut dans .NET est UTF8. Dans de nombreux cas, Encodings.ASCII serait probablement OK, mais cela inclut seulement les 127 premiers caractères.

Dans la plupart des cas, je pense que Encodings.Default serait bon à utiliser, car cela représente la page de code par défaut utilisée sur l'instance de Windows exécutable, qui dans mon cas (et probablement dans votre cas) mappe l'ISO-8859 -1 encodage. Dans d'autres parties du monde, il sera mappé à d'autres sous-ensembles de la norme ISO-8859.

+0

Je convertis réellement les IniFiles en ISO-8859-1. Mais je pense que je ne devrais pas avoir à faire ça. –