如何在c#中确定文件是二进制还是文本?

时间:2009-05-26 14:05:20

标签: c# text file-io binary

我需要在80%确定文件是二进制文件还是文本文件,有没有办法在c#中快速,脏/丑?

11 个答案:

答案 0 :(得分:31)

有一种名为Markov Chains的方法。扫描两种类型的几个模型文件,并从0到255的每个字节值收集后续值的统计数据(基本上是概率)。这将为您提供64Kb(256x256)配置文件,您可以比较运行时文件(在%阈值内)。

据说,这就是浏览器的自动检测编码功能的工作原理。

答案 1 :(得分:21)

我可能会寻找大量的控制字符,这些字符通常存在于二进制文件中,但很少出现在文本文件中。二进制文件倾向于使用足够的0,只测试多个0字节可能足以捕获大多数文件。如果您关心本地化,您还需要测试多字节模式。

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

答案 2 :(得分:13)

分享我的解决方案,希望它可以帮助其他人,因为它可以帮助我从这些帖子和论坛中获取。

背景

我一直在研究和探索相同的解决方案。但是,我预计它会变得简单或略微扭曲。

然而,大多数尝试在此提供了复杂的解决方案以及其他来源,并潜入Unicode,UTF-series,BOM,编码,字节顺序。在这个过程中,我也走了越野路进入Ascii Tables and Code pages

无论如何,我已经提出了一个基于流阅读器和自定义控制字符检查的解决方案的解决方案。

它的构建考虑了论坛和其他地方提供的各种提示和技巧,例如:

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

    1. 它不应该依赖字节顺序,编码和其他更复杂的深奥工作。
    2. 它应该相对容易实现并且易于理解。
    3. 它应该适用于所有类型的文件。
    4. 该解决方案适用于我的测试数据,包括mp3,eml,txt,info,flv,mp4,pdf,gif,png,jpg。到目前为止,它给出了预期的结果。

      解决方案如何运作

      我依靠StreamReader default constructor来确定在确定默认情况下使用UTF8Encoding的文件编码相关特征方面做得最好的事情。

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

        

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

      这使它无用,因为文本文件至少包含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
      }
      

      如果您尝试以上解决方案,请告诉我它是否适合您。

      其他有趣和相关的链接:

答案 3 :(得分:10)

如果真正的问题是“可以使用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;
    }
}

答案 4 :(得分:8)

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

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

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

答案 5 :(得分:4)

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

答案 6 :(得分:4)

好问题!我很惊讶.NET没有为此提供简单的解决方案。

以下代码可用于区分图像(png,jpg等)和文本文件。

我根据Ron Warholic和Adam Bruss的建议,检查了前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字节的块来轻松扩展,并检查其中一个是否为连续的空值(个人而言,我会推断它的二进制文件)文件,如果它们中的2个或3个匹配 - 在文本文件中很少出现空值)。

这应该为你所追求的目标提供一个非常好的解决方案。

答案 7 :(得分:2)

真正非常脏的方法是构建一个只使用标准文本,标点符号,空格符号和空白字符的正则表达式,在文本流中加载文件的一部分,然后针对正则表达式运行它。根据问题域中纯文本文件的限定条件,没有成功的匹配项表示二进制文件。

要考虑unicode,请务必在流上标记编码。

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

答案 8 :(得分:1)

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

  • 0xxxxxxx ASCII&lt; 0x80(128)
  • 110xxxxx 10xxxxxx 2-byte&gt; = 0x80
  • 1110xxxx 10xxxxxx 10xxxxxx 3-byte&gt; = 0x400
  • 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 4字节&gt; = 0x10000

答案 9 :(得分:1)

另一种方式:确定二进制数组的长度,表示文件的内容,并将它与将给定二进制数组转换为文本后将具有的字符串长度进行比较。

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

答案 10 :(得分:1)

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

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