从文本文件的大小(以字节为单位)获取可靠的字符数
Obtaining a reliable character count from a text file's size in bytes
好的,所以问题是:给定一个随机文本文件的 FileInfo
对象,并且知道该文件的编码(可以是 ASCII、UTF7, UTF8, Unicode, 等等)有没有办法得到文件的确切字符数而不读了吗?
您通过 FileInfo.Length
属性 知道文件的字节大小,所以理论上知道编码的 CharSize
您应该能够得到字符数。
使用某些编码进行测试似乎有效(ASCII、Unicode),但其他编码略有偏差(UTF8 例如)。
这在一般情况下是否可行,还是您必须阅读整个文件才能始终获得可靠的字符数?
一般情况下,不阅读全部内容是不可能的。
原因是编码并不能保证一个字符恰好占用 N 个字节。例如,默认的 C# 编码 Unicode,又名 UTF-16 允许一些字符为 2 或 4 个字节(也可能是 3 个字节 - 不确定,请参阅本主题的 another answer)。其他一些编码可能允许您给出准确的数字,例如 ASCII,通常为 7(填充为 8)或 8 位。
你可以得到一个很好的估计,但可能不是一个确切的数字。
你可以提供一个解决方案,当你给用户一个估计时,这会很快,因为你不需要阅读内容,如果用户想要得到确切的数字 - 你阅读内容并return 一个确切的数字 - 明确条件是此过程可能需要一些时间。
问题
如前所述,由于 variable-width character encoding.
,不读取所有字符是不可能的
您所做的是通过假设所有字符都适合最小单位来估算字符数。当文件中只有 ASCII
个字符时,这将完全适用于 UTF8
或 UTF16
等字符编码。
更好的近似值
如果您了解一种目标语言,则可以通过假设平均每个字符都是一定数量的字节来更好地近似字符。例如,对于 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);
}
回避问题
您可以尝试完全避免计算文件中的字符数,只需执行一次并存储它即可。这样你甚至不需要解码文件,但你需要做一些簿记。如果它经常查询 refresh/create 几次,这可能是您最好的方法。您可以保留一个包含文件名和字符数的缓存,然后查询它,而不是读取实际文件。
这是否是一个有效的解决方案完全取决于您的用例。
优化解码
如果您无法控制输入文件,并且它们可能过大或太多,您可以通过编写专门的代码获得重大收益。您可以使用 SIMD 和缓存优化达到 C。或者只是在 C# 中使用更高效的文件访问模式。无论您选择什么路径,它都会很快变得毛茸茸。一般来说,除非您的应用程序的目的仅仅是计算文件中的字符数,否则我不会在这上面浪费时间。
好的,所以问题是:给定一个随机文本文件的 FileInfo
对象,并且知道该文件的编码(可以是 ASCII、UTF7, UTF8, Unicode, 等等)有没有办法得到文件的确切字符数而不读了吗?
您通过 FileInfo.Length
属性 知道文件的字节大小,所以理论上知道编码的 CharSize
您应该能够得到字符数。
使用某些编码进行测试似乎有效(ASCII、Unicode),但其他编码略有偏差(UTF8 例如)。
这在一般情况下是否可行,还是您必须阅读整个文件才能始终获得可靠的字符数?
一般情况下,不阅读全部内容是不可能的。
原因是编码并不能保证一个字符恰好占用 N 个字节。例如,默认的 C# 编码 Unicode,又名 UTF-16 允许一些字符为 2 或 4 个字节(也可能是 3 个字节 - 不确定,请参阅本主题的 another answer)。其他一些编码可能允许您给出准确的数字,例如 ASCII,通常为 7(填充为 8)或 8 位。
你可以得到一个很好的估计,但可能不是一个确切的数字。
你可以提供一个解决方案,当你给用户一个估计时,这会很快,因为你不需要阅读内容,如果用户想要得到确切的数字 - 你阅读内容并return 一个确切的数字 - 明确条件是此过程可能需要一些时间。
问题
如前所述,由于 variable-width character encoding.
,不读取所有字符是不可能的您所做的是通过假设所有字符都适合最小单位来估算字符数。当文件中只有 ASCII
个字符时,这将完全适用于 UTF8
或 UTF16
等字符编码。
更好的近似值
如果您了解一种目标语言,则可以通过假设平均每个字符都是一定数量的字节来更好地近似字符。例如,对于 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);
}
回避问题
您可以尝试完全避免计算文件中的字符数,只需执行一次并存储它即可。这样你甚至不需要解码文件,但你需要做一些簿记。如果它经常查询 refresh/create 几次,这可能是您最好的方法。您可以保留一个包含文件名和字符数的缓存,然后查询它,而不是读取实际文件。
这是否是一个有效的解决方案完全取决于您的用例。
优化解码
如果您无法控制输入文件,并且它们可能过大或太多,您可以通过编写专门的代码获得重大收益。您可以使用 SIMD 和缓存优化达到 C。或者只是在 C# 中使用更高效的文件访问模式。无论您选择什么路径,它都会很快变得毛茸茸。一般来说,除非您的应用程序的目的仅仅是计算文件中的字符数,否则我不会在这上面浪费时间。