web-dev-qa-db-fra.com

Comment puis-je déterminer si un fichier est binaire ou texte en c #?

J'ai besoin de déterminer à 80% si un fichier est binaire ou texte, est-il possible de le faire même rapidement et sale/laid en c #?

51
Pablo Retyk

Je rechercherais probablement une abondance de caractères de contrôle qui seraient généralement présents dans un fichier binaire mais rarement dans un fichier texte. Les fichiers binaires ont tendance à utiliser 0 suffisamment pour que le simple test de plusieurs 0 octets soit probablement suffisant pour intercepter la plupart des fichiers. Si vous vous souciez de la localisation, vous devrez également tester des modèles multi-octets.

Comme indiqué, vous pouvez toujours être malchanceux et obtenir un fichier binaire qui ressemble à du texte ou vice versa.

21
Ron Warholic

Il existe une méthode appelée chaînes de Markov. Analysez quelques fichiers de modèle des deux types et pour chaque valeur d'octet de 0 à 255, collectez les statistiques (essentiellement la probabilité) d'une valeur ultérieure. Cela vous donnera un profil de 64 Ko (256 x 256) auquel vous pourrez comparer vos fichiers d'exécution (dans un seuil de%).

Soi-disant, c'est ainsi que fonctionne la fonction d'encodage à détection automatique des navigateurs.

31
zvolkov

Partager ma solution dans l'espoir qu'elle aide les autres car elle m'aide à partir de ces messages et forums.

Contexte

J'ai recherché et exploré une solution pour le même. Cependant, je m'attendais à ce qu'il soit simple ou légèrement tordu.

Cependant, la plupart des tentatives fournissent ici des solutions compliquées ainsi que d'autres sources et plongent dans Unicode, série UTF , BOM, encodages, ordres d'octets. Dans le processus, je suis également allé hors route et dans Tableaux Ascii et pages de code aussi.

Quoi qu'il en soit, j'ai trouvé une solution basée sur l'idée de lecteur de flux et vérification des caractères de contrôle personnalisés.

Il est construit en tenant compte de divers conseils et astuces fournis sur le forum et ailleurs tels que:

  1. Recherchez de nombreux caractères de contrôle, par exemple à la recherche de plusieurs caractères nuls consécutifs.
  2. Vérifiez UTF, Unicode, les encodages, la nomenclature, les ordres d'octets et les aspects similaires.

Mon but est:

  1. Il ne doit pas s'appuyer sur des ordres d'octets, des codages et d'autres travaux ésotériques plus complexes.
  2. Il devrait être relativement facile à mettre en œuvre et à comprendre.
  3. Il devrait fonctionner sur tous les types de fichiers.

La solution présentée fonctionne pour moi sur des données de test qui incluent mp3, eml, txt, info, flv, mp4, pdf, gif, png, jpg. Il donne les résultats attendus jusqu'à présent.

Fonctionnement de la solution

Je m'appuie sur le constructeur par défaut de StreamReader pour faire ce qu'il peut le mieux en ce qui concerne la détermination des caractéristiques liées au codage de fichier qui utilise TF8Encoding par défaut.

J'ai créé ma propre version de la vérification de la condition du caractère de contrôle personnalisé parce que Char.IsControl ne semble pas utile. Ça dit:

Les caractères de contrôle sont le formatage et d'autres caractères non imprimables, tels que ACK, BEL, CR, FF, LF et VT. La norme Unicode affecte des points de code de\U0000 à\U001F,\U007F et de\U0080 à\U009F pour contrôler les caractères. Ces valeurs doivent être interprétées comme des caractères de contrôle, sauf si leur utilisation est définie autrement par une application. Il considère LF et CR comme caractères de contrôle entre autres choses

Cela ne le rend pas utile car les fichiers texte incluent CR et LF au moins.

Solution

static void testBinaryFile(string folderPath)
{
    List<string> output = new List<string>();
    foreach (string filePath in getFiles(folderPath, true))
    {
        output.Add(isBinary(filePath).ToString() + "  ----  " + filePath);
    }
    Clipboard.SetText(string.Join("\n", output), TextDataFormat.Text);
}

public static List<string> getFiles(string path, bool recursive = false)
{
    return Directory.Exists(path) ?
        Directory.GetFiles(path, "*.*",
        recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly).ToList() :
        new List<string>();
}    

public static bool isBinary(string path)
{
    long length = getSize(path);
    if (length == 0) return false;

    using (StreamReader stream = new StreamReader(path))
    {
        int ch;
        while ((ch = stream.Read()) != -1)
        {
            if (isControlChar(ch))
            {
                return true;
            }
        }
    }
    return false;
}

public static bool isControlChar(int ch)
{
    return (ch > Chars.NUL && ch < Chars.BS)
        || (ch > Chars.CR && ch < Chars.SUB);
}

public static class Chars
{
    public static char NUL = (char)0; // Null char
    public static char BS = (char)8; // Back Space
    public static char CR = (char)13; // Carriage Return
    public static char SUB = (char)26; // Substitute
}

Si vous essayez la solution ci-dessus, faites-moi savoir que cela fonctionne pour vous ou non.

Autres liens intéressants et connexes:

13
bhavik shah

Si la vraie question ici est "Est-ce que ce fichier peut être lu et écrit en utilisant StreamReader/StreamWriter sans modification?", Alors la réponse est ici:

/// <summary>
/// Detect if a file is text and detect the encoding.
/// </summary>
/// <param name="encoding">
/// The detected encoding.
/// </param>
/// <param name="fileName">
/// The file name.
/// </param>
/// <param name="windowSize">
/// The number of characters to use for testing.
/// </param>
/// <returns>
/// true if the file is text.
/// </returns>
public static bool IsText(out Encoding encoding, string fileName, int windowSize)
{
    using (var fileStream = File.OpenRead(fileName))
    {
    var rawData = new byte[windowSize];
    var text = new char[windowSize];
    var isText = true;

    // Read raw bytes
    var rawLength = fileStream.Read(rawData, 0, rawData.Length);
    fileStream.Seek(0, SeekOrigin.Begin);

    // Detect encoding correctly (from Rick Strahl's blog)
    // http://www.west-wind.com/weblog/posts/2007/Nov/28/Detecting-Text-Encoding-for-StreamReader
    if (rawData[0] == 0xef && rawData[1] == 0xbb && rawData[2] == 0xbf)
    {
        encoding = Encoding.UTF8;
    }
    else if (rawData[0] == 0xfe && rawData[1] == 0xff)
    {
        encoding = Encoding.Unicode;
    }
    else if (rawData[0] == 0 && rawData[1] == 0 && rawData[2] == 0xfe && rawData[3] == 0xff)
    {
        encoding = Encoding.UTF32;
    }
    else if (rawData[0] == 0x2b && rawData[1] == 0x2f && rawData[2] == 0x76)
    {
        encoding = Encoding.UTF7;
    }
    else
    {
        encoding = Encoding.Default;
    }

    // Read text and detect the encoding
    using (var streamReader = new StreamReader(fileStream))
    {
        streamReader.Read(text, 0, text.Length);
    }

    using (var memoryStream = new MemoryStream())
    {
        using (var streamWriter = new StreamWriter(memoryStream, encoding))
        {
        // Write the text to a buffer
        streamWriter.Write(text);
        streamWriter.Flush();

        // Get the buffer from the memory stream for comparision
        var memoryBuffer = memoryStream.GetBuffer();

        // Compare only bytes read
        for (var i = 0; i < rawLength && isText; i++)
        {
            isText = rawData[i] == memoryBuffer[i];
        }
        }
    }

    return isText;
    }
}
10
DigitalMindspring

Bien que ce ne soit pas infaillible, cela devrait vérifier pour voir s'il a du contenu binaire.

public bool HasBinaryContent(string content)
{
    return content.Any(ch => char.IsControl(ch) && ch != '\r' && ch != '\n');
}

Parce que si un caractère de contrôle existe (à part le standard \r\n), alors ce n'est probablement pas un fichier texte.

Rapide et sale consiste à utiliser l'extension de fichier et à rechercher des extensions de texte courantes telles que .txt. Pour cela, vous pouvez utiliser l'appel Path.GetExtension . Tout le reste ne serait pas vraiment classé comme "rapide", bien qu'il puisse être sale.

4
Jeff Yates

Grande question! J'ai été moi-même surpris que .NET ne fournisse pas de solution simple à cela.

Le code suivant a fonctionné pour moi pour distinguer les images (png, jpg, etc.) et les fichiers texte.

Je viens de vérifier les valeurs nulles consécutives (0x00) dans les 512 premiers octets, selon les suggestions de Ron Warholic et Adam Bruss:

if (File.Exists(path))
{
    // Is it binary? Check for consecutive nulls..
    byte[] content = File.ReadAllBytes(path);
    for (int i = 1; i < 512 && i < content.Length; i++) {
        if (content[i] == 0x00 && content[i-1] == 0x00) {
            return Convert.ToBase64String(content);
        }
    }
    // No? return text
    return File.ReadAllText(path);
}

Évidemment, c'est une approche rapide et sale, mais elle peut être facilement développée en divisant le fichier en 10 morceaux de 512 octets chacun et en vérifiant 8 l'un d'eux pour des valeurs nulles consécutives (personnellement, je déduirais son fichier binaire si 2 ou 3 d'entre eux correspondent - les valeurs nulles sont rares dans les fichiers texte).

Cela devrait fournir une assez bonne solution pour ce que vous recherchez.

4
Steven de Salas

Une façon vraiment vraiment très sale serait de construire une expression régulière qui ne prend que du texte standard, de la ponctuation, des symboles et des espaces, charger une partie du fichier dans un flux de texte, puis l'exécuter contre l'expression régulière. Selon ce qui est considéré comme un fichier texte pur dans votre domaine problématique, aucune correspondance réussie n'indiquerait un fichier binaire.

Pour tenir compte de l'unicode, assurez-vous de marquer l'encodage sur votre flux comme tel.

C'est vraiment sous-optimal, mais vous avez dit rapide et sale.

2
Chad Ruppert

Une autre façon est de détecter le jeu de caractères du fichier en utilisant UDE . Si charset est détecté avec succès, vous pouvez être sûr que c'est du texte, sinon c'est binaire. Parce que le binaire n'a pas de charset.

Bien sûr, vous pouvez utiliser une autre bibliothèque de détection de jeux de caractères autre que UDE. Si la bibliothèque de détection de jeux de caractères est suffisamment bonne, cette approche pourrait atteindre une exactitude de 100%.

1
Tyler Long

Que diriez-vous d'une autre manière: déterminez la longueur du tableau binaire, représentant le contenu du fichier et comparez-le avec la longueur de la chaîne que vous aurez après avoir converti le tableau binaire donné en texte.

Si la longueur est la même, il n'y a pas de symboles "non lisibles" dans le fichier, c'est du texte (je suis sûr à 80%).

1
shytikov

http://codesnipers.com/?q=node/68 décrit comment détecter UTF-16 par rapport à UTF-8 en utilisant une marque d'ordre d'octets (qui peut apparaître dans votre fichier). Il suggère également de parcourir certains octets pour voir s'ils sont conformes au modèle de séquence multi-octets UTF-8 (ci-dessous) afin de déterminer si votre fichier est un fichier texte.

  • 0xxxxxxx ASCII <0x80 (128)
  • 110xxxxx 10xxxxxx 2 octets> = 0x80
  • 1110xxxx 10xxxxxx 10xxxxxx 3 octets> = 0x400
  • 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 4 octets> = 0x10000
1
foson