从文本文件的大小(字节

时间:2015-09-02 08:26:11

标签: c# character-encoding text-files

好的,问题是:给定一个随机文本文件的FileInfo对象,并知道所述文件的编码(它可以是 ASCII UTF7 UTF8 Unicode 等)有没有办法在不读取文件的情况下获取文件的确切字符数?

您通过FileInfo.Length属性知道文件的大小(以字节为单位),因此理论上知道编码的CharSize,您应该能够获得字符数。

使用某些编码进行测试似乎有效( ASCII Unicode )但其他编码稍微偏离(例如 UTF8 )。

这一般是否可行,或者您是否必须阅读整个文件才能获得可靠的字符数?

2 个答案:

答案 0 :(得分:1)

一般情况下,如果不阅读整个内容,就不可能。

原因是编码不能保证char只需要N个字节。例如,默认的C#编码Unicode,又名UTF-16允许一些字符为2或4个字节(可能还有3个字符 - 不确定,请参阅本主题的another answer)。其他一些编码可能允许您给出确切的数字,如ASCII,通常为7(填充为8)或8位。

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

当您对用户进行估算时,您可以提供一个解决方案,这个解决方案会很快,因为您不需要阅读内容,如果用户想要获得确切的数字,您可以阅读内容并返回确切的数字 - 条件清楚,这个过程可能需要一些时间。

答案 1 :(得分:1)

问题

如前所述,如果没有阅读由variable-width character encoding引起的所有字符,则无法实现。

您所做的是通过假设所有字符都适合最小单位来近似字符数。对于UTF8UTF16等字符编码,如果文件中只有ASCII个字符,则会完全相同。

更好的近似值

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

解码速度更快

由于这里读取整个文件的速度很快,我会假设您要么处理大量文件,要么处理大量文件。两者都有自己的问题。如果这些都不是真的,那么无论如何都没有必要进行优化。

内存问题

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

缺点是C#没有提供一种有效的内置方法来计算流中的字符。我能想到的唯一内置解决方案是this SO answer中列出的解决方案。它确实考虑了代理,您可以指定编码。

速度问题

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

/// <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#中使用更有效的文件访问模式。无论你选择什么样的路径,它都会迅速变得毛茸茸。一般来说,除非您的申请的目的仅仅是计算文件中的字符数,否则我不会浪费我的时间。