从以字节为单位的文本文件中获取可靠的字符数

本文关键字:获取 字符 文件 字节 为单位 文本 | 更新日期: 2023-09-27 18:10:59

好的,所以问题是:给定一个随机文本文件的FileInfo对象,并且知道所述文件的编码(它可以是ASCIIUTF7UTF8Unicode等)是否有一种方法可以在不读取它的情况下获得文件的确切字符计数?

你通过FileInfo.Length属性知道文件的字节大小,所以理论上知道编码的CharSize,你应该能够得到字符数。

测试一些编码似乎有效(ASCIIUnicode),但其他编码稍微偏离(例如UTF8)。

这在一般情况下是可能的,还是你必须读取整个文件才能始终获得可靠的字符计数?

从以字节为单位的文本文件中获取可靠的字符数

问题

如前所述,由于可变宽度字符编码,不读取所有字符是不可能的。

你所做的是通过假设所有字符都适合最小的单位来近似字符的数量。当文件中只有ASCII字符时,这将准确地用于字符编码,如UTF8UTF16

更好的近似

如果您知道目标语言,您可以通过假设平均每个字符是一定数量的字节来更好地近似字符。例如,对于UTF8和英语,大多数字符将是1字节。你可以说一个字符平均占用1.005字节(每200个字符占用一个2字节的字符),然后你可能会得到一个更好的近似值。

快速解码

由于这里的问题是读取整个文件的速度,所以我假设您正在处理大量文件或大量文件。两者都有各自的问题。如果这两种情况都不成立,那么尝试优化也就没有意义了。

内存问题

都有自己的问题,在第一种情况下,很可能内存一次不适合内存(至少不是连续的或与应用程序的其余部分运行)。解决方案是流式传输文件,而不是立即加载。

缺点是c#没有提供有效的内置方法来计数流中的字符。我能想到的唯一内置解决方案是这个SO答案中列出的。它会考虑代理,您可以指定编码。

速度问题

如果问题是存在大量文件,那么您可能已经花费了大量时间来查找每个文件的元数据。在这种情况下,我建议完全避免这个问题。如果您需要读取文件,那么您可能会从使用一个专门的函数中获得一些好处,在这个函数中,您可以跨多个调用共享一个大的文件缓冲区。代码示例:

/// <summary>
/// Counts all the characters in a file sharing a reading buffer across multiple calls.
/// </summary>
/// <param name="filePath">The path to the file.</param>
/// <param name="encoding">Encoding to use.</param>
/// <param name="buffer">The buffer to share, will be recreated if it cannot contain the file.</param>
/// <returns>The amount of characters in the file.</returns>
public static int GetCharacterCount(string filePath, Encoding encoding, ref byte[] buffer)
{
    int fileLength;
    using (var fstream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
    {
        fileLength = (int)fstream.Length;
        // Expand the buffer if necessary
        if (buffer == null || buffer.Length < fileLength)
            buffer = new byte[fstream.Length];
        if (fstream.Read(buffer, 0, fileLength) != fileLength)
            throw new EndOfStreamException("Couldn't read all bytes from the file.");
    }
    return encoding.GetCharCount(buffer, 0, fileLength);
}

回避问题

不计算文件中的字符数,您可以尝试完全避免这样做,只需执行一次,然后存储它。这样你甚至不需要解码文件,但你确实需要做一些簿记。如果查询频繁,刷新/创建很少,这可能是您最好的方法。您可以保留一个包含文件名和字符数的缓存,然后查询,而不是读取实际的文件。

这是否是一个有效的解决方案完全取决于你的用例。

优化解码

如果您无法控制输入文件,并且它们可能太大或太多,那么您可以通过编写专门的代码来获得主要收益。您可以使用SIMD和缓存优化的C语言。或者只是在c#中使用更有效的文件访问模式。不管你选择哪条路,它很快就会变得很棘手。通常,除非应用程序的目的仅仅是计算文件中的字符数,否则我不会在这上面浪费时间。

一般情况下,不阅读整个内容是不可能的。

原因是编码不能保证一个char正好占用N个字节。例如,默认的c#编码Unicode,即UTF-16允许某些字符为2或4字节(也可能是3字节-不确定,参见此主题的另一个答案)。一些其他编码可能允许您给出确切的数字,如ASCII,通常是7(填充到8)或8位。

你可以得到一个很好的估计,但可能不是一个确切的数字。

你可以提供一个解决方案,当你给用户一个估计,这将是快速的,因为你不需要阅读内容,如果用户想要得到确切的数字-你读取内容并返回一个确切的数字-与一个明确的条件,这个过程可能需要一些时间。