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

Pab*_*tyk 52 c# binary file-io text

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

zvo*_*kov 31

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

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


Ron*_*lic 21

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

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

  • 如果文本文件是ASCII或UTF-8,找到_one_零字节应该足以得出结论.对于UTF-16和UTF-32文件,这将失败,但大多数文本编辑器也将失败;-) (8认同)
  • 谢谢,我找了4个连续的空"\ 0\0\0\0"二进制文件似乎有很多,所以我测试了50个随机文件,它的工作原理. (6认同)
  • 四个连续的空值没能说明一些 .png 文件是二进制的,所以我尝试了两个连续的空值,效果更好。 (2认同)

bha*_*hah 15

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

背景

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

但是,大多数尝试在这里提供了复杂的解决方案以及其他来源,并潜入Unicode,UTF系列,BOM,编码,字节顺序.在这个过程中,我也越野了,进入Ascii Tables和Code页面.

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

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

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

我的目标是:

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

所提供的解决方案适用于测试数据,包括mp3,eml,txt,info,flv,mp4,pdf,gif,png,jpg.到目前为止,它给出了预期的结果.

解决方案如何运作

我依靠StreamReader默认构造函数来执行它在确定默认使用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
}
Run Code Online (Sandbox Code Playgroud)

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

其他有趣和相关的链接:


小智 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 对于我放置à(法语a带重音)的简单文本文件不起作用. (3认同)

Але*_*чек 9

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

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

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

  • 您可能也应该排除 '\t' (3认同)
  • 我会包括FF和VT。(我猜HT已经..) (2认同)

Jef*_*tes 5

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

  • 有时像我这样的人可以将二进制文件的扩展名更改为 .txt (5认同)

Ste*_*las 5

好问题!我为自己感到惊讶,.NET并未为此提供简单的解决方案。

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

0x00根据Ron Warholic和Adam Bruss的建议,我只是检查了前512个字节中是否有连续的null():

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);
}
Run Code Online (Sandbox Code Playgroud)

显然,这是一种快速而又肮脏的方法,但是可以通过将文件分成10个大块(每个大块512字节)并检查其中8个块中是否包含连续的空值来轻松扩展(个人而言,如果2或其中3个匹配-空值在文本文件中很少)。

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