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

Posted

技术标签:

【中文标题】如何确定文件是c#中的二进制文件还是文本文件? [复制]【英文标题】:How can I determine if a file is binary or text in c#? [duplicate] 【发布时间】:2010-10-28 23:51:00 【问题描述】:

我需要确定 80% 的文件是二进制文件还是文本文件,有什么方法可以在 c# 中快速、肮脏/丑陋地做到这一点?

【问题讨论】:

“二进制”是指可执行文件还是任何随机的东西? 任何不是文本的东西,例如图片、音乐、msword、可执行文件、dll 等 如果你告诉我一个人如何确定差异......我可以帮助你处理 C# 部分:) 【参考方案1】:

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

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

【讨论】:

谢谢,我做了类似的事情,我寻找了连续的空值。【参考方案2】:

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

尽管如此,您总是很不走运,得到一个看起来像文本的二进制文件,反之亦然。

【讨论】:

谢谢,我查找了 4 个连续的空值“\0\0\0\0”二进制文件似乎有很多,所以我在 50 个随机文件中对其进行了测试,它可以工作。 四个连续的空值不能说明一些 .png 文件是二进制的,所以我尝试了两个连续的空值,效果更好。 如果文本文件是 ASCII 或 UTF-8,找到 一个 零字节应该足以断定它不是。这对于 UTF-16 和 UTF-32 文件会失败,但大多数文本编辑器也会失败;-)【参考方案3】:

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

背景

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

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

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

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

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

我的目标是:

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

我提出的解决方案适用于测试数据,包括 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

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

其他有趣的相关链接:

About UTF and BOM on Unicode.org Unicode sample files How to detect encoding of text file 和 Detect file encoding in Csharp

【讨论】:

缺少 getSize 函数。感谢您的代码。重要的部分已被使用,到目前为止测试似乎进展顺利。 我其实很喜欢这个解决方案不会读取整个文件。它使运行一个工具来观察可能包含 50 MB 视频的整个目录变得更加容易。 @AtronSeige 您可以使用new FileInfo(path).Length 获取文件大小。 符合编码也有帮助。我编写了工具来符合marketplace.visualstudio.com/…中使用您的解决方案的编码。 谢谢。工作除了一个案例。我拿了一个 XML 文件,在记事本中打开它,然后保存为 Unicode(还添加了一些外来字符)。我将文件存储在 mysql 数据列的博客或文本字段中,然后将其写回磁盘。【参考方案4】:

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

public bool HasBinaryContent(string content)

    return content.Any(ch => char.IsControl(ch) && ch != '\r' && ch != '\n');

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

【讨论】:

我将包括 FF 和 VT。 (我猜 HT 已经……) 你也应该排除 '\t' 不适用于波斯文本【参考方案5】:

如果这里真正的问题是“可以不加修改地使用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;
    

【讨论】:

不适用于我放置 à(带重音的法语 a)的简单文本文件。【参考方案6】:

好问题! .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 字节的块并检查其中 8 个块中的连续空值来轻松扩展它(就我个人而言,我会推断它是一个二进制如果其中 2 个或 3 个匹配,则文件 - 空值在文本文件中很少见)。

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

【讨论】:

【参考方案7】:

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

【讨论】:

有时像我这样的人可以将二进制文件的扩展名更改为.txt 显然,但他要求便宜又肮脏-没有万无一失的方法,只能请人阅读。 那很好,不幸的是我没有处理常见的扩展,我正在编写所有文件的某种列表,并且需要对它们进行分类 bin 或文本,大多数人都是这样做的,但就像我一样我很懒,我更喜欢写代码。 许多人导出带有 .xls 扩展名的“excel 文件”,实际上是 csv 文件或 html 文件。 @TimSchmelter:我说快速而肮脏,不是万无一失且 100% 有效。 :)【参考方案8】:

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

要考虑 unicode,请确保在您的流上标记编码。

这确实不是最理想的,但你说的又快又脏。

【讨论】:

哦,\me 不确定正则表达式几兆字节的文件是否接近“快速”。 取决于quick的定义。快跑还是快写? :)【参考方案9】:

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

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

【讨论】:

如果文件保证为 UTF8/16 或二进制文件,则此方法有效。但如果两者都不是呢?如果它是一个既不是 ASCII 也不是 UTF-8/16 编码的文本文件怎么办。如果它在 Big5 代码页中编码怎么办?还是 ISO-8859-1?这些没有 BOM。那么......如何也涵盖这种情况? 如果文件是 (US-)ASCII 它实际上是 UTF-8,因为具有 7 位字符代码的字符在 UTF-8 中被翻译成它们自己,但如果它是在某些本地化的 ANSI 代码页,通过上述方法仍然会被识别为二​​进制。【参考方案10】:

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

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

【讨论】:

这当然取决于使用的编码。 并且将随机长度的文件转换为字节数组并转换为字符串很容易使用大量资源。想想一个 2Gb 的日志文件(这绝对是一个文本文件)......如果你想将它与未转换的版本进行比较,你必须保留超过 4 Gb 的内存,而不是逐页比较...... . 这甚至不快。【参考方案11】:

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

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

【讨论】:

以上是关于如何确定文件是c#中的二进制文件还是文本文件? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

☀️ 学会编程入门必备 C# 最基础知识介绍—— C# 高级文件操作(文本文件的读写二进制文件的读写Windows 文件系统的操作)

写入/读取文本文件 (C#)

c# 将字符串以二进制形式写入文件

☀️ 学会编程入门必备 C# 最基础知识介绍—— C# 高级文件操作(文本文件的读写二进制文件的读写Windows 文件系统的操作)

如何从 C# 中的文本文件中删除一行?

在 C# 中确定可执行文件是 64 位还是 32 位 [重复]