c# 中 fileinfo.length 的字节转可读大小应优先用 humanizer 库,它自动处理二进制/十进制单位切换、边界判断与本地化;若自行实现,须严格按 1024 阈值分级、避免错误四舍五入,并预先捕获 unauthorizedaccessexception 等异常防止负值参与格式化。

用 Math.DivideByZeroException?不,别自己除——用 Humanizer 库最稳
直接上结论:C# 没有内置的“字节→可读大小”格式化函数,FileInfo.Length 返回的是 long 字节数,硬除容易错单位、漏边界、丢精度。推荐用开源库 Humanizer,它处理了二进制(KiB)/十进制(KB)切换、小数位截断、本地化等所有细节。
实操建议:
- 安装:
dotnet add package Humanizer - 调用:
bytes.Bytes().ToString("0.##", byteFormat: ByteFormat.Binary)→ 输出"1.23 MiB" - 注意:默认是二进制(1024进制),要十进制(1000进制)得显式传
ByteFormat.Decimal - 常见错误:直接
bytes / 1024.0 / 1024.0后四舍五入,结果在 999.5–1023.9 字节区间会显示成"1.0 KB",但实际应是"1023 B"——Humanizer自动选最合适的单位
string.Format 或插值字符串手动算?可以,但必须守住三道线
如果项目不能引入第三方库,就得自己写。核心不是“怎么除”,而是“在哪切单位、怎么取舍、谁来决定小数位”。
关键逻辑点:
- 单位阈值必须严格按 1024 切:B
- 小数位只对非整数单位生效:1024 字节就是
"1 KiB",不是"1.00 KiB";而 1500 字节应是"1.46 KiB" - 避免
double累积误差:用decimal做中间计算,或用整数移位(如bytes >> 10算 KiB 整数部分) - 常见错误:用
Math.Round(value, 2)直接套在除完的结果上,导致 1023.999 显示为"1.00 KiB"(实际该显示"1023 B")
Windows API 的 StrFormatByteSize64 能用吗?能,但仅限桌面 Win32
这是 Windows 原生支持的格式化函数,返回带本地化单位(如中文“KB”“MB”)和千分位分隔符的字符串,Shell32.dll 导出。
适用场景有限:
- 只在 .NET Framework / .NET 5+ Windows 桌面应用中可用(
net6.0-windows或更高) - 跨平台项目(Linux/macOS)、Blazor、Unity IL2CPP 构建会失败
- 需要 P/Invoke 声明,且输入必须是
ulong;负值或超ulong.MaxValue会崩 - 错误现象:
System.EntryPointNotFoundException: Unable to find an entry point named 'StrFormatByteSize64' in DLL 'shell32.dll'—— 多见于 Nano Server 或容器环境
为什么 FileInfo.Length 有时是负数?这和格式化无关,但会影响你整个逻辑
FileInfo.Length 返回 long,但某些特殊文件(如符号链接指向不存在路径、NTFS 稀疏文件未初始化区域、权限不足的系统文件)可能抛 UnauthorizedAccessException 或返回 -1。这不是格式化问题,但如果你没捕获就直接传给格式化函数,会导致单位错乱(比如 -1 字节被转成 "-1 B" 或触发除零)。
安全做法:
- 永远先检查异常:用
try/catch包住fileInfo.Length访问 - 或改用异步方法
new FileInfo(path).GetAttributesAsync()预判可访问性 - 若长度为负,不要参与格式化,应返回占位符如
"?"或抛业务异常 - 容易被忽略的点:日志里看到
"-1 B",第一反应常是“格式化函数 bug”,其实是上游没兜住异常











