代码之家  ›  专栏  ›  技术社区  ›  Pablo Retyk

如何确定文件是二进制文件还是C_中的文本?[复制品]

  •  51
  • Pablo Retyk  · 技术社区  · 17 年前

    这个问题已经有了答案:

    我需要用80%来确定一个文件是二进制的还是文本的,是否有任何方法可以在C中快速、肮脏/丑陋地完成它?

    11 回复  |  直到 17 年前
        1
  •  21
  •   Jeff Yates    12 年前

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

    不过,如前所述,您可能总是不走运,并得到一个看起来像文本的二进制文件,反之亦然。

        2
  •  31
  •   Andriy Volkov    12 年前

    有一种方法叫做马尔可夫链。扫描两种类型的几个模型文件,并针对从0到255的每个字节值收集后续值的统计信息(基本上是概率)。这将为您提供一个64kb(256x256)的配置文件,您可以将运行时文件与之进行比较(在%阈值内)。

    假设浏览器的自动检测编码功能就是这样工作的。

        3
  •  13
  •   Community Mohan Dere    9 年前

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

    背景

    我一直在研究和探索同样的解决方案。但是,我希望它是简单的或者稍微扭曲的。

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

    不管怎样,我提出了一个基于流阅读器和 自定义控件字符检查 .

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

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

    我的目标是:

    1. 它不应该依赖字节顺序、编码和其他更复杂的深奥工作。
    2. 它应该相对容易实现和理解。
    3. 它应该适用于所有类型的文件。

    提供的解决方案适用于测试数据,包括MP3、EML、TXT、INFO、FLV、MP4、PDF、GIF、PNG、JPG。到目前为止,它给出了预期的结果。

    解决方案的工作原理

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

    我创建了我自己版本的检查自定义控件char条件,因为 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
    }
    

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

    其他有趣的相关链接:

        4
  •  10
  •   DigitalMindspring    15 年前

    如果真正的问题是“是否可以使用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;
        }
    }
    
        5
  •  8
  •   McKay    11 年前

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

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

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

        6
  •  4
  •   Jeff Yates    17 年前

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

        7
  •  3
  •   Steven de Salas Alexander Bollaert    11 年前

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

    下面的代码可以帮助我区分图像(PNG、JPG等)和文本文件。

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

    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个匹配,我会推断它是一个二进制文件-空值在文本文件中很少见)。

    这应该为你所追求的提供一个很好的解决方案。

        8
  •  2
  •   Chad Ruppert    17 年前

    一个真正肮脏的方法是构建一个只使用标准文本、标点符号、符号和空白字符的regex,在文本流中加载文件的一部分,然后对regex运行它。根据问题域中哪些文件是纯文本文件,没有成功的匹配将指示二进制文件。

    要解释Unicode,请确保在流中标记编码。

    这真是次优,但你说得又快又脏。

        9
  •  1
  •   foson    17 年前

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

    • 0xXXXXXX ASCII<0x80(128)
    • 110xxxxx 10xxxx 2字节>=0x80
    • 1110XXXX 10XXXX 10XXXX 3字节>。= 0x400
    • 11110xxx 10xxxx 10xxxx 10XXXXXX 4字节>=0x1000
        10
  •  1
  •   shytikov Cascabel    14 年前

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

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

        11
  •  1
  •   Tyler Liu    11 年前

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

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