web-dev-qa-db-fra.com

Moyen efficace de trouver l'encodage de n'importe quel fichier

Oui est une question très fréquente, et cette question est vague pour moi et comme je ne sais pas grand chose à ce sujet.

Mais je voudrais un moyen très précis de trouver un encodage de fichiers. Si précis que le Notepad ++ l’est.

87
Fábio Antunes

Le StreamReader.CurrentEncoding _ propriété retourne rarement le codage de fichier texte correct pour moi. J'ai eu plus de succès à déterminer la finalité d'un fichier en analysant sa marque d'ordre des octets (BOM):

/// <summary>
/// Determines a text file's encoding by analyzing its byte order mark (BOM).
/// Defaults to ASCII when detection of the text file's endianness fails.
/// </summary>
/// <param name="filename">The text file to analyze.</param>
/// <returns>The detected encoding.</returns>
public static Encoding GetEncoding(string filename)
{
    // Read the BOM
    var bom = new byte[4];
    using (var file = new FileStream(filename, FileMode.Open, FileAccess.Read))
    {
        file.Read(bom, 0, 4);
    }

    // Analyze the BOM
    if (bom[0] == 0x2b && bom[1] == 0x2f && bom[2] == 0x76) return Encoding.UTF7;
    if (bom[0] == 0xef && bom[1] == 0xbb && bom[2] == 0xbf) return Encoding.UTF8;
    if (bom[0] == 0xff && bom[1] == 0xfe) return Encoding.Unicode; //UTF-16LE
    if (bom[0] == 0xfe && bom[1] == 0xff) return Encoding.BigEndianUnicode; //UTF-16BE
    if (bom[0] == 0 && bom[1] == 0 && bom[2] == 0xfe && bom[3] == 0xff) return Encoding.UTF32;
    return Encoding.ASCII;
}

En guise de remarque, vous pouvez modifier la dernière ligne de cette méthode pour renvoyer Encoding.Default à la place, le codage de la page de codes ANSI actuelle du système d'exploitation est renvoyé par défaut.

136
2Toad

Le code suivant fonctionne bien pour moi, en utilisant la classe StreamReader:

  using (var reader = new StreamReader(fileName, defaultEncodingIfNoBom, true))
  {
      reader.Peek(); // you need this!
      var encoding = reader.CurrentEncoding;
  }

L'astuce consiste à utiliser l'appel Peek, sinon .NET n'a rien fait (et il n'a pas lu le préambule, la nomenclature). Bien sûr, si vous utilisez un autre appel ReadXXX avant de vérifier le codage, cela fonctionne également.

Si le fichier n'a pas de nomenclature, le codage defaultEncodingIfNoBom sera utilisé. Il existe également un StreamReader sans cette méthode de surcharge (dans ce cas, le codage par défaut (ANSI) sera utilisé comme defaultEncodingIfNoBom), mais je vous recommande de définir ce que vous considérez comme le codage par défaut dans votre contexte.

J'ai testé cela avec succès avec des fichiers de nomenclature pour UTF8, UTF16/Unicode (LE & BE) et UTF32 (LE & BE). Cela ne fonctionne pas pour UTF7.

40
Simon Mourier

Je voudrais essayer les étapes suivantes:

1) Vérifiez s'il y a une marque d'ordre d'octet

2) Vérifiez si le fichier est valide UTF8

3) Utiliser la page de code "ANSI" locale (ANSI telle que définie par Microsoft)

L'étape 2 fonctionne parce que la plupart des séquences non ASCII dans les pages de codes autres que UTF8 ne sont pas valides UTF8.

11
CodesInChaos

Vérifiez ça.

UDE

C'est un port de Mozilla Universal Charset Detector et vous pouvez l'utiliser comme ça ...

public static void Main(String[] args)
{
    string filename = args[0];
    using (FileStream fs = File.OpenRead(filename)) {
        Ude.CharsetDetector cdet = new Ude.CharsetDetector();
        cdet.Feed(fs);
        cdet.DataEnd();
        if (cdet.Charset != null) {
            Console.WriteLine("Charset: {0}, confidence: {1}", 
                 cdet.Charset, cdet.Confidence);
        } else {
            Console.WriteLine("Detection failed.");
        }
    }
}
7

Fournir les détails de la mise en œuvre pour les étapes proposées par @CodesInChaos:

1) Vérifiez s'il y a une marque d'ordre d'octet

2) Vérifiez si le fichier est valide UTF8

3) Utiliser la page de code "ANSI" locale (ANSI telle que définie par Microsoft)

L'étape 2 fonctionne car la plupart des séquences non ASCII dans les pages de codes autres que UTF8 ne sont pas valides UTF8. https://stackoverflow.com/a/4522251/867248 explique la tactique de manière plus détaillée.

using System; using System.IO; using System.Text;

// Using encoding from BOM or UTF8 if no BOM found,
// check if the file is valid, by reading all lines
// If decoding fails, use the local "ANSI" codepage

public string DetectFileEncoding(Stream fileStream)
{
    var Utf8EncodingVerifier = Encoding.GetEncoding("utf-8", new EncoderExceptionFallback(), new DecoderExceptionFallback());
    using (var reader = new StreamReader(fileStream, Utf8EncodingVerifier,
           detectEncodingFromByteOrderMarks: true, leaveOpen: true, bufferSize: 1024))
    {
        string detectedEncoding;
        try
        {
            while (!reader.EndOfStream)
            {
                var line = reader.ReadLine();
            }
            detectedEncoding = reader.CurrentEncoding.BodyName;
        }
        catch (Exception e)
        {
            // Failed to decode the file using the BOM/UT8. 
            // Assume it's local ANSI
            detectedEncoding = "ISO-8859-1";
        }
        // Rewind the stream
        fileStream.Seek(0, SeekOrigin.Begin);
        return detectedEncoding;
   }
}


[Test]
public void Test1()
{
    Stream fs = File.OpenRead(@".\TestData\TextFile_ansi.csv");
    var detectedEncoding = DetectFileEncoding(fs);

    using (var reader = new StreamReader(fs, Encoding.GetEncoding(detectedEncoding)))
    {
       // Consume your file
        var line = reader.ReadLine();
        ...
2
Berthier Lemieux

Les codes suivants sont les codes Powershell permettant de déterminer si certains fichiers cpp, h ou ml sont codés avec ISO-8859-1 (Latin-1) ou UTF-8 sans nomenclature, si ni l’un ni l’autre ne suppose qu’il s’agit de GB18030. Je suis un Chinois qui travaille en France et MSVC enregistre en tant que Latin-1 sur un ordinateur français et en GB sur un ordinateur chinois, ce qui me permet d'éviter les problèmes d'encodage lors des échanges de fichiers source entre mon système et mes collègues.

Le chemin est simple, si tous les caractères sont entre x00-x7E, ASCII, UTF-8 et Latin-1 sont tous identiques, mais si je lis un fichier non ASCII par UTF-8, nous allons trouver le caractère spécial �, alors essayez de lire avec Latin 1. En Latin-1, entre\x7F et\xAF est vide, alors que GB utilise full entre x00-xFF, donc si j'en ai un entre les deux, ce n'est pas latin-1

Le code est écrit en PowerShell, mais utilise .net, il est donc facile de le traduire en C # ou en F #.

$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding($False)
foreach($i in Get-ChildItem .\ -Recurse -include *.cpp,*.h, *.ml) {
    $openUTF = New-Object System.IO.StreamReader -ArgumentList ($i, [Text.Encoding]::UTF8)
    $contentUTF = $openUTF.ReadToEnd()
    [regex]$regex = '�'
    $c=$regex.Matches($contentUTF).count
    $openUTF.Close()
    if ($c -ne 0) {
        $openLatin1 = New-Object System.IO.StreamReader -ArgumentList ($i, [Text.Encoding]::GetEncoding('ISO-8859-1'))
        $contentLatin1 = $openLatin1.ReadToEnd()
        $openLatin1.Close()
        [regex]$regex = '[\x7F-\xAF]'
        $c=$regex.Matches($contentLatin1).count
        if ($c -eq 0) {
            [System.IO.File]::WriteAllLines($i, $contentLatin1, $Utf8NoBomEncoding)
            $i.FullName
        } 
        else {
            $openGB = New-Object System.IO.StreamReader -ArgumentList ($i, [Text.Encoding]::GetEncoding('GB18030'))
            $contentGB = $openGB.ReadToEnd()
            $openGB.Close()
            [System.IO.File]::WriteAllLines($i, $contentGB, $Utf8NoBomEncoding)
            $i.FullName
        }
    }
}
Write-Host -NoNewLine 'Press any key to continue...';
$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown');
2
Enzojz

Regardez ici pour c #

https://msdn.Microsoft.com/en-us/library/system.io.streamreader.currentencoding%28v=vs.110%29.aspx

string path = @"path\to\your\file.ext";

using (StreamReader sr = new StreamReader(path, true))
{
    while (sr.Peek() >= 0)
    {
        Console.Write((char)sr.Read());
    }

    //Test for the encoding after reading, or at least
    //after the first read.
    Console.WriteLine("The encoding used was {0}.", sr.CurrentEncoding);
    Console.ReadLine();
    Console.WriteLine();
}
1
Sedrick

.NET n'est pas très utile, mais vous pouvez essayer l'algorithme suivant:

  1. essayez de trouver le codage par nomenclature (BOM) ... très probablement introuvable
  2. essayez d'analyser dans différents encodages

Voici l'appel:

var encoding = FileHelper.GetEncoding(filePath);
if (encoding == null)
    throw new Exception("The file encoding is not supported. Please choose one of the following encodings: UTF8/UTF7/iso-8859-1");

Voici le code:

public class FileHelper
{
    /// <summary>
    /// Determines a text file's encoding by analyzing its byte order mark (BOM) and if not found try parsing into diferent encodings       
    /// Defaults to UTF8 when detection of the text file's endianness fails.
    /// </summary>
    /// <param name="filename">The text file to analyze.</param>
    /// <returns>The detected encoding or null.</returns>
    public static Encoding GetEncoding(string filename)
    {
        var encodingByBOM = GetEncodingByBOM(filename);
        if (encodingByBOM != null)
            return encodingByBOM;

        // BOM not found :(, so try to parse characters into several encodings
        var encodingByParsingUTF8 = GetEncodingByParsing(filename, Encoding.UTF8);
        if (encodingByParsingUTF8 != null)
            return encodingByParsingUTF8;

        var encodingByParsingLatin1 = GetEncodingByParsing(filename, Encoding.GetEncoding("iso-8859-1"));
        if (encodingByParsingLatin1 != null)
            return encodingByParsingLatin1;

        var encodingByParsingUTF7 = GetEncodingByParsing(filename, Encoding.UTF7);
        if (encodingByParsingUTF7 != null)
            return encodingByParsingUTF7;

        return null;   // no encoding found
    }

    /// <summary>
    /// Determines a text file's encoding by analyzing its byte order mark (BOM)  
    /// </summary>
    /// <param name="filename">The text file to analyze.</param>
    /// <returns>The detected encoding.</returns>
    private static Encoding GetEncodingByBOM(string filename)
    {
        // Read the BOM
        var byteOrderMark = new byte[4];
        using (var file = new FileStream(filename, FileMode.Open, FileAccess.Read))
        {
            file.Read(byteOrderMark, 0, 4);
        }

        // Analyze the BOM
        if (byteOrderMark[0] == 0x2b && byteOrderMark[1] == 0x2f && byteOrderMark[2] == 0x76) return Encoding.UTF7;
        if (byteOrderMark[0] == 0xef && byteOrderMark[1] == 0xbb && byteOrderMark[2] == 0xbf) return Encoding.UTF8;
        if (byteOrderMark[0] == 0xff && byteOrderMark[1] == 0xfe) return Encoding.Unicode; //UTF-16LE
        if (byteOrderMark[0] == 0xfe && byteOrderMark[1] == 0xff) return Encoding.BigEndianUnicode; //UTF-16BE
        if (byteOrderMark[0] == 0 && byteOrderMark[1] == 0 && byteOrderMark[2] == 0xfe && byteOrderMark[3] == 0xff) return Encoding.UTF32;

        return null;    // no BOM found
    }

    private static Encoding GetEncodingByParsing(string filename, Encoding encoding)
    {            
        var encodingVerifier = Encoding.GetEncoding(encoding.BodyName, new EncoderExceptionFallback(), new DecoderExceptionFallback());

        try
        {
            using (var textReader = new StreamReader(filename, encodingVerifier, detectEncodingFromByteOrderMarks: true))
            {
                while (!textReader.EndOfStream)
                {                        
                    textReader.ReadLine();   // in order to increment the stream position
                }

                // all text parsed ok
                return textReader.CurrentEncoding;
            }
        }
        catch (Exception ex) { }

        return null;    // 
    }
}
0
Pacurar Stefan

Il peut être utile

string path = @"address/to/the/file.extension";

using (StreamReader sr = new StreamReader(path))
{ 
    Console.WriteLine(sr.CurrentEncoding);                        
}
0
raushan