C# - 检查文件是否为文本文件

20

如何在C#中使用FileStream打开文件时测试文件是否为“文本类型”文件?我希望我的程序可以打开任何文本基础文件,例如.txt、.html等。

但不要打开诸如.doc、.pdf或.exe等文件。


1
创建一个 TXT 文件并将其重命名为 myfile.abc,在您看来它仍然是一个“文本类型”文件吗? - Cheng Chen
3
是的,文件内容才是重要的,而不是扩展名。 - Sev
那么在你的程序中,a.exe 可能是一个文本类型文件吗? - Cheng Chen
如果它是一个扩展名被修改的文本文件,理论上它可能存在。 - Sev
1
请查看此解决方案 http://filesignatures.codeplex.com/ - NoWar
6个回答

10

一般情况下:无法判断。

如果你使用8位编码打开以UTF-16格式存储的文本文件,它可能看起来像是二进制文件。同样地,某人可以将文本文件保存为.doc(它是一个文档)。

虽然你可以打开文件并查看其中一些内容,但这些启发式方法有时会失败(例如,记事本尝试这样做,通过精选几个字符,记事本会猜错并显示完全不同的内容)。

如果你有具体的场景,而不是能够打开和处理任何东西,那么应该能够做得更好。


9
我猜你可以检查前1000个(任意数字)字符,看是否有不可打印的字符,或者它们是否都是某个特定范围内的ASCII码。如果是后者,则假设它是文本?无论你做什么都只是一个猜测。

可能检查空格和换行符的常规间隔 - Wouter
4
更新:如今,文本文件更有可能是UTF-8或其他Unicode字符编码的变体。寻找更新的答案。例如,了解Notepad++如何确定一个文件是文本文件。 - ToolmakerSteve
许多应用程序检查NUL字符以确定文件是否为二进制文件。Git就是一个例子。请参见下面的完整答案。 - bytedev

8

正如其他人指出的那样,没有绝对确定的方法。但是为了确定一个文件是否为二进制文件(相对于确定它是否为文本文件来说要容易一些),有些实现会检查连续的NUL字符。Git似乎只检查前8000个字符中是否存在NUL字符,如果找到一个则将该文件视为二进制文件。更多详细信息请参见这里

以下是我编写的类似的C#解决方案,它寻找给定数量的要求连续的NUL。如果IsBinary返回false,则很可能你的文件是基于文本的。

public bool IsBinary(string filePath, int requiredConsecutiveNul = 1)
{
    const int charsToCheck = 8000;
    const char nulChar = '\0';

    int nulCount = 0;

    using (var streamReader = new StreamReader(filePath))
    {
        for (var i = 0; i < charsToCheck; i++)
        {
            if (streamReader.EndOfStream)
                return false;

            if ((char) streamReader.Read() == nulChar)
            {
                nulCount++;

                if (nulCount >= requiredConsecutiveNul)
                    return true;
            }
            else
            {
                nulCount = 0;
            }
        }
    }

    return false;
}

3
要获取文件的实际类型,您必须检查其头信息,即使扩展名被修改也不会改变。您可以在此处链接获取头信息列表,并在代码中使用类似以下的内容:
using(var stream = new FileStream(fileName, FileMode.Open, FileAccess.Read))
{
   using(var reader = new BinaryReader(stream))
   {
     // read the first X bytes of the file
     // In this example I want to check if the file is a BMP
     // whose header is 424D in hex(2 bytes 6677)
     string code = reader.ReadByte().ToString() + reader.ReadByte().ToString();
     if (code.Equals("6677"))
     {
        //it's a BMP file
     }
   }
}

2
我对你提供的“这里”的链接很感兴趣,但是它已经失效了。你知道原来的URL是什么,或者新的URL是什么吗? - Entree
@MacGyver: 抱歉,坏链接不在我的控制范围之内。我是在别人的帖子中找到这个解决方案的。 - Cheng Chen
3
这种方法有缺陷。无法区分BMP文件和以字符“BM”开头的文件,而这种情况很可能会发生。 - mafu
1
作为实际问题,这应该扩展到检查不止前两个字符,以决定文件缺少头部的“可能性”,并且实际上是其他格式。例如,如果您读取文件的所有字节,并且其中没有一个设置了符号位,则高度可能是ASCII文本。决定它是否实际上是没有BOM的UTF-8文本文件要困难得多(无法完美地完成)。 - ToolmakerSteve

0

我有一个对我有效的解决方案。这是一个通用的解决方案,可以检查所有类型的二进制文件。

     /// <summary>
     /// This method checks whether selected file is Binary file or not.
     /// </summary>     
     public bool CheckForBinary()
     {

             Stream objStream = new FileStream("your file path", FileMode.Open, FileAccess.Read);
             bool bFlag = true;

             // Iterate through stream & check ASCII value of each byte.
             for (int nPosition = 0; nPosition < objStream.Length; nPosition++)
             {
                 int a = objStream.ReadByte();

                 if (!(a >= 0 && a <= 127))
                 {
                     break;            // Binary File
                 }
                 else if (objStream.Position == (objStream.Length))
                 {
                     bFlag = false;    // Text File
                 }
             }
             objStream.Dispose();

             return bFlag;                   
     }

4
当然,这只适用于真正的ASCII字符,而不适用于其他带有重音符号的字符。 - Harald Coppoolse

-1
public bool IsTextFile(string FilePath)
  using (StreamReader reader = new StreamReader(FilePath))
  {
       int Character;
       while ((Character = reader.Read()) != -1)
       {
           if ((Character > 0 && Character < 8) || (Character > 13 && Character < 26))
           {
                    return false; 
           }
       }
  }
  return true;
}

2
请解释一下为什么选择了这些范围。 - ToolmakerSteve
为什么魔数被认为是反模式的一个教训。 - bytedev

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接