我怎样才能确定一个文件是二进制或文本在C#中?

我需要确定在80%,如果一个文件是二进制或文本,有没有办法做到这一点,即使在c#中快速和脏/丑陋?

我可能会寻找丰富的控制字符,通常会出现在二进制文件中,但很less出现在文本文件中。 二进制文件倾向于使用足够的0,只是testing多个0字节可能足以捕获大多数文件。 如果你关心本地化,你也需要testing多字节模式。

如上所述,你总是不幸的,并得到一个看起来像文本,反之亦然的二进制文件。

有一种叫做马尔可夫链的方法。 扫描两种types的几个模型文件,并从0到255的每个字节值收集后续值的统计数据(基本概率)。 这会给你一个64Kb(256×256)的configuration文件,你可以比较你的运行时间文件(%阈值内)。

假设这是浏览器的自动检测编码function的工作原理。

分享我的解决scheme,希望它可以帮助其他人,因为它可以帮助我从这些post和论坛。

背景

我一直在研究和探索相同的解决scheme。 不过,我期望它是简单或稍微扭曲。

但是,大多数尝试在这里提供了复杂的解决scheme,以及其他源代码,并深入到Unicode, UTF-系列 ,BOM,编码,字节命令。 在这个过程中,我也进入了Ascii Tables and Code页面 。

无论如何,我已经想出了一个基于stream读取器和自定义控制字符检查的解决scheme。

考虑到论坛和其他地方提供的各种提示和提示,比如:

  1. 检查很多控制字符,例如查找多个连续的空字符。
  2. 检查UTF,Unicode,编码,BOM,字节顺序和类似的方面。

我的目标是:

  1. 它不应该依赖字节顺序,编码和其他更多的深奥的工作。
  2. 这应该是相对容易实施和易于理解。
  3. 它应该适用于所有types的文件。

这个解决scheme适用于我的testing数据,包括mp3,eml,txt,info,flv,mp4,pdf,gif,png,jpg。 它迄今为止预期的结果。

该解决scheme如何工作

我依靠StreamReader的默认构造函数来完成它在确定使用UTF8Encoding默认的文件编码相关特性方面做得最好的方法。

我创build了自己的检查自定义控件字符条件的版本,因为Char.IsControl似乎没有用。 它说:

控制字符是格式化和其他非打印字符,如ACK,BEL,CR,FF,LF和VT。 Unicode标准分配从\ U0000到\ U001F,\ U007F和从\ U0080到\ U009F的代码点来控制字符。 这些值将被解释为控制字符,除非应用程序另有定义。 它将LF和CR视为控制字符等等

这使得它没有用,因为文本文件至less包含CR和LF。

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 } 

如果你尝试以上解决scheme,让我知道它适用于你或不。

其他有趣的和相关的链接:

  • 关于Unicode.org上的UTF和BOM
  • Unicode样本文件
  • 如何检测文本文件的编码和
  • 检测Csharp中的文件编码

如果真正的问题是“可以使用StreamReader / StreamWriter读写这个文件而不做修改吗?”,那么答案就在这里:

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

虽然这不是万无一失的,但应该检查它是否有任何二进制内容。

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

因为如果存在任何控制字符(除了标准\r\n ),那么它可能不是一个文本文件。

快速而肮脏的是使用文件扩展名,并寻找常见的文本扩展名,如.txt。 为此,您可以使用Path.GetExtension调用。 其他任何东西都不会被归类为“快”,尽pipe它可能很脏。

一个非常非常肮脏的方法是构build一个只带有标准文本,标点符号,符号和空白字符的正则expression式,在文本stream中加载一部分文件,然后在正则expression式中运行它。 根据您的问题域中纯文本文件的限定,没有成功的匹配将指示二进制文件。

要说明unicode,请确保在stream上标记编码。

这真的不是最理想的,但是你说得快而肮脏。

好问题! 我很惊讶,.NET不提供一个简单的解决scheme。

下面的代码为我工作区分图像(PNG,JPG等)和文本文件。

根据Ron Warholic和Adam Bruss的build议,我在前512个字节中检查了连续的空值( 0x00 ):

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

显然,这是一个快速和肮脏的方法,但是它可以很容易地扩展,将文件分割成10个512字节的数据块,并检查8个连续的空值(个人来说,如果是2个或者其中的3个匹配 – 在文本文件中的空值是罕见的)。

这应该提供一个很好的解决scheme,你以后。

http://codesnipers.com/?q=node/68描述了如何使用字节顺序标记(可能出现在你的文件中)来检测UTF-16与UTF-8。; 它还build议循环一些字节,看它们是否符合UTF-8多字节序列模式(如下)以确定您的文件是否是文本文件。

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

另外一种方法:确定二进制数组的长度,表示文件的内容,并将其与将给定的二进制数组转换为文本后的string长度进行比较。

如果长度相同,文件中就没有“不可读”的符号,它就是文本(我确信80%)。

另一种方法是使用UDE检测文件的字符集。 如果字符集检测成功,可以确定它是文本,否则是二进制的。 因为二进制没有字符集。

当然你也可以使用UDE以外的其他字符集检测库。 如果字符集检测库足够好,这种方法可以达到100%的正确性。