好的,问题是:给定一个随机文本文件的FileInfo
对象,并知道所述文件的编码(它可以是 ASCII , UTF7 , UTF8 , Unicode 等)有没有办法在不读取文件的情况下获取文件的确切字符数?
您通过FileInfo.Length
属性知道文件的大小(以字节为单位),因此理论上知道编码的CharSize
,您应该能够获得字符数。
使用某些编码进行测试似乎有效( ASCII , Unicode )但其他编码稍微偏离(例如 UTF8 )。
这一般是否可行,或者您是否必须阅读整个文件才能获得可靠的字符数?
答案 0 :(得分:1)
一般情况下,如果不阅读整个内容,就不可能。
原因是编码不能保证char只需要N个字节。例如,默认的C#编码Unicode,又名UTF-16允许一些字符为2或4个字节(可能还有3个字符 - 不确定,请参阅本主题的another answer)。其他一些编码可能允许您给出确切的数字,如ASCII,通常为7(填充为8)或8位。
你可以得到一个很好的估计,但可能不是一个确切的数字。
当您对用户进行估算时,您可以提供一个解决方案,这个解决方案会很快,因为您不需要阅读内容,如果用户想要获得确切的数字,您可以阅读内容并返回确切的数字 - 条件清楚,这个过程可能需要一些时间。
答案 1 :(得分:1)
如前所述,如果没有阅读由variable-width character encoding引起的所有字符,则无法实现。
您所做的是通过假设所有字符都适合最小单位来近似字符数。对于UTF8
或UTF16
等字符编码,如果文件中只有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#中使用更有效的文件访问模式。无论你选择什么样的路径,它都会迅速变得毛茸茸。一般来说,除非您的申请的目的仅仅是计算文件中的字符数,否则我不会浪费我的时间。