¿Cómo puedo determinar si un archivo es binario o texto en c #?

Necesito determinar en 80% si un archivo es binario o de texto, ¿hay alguna forma de hacerlo, incluso rápido y sucio / feo en c #?

Probablemente buscaría una gran cantidad de caracteres de control que, por lo general, estarían presentes en un archivo binario, pero rara vez en un archivo de texto. Los archivos binarios tienden a usar 0 lo suficiente como para que la prueba de muchos bytes 0 sea suficiente para capturar la mayoría de los archivos. Si te importa la localización, también deberías probar patrones de varios bytes.

Sin embargo, como se dijo, siempre puede tener mala suerte y obtener un archivo binario que se parece al texto o viceversa.

Hay un método llamado Markov Chains. Escanee algunos archivos de modelo de ambos tipos y para cada valor de byte de 0 a 255 reúna estadísticas (básicamente probabilidad) de un valor posterior. Esto le dará un perfil de 64 Kb (256 x 256) con el que puede comparar sus archivos de tiempo de ejecución (dentro de un umbral de%).

Supuestamente, así es como funciona la función Auto-Detect Encoding de los navegadores.

Compartir mi solución con la esperanza de que ayude a los demás, ya que me ayuda desde estas publicaciones y foros.

Fondo

He estado investigando y explorando una solución para el mismo. Sin embargo, esperaba que fuera simple o ligeramente retorcido.

Sin embargo, la mayoría de los bashs proporcionan soluciones intrincadas aquí, así como otras fonts y se sumerge en Unicode, serie UTF , lista de materiales, codificaciones, órdenes de bytes. En el proceso, también fui fuera de la carretera y también a Ascii Tables and Code pages .

De todos modos, he encontrado una solución basada en la idea del lector de flujo y verificación de caracteres de control personalizado .

Está construido teniendo en cuenta varias sugerencias y consejos proporcionados en el foro y en otros lugares, tales como:

  1. Compruebe la gran cantidad de caracteres de control, por ejemplo, buscando múltiples caracteres nulos consecutivos.
  2. Verifique UTF, Unicode, Codificaciones, BOM, Órdenes de bytes y aspectos similares.

Mi meta es:

  1. No debería depender de órdenes de bytes, codificaciones y otros trabajos esotéricos más complicados.
  2. Debería ser relativamente fácil de implementar y fácil de entender.
  3. Debería funcionar en todo tipo de archivos.

La solución presentada funciona para mí en datos de prueba que incluyen mp3, eml, txt, información, flv, mp4, pdf, gif, png, jpg. Da resultados como se esperaba hasta ahora.

Cómo funciona la solución

Confío en el constructor predeterminado de StreamReader para hacer lo que mejor puede hacer con respecto a la determinación de las características relacionadas con la encoding de archivos que usa UTF8Encoding de manera predeterminada.

Creé mi propia versión de verificación para la condición de char de control personalizado porque Char.IsControl no parece útil. Dice:

Los caracteres de control están formateando y otros caracteres que no son de impresión, como ACK, BEL, CR, FF, LF y VT. El estándar Unicode asigna puntos de código de \ U0000 a \ U001F, \ U007F y de \ U0080 a \ U009F para controlar los caracteres. Estos valores se deben interpretar como caracteres de control a menos que su uso esté definido por una aplicación. Considera a LF y CR como personajes de control, entre otras cosas

Eso hace que no sea útil ya que los archivos de texto incluyen CR y LF al menos.

Solución

static void testBinaryFile(string folderPath) { List output = new List(); foreach (string filePath in getFiles(folderPath, true)) { output.Add(isBinary(filePath).ToString() + " ---- " + filePath); } Clipboard.SetText(string.Join("\n", output), TextDataFormat.Text); } public static List getFiles(string path, bool recursive = false) { return Directory.Exists(path) ? Directory.GetFiles(path, "*.*", recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly).ToList() : new List(); } 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 prueba la solución anterior, hágamelo saber que funciona para usted o no.

Otros enlaces interesantes y relacionados:

  • Acerca de UTF y BOM en Unicode.org
  • Archivos de muestra Unicode
  • Cómo detectar la encoding del archivo de texto y
  • Detectar la encoding de archivos en Csharp

Si la verdadera pregunta aquí es “¿Se puede leer y escribir este archivo usando StreamReader / StreamWriter sin modificación?”, Entonces la respuesta está aquí:

 ///  /// Detect if a file is text and detect the encoding. ///  ///  /// The detected encoding. ///  ///  /// The file name. ///  ///  /// The number of characters to use for testing. ///  ///  /// true if the file is text. ///  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; } } 

Si bien esto no es infalible, debería verificar si tiene algún contenido binario.

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

Porque si existe algún carácter de control (aparte del estándar \r\n ), entonces probablemente no sea un archivo de texto.

Rápido y sucio es usar la extensión de archivo y buscar extensiones comunes de texto como .txt. Para esto, puede usar la llamada Path.GetExtension . Cualquier otra cosa no se clasificaría realmente como “rápida”, aunque podría estar sucia.

Una forma realmente muy sucia sería construir una expresión regular que solo tome texto estándar, signos de puntuación, símbolos y espacios en blanco, cargue una porción del archivo en una secuencia de texto y luego ejecútelo contra la expresión regular. Dependiendo de lo que califica como un archivo de texto puro en el dominio de su problema, ninguna coincidencia exitosa indicaría un archivo binario.

Para contabilizar Unicode, asegúrese de marcar la encoding en su transmisión como tal.

Esto es realmente poco óptimo, pero dijiste rápido y sucio.

Gran pregunta! Me sorprendió que .NET no ofrezca una solución fácil para esto.

El siguiente código me ayudó a distinguir entre imágenes (png, jpg, etc.) y archivos de texto.

Acabo de comprobar nulos consecutivos ( 0x00 ) en los primeros 512 bytes, según las sugerencias de Ron Warholic y 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); } 

Obviamente, este es un enfoque rápido y sucio, sin embargo, se puede expandir fácilmente al dividir el archivo en 10 fragmentos de 512 bytes cada uno y verificar 8 uno de ellos por nulos consecutivos (personalmente, deduciría que es un archivo binario si 2 o 3 de ellos coinciden - los nulos son raros en los archivos de texto).

Eso debería proporcionar una solución bastante buena para lo que está buscando.

http://codesnipers.com/?q=node/68 describe cómo detectar UTF-16 vs. UTF-8 usando una Marca de Orden de Byte (que puede aparecer en su archivo). También sugiere recorrer algunos bytes para ver si se ajustan al patrón de secuencia de múltiples bytes UTF-8 (a continuación) para determinar si su archivo es un archivo de texto.

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

¿Qué hay de otra manera: determinar la longitud de la matriz binaria, que representa los contenidos del archivo y compararlo con la longitud de la cadena que tendrá después de convertir determinada matriz binaria en texto.

Si la longitud es la misma, no hay símbolos “no legibles” en el archivo, es texto (estoy seguro de que el 80%).

Otra forma es detectar el juego de caracteres del archivo usando UDE . Si el juego de caracteres se detectó con éxito, puede estar seguro de que es texto, de lo contrario es binario. Porque el binario no tiene charset.

Por supuesto, puede usar otra biblioteca de detección de juego de caracteres que no sea UDE. Si la biblioteca de detección de juego de caracteres es lo suficientemente buena, este enfoque podría alcanzar el 100% de exactitud.