提问人:Larsenal 提问时间:11/12/2008 最后编辑:shA.tLarsenal 更新时间:11/20/2023 访问量:200159
如何使用 .NET 获得人类可读的文件大小(以字节为单位)缩写?
How do I get a human-readable file size in bytes abbreviation using .NET?
答:
int size = new FileInfo( filePath ).Length / 1024;
string humanKBSize = string.Format( "{0} KB", size );
string humanMBSize = string.Format( "{0} MB", size / 1024 );
string humanGBSize = string.Format( "{0} GB", size / 1024 / 1024 );
评论
Math.Ceiling
我假设您正在寻找“1.4 MB”而不是“1468006 字节”?
我不认为在.NET中有一种内置的方法可以做到这一点。您只需要弄清楚哪个单位是合适的,并对其进行格式化。
编辑:这里有一些示例代码可以做到这一点:
http://www.codeproject.com/KB/cpp/formatsize.aspx
这可能不是最有效或最优化的方法,但如果你不熟悉对数数学,它更容易阅读,并且对于大多数方案来说应该足够快。
string[] sizes = { "B", "KB", "MB", "GB", "TB" };
double len = new FileInfo(filename).Length;
int order = 0;
while (len >= 1024 && order < sizes.Length - 1) {
order++;
len = len/1024;
}
// Adjust the format string to your preferences. For example "{0:0.#}{1}" would
// show a single decimal place, and no space.
string result = String.Format("{0:0.##} {1}", len, sizes[order]);
评论
The unit was established by the International Electrotechnical Commission (IEC) in 1998 and has been accepted for use by all major standards organizations
string[] suffixes = { "B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };
int s = 0;
long size = fileInfo.Length;
while (size >= 1024)
{
s++;
size /= 1024;
}
string humanReadable = String.Format("{0} {1}", size, suffixes[s]);
评论
[DllImport ( "Shlwapi.dll", CharSet = CharSet.Auto )]
public static extern long StrFormatByteSize (
long fileSize
, [MarshalAs ( UnmanagedType.LPTStr )] StringBuilder buffer
, int bufferSize );
/// <summary>
/// Converts a numeric value into a string that represents the number expressed as a size value in bytes, kilobytes, megabytes, or gigabytes, depending on the size.
/// </summary>
/// <param name="filelength">The numeric value to be converted.</param>
/// <returns>the converted string</returns>
public static string StrFormatByteSize (long filesize) {
StringBuilder sb = new StringBuilder( 11 );
StrFormatByteSize( filesize, sb, sb.Capacity );
return sb.ToString();
}
寄件人:http://www.pinvoke.net/default.aspx/shlwapi/StrFormatByteSize.html
评论
还有一种外观设计方法,没有任何类型的循环,并且支持负大小(对于文件大小增量之类的东西很有意义):
public static class Format
{
static string[] sizeSuffixes = {
"B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };
public static string ByteSize(long size)
{
Debug.Assert(sizeSuffixes.Length > 0);
const string formatTemplate = "{0}{1:0.#} {2}";
if (size == 0)
{
return string.Format(formatTemplate, null, 0, sizeSuffixes[0]);
}
var absSize = Math.Abs((double)size);
var fpPower = Math.Log(absSize, 1000);
var intPower = (int)fpPower;
var iUnit = intPower >= sizeSuffixes.Length
? sizeSuffixes.Length - 1
: intPower;
var normSize = absSize / Math.Pow(1000, iUnit);
return string.Format(
formatTemplate,
size < 0 ? "-" : null, normSize, sizeSuffixes[iUnit]);
}
}
下面是测试套件:
[TestFixture] public class ByteSize
{
[TestCase(0, Result="0 B")]
[TestCase(1, Result = "1 B")]
[TestCase(1000, Result = "1 KB")]
[TestCase(1500000, Result = "1.5 MB")]
[TestCase(-1000, Result = "-1 KB")]
[TestCase(int.MaxValue, Result = "2.1 GB")]
[TestCase(int.MinValue, Result = "-2.1 GB")]
[TestCase(long.MaxValue, Result = "9.2 EB")]
[TestCase(long.MinValue, Result = "-9.2 EB")]
public string Format_byte_size(long size)
{
return Format.ByteSize(size);
}
}
使用日志来解决问题。
static String BytesToString(long byteCount)
{
string[] suf = { "B", "KB", "MB", "GB", "TB", "PB", "EB" }; //Longs run out around EB
if (byteCount == 0)
return "0" + suf[0];
long bytes = Math.Abs(byteCount);
int place = Convert.ToInt32(Math.Floor(Math.Log(bytes, 1024)));
double num = Math.Round(bytes / Math.Pow(1024, place), 1);
return (Math.Sign(byteCount) * num).ToString() + suf[place];
}
同样在 C# 中,但应该很容易转换。此外,为了便于阅读,我四舍五入到小数点后 1 位。
基本上确定 Base 1024 中的小数位数,然后除以 。1024^decimalplaces
以及一些使用和输出示例:
Console.WriteLine(BytesToString(9223372036854775807)); //Results in 8EB
Console.WriteLine(BytesToString(0)); //Results in 0B
Console.WriteLine(BytesToString(1024)); //Results in 1KB
Console.WriteLine(BytesToString(2000000)); //Results in 1.9MB
Console.WriteLine(BytesToString(-9023372036854775807)); //Results in -7.8EB
编辑:
有人指出我错过了一个,所以我把它合并了。(使用舍入,而不是截断,这就是为什么有必要。谢谢你的收获。Math.Floor
Convert.ToInt32
Floor
编辑2:
有一些关于负大小和0字节大小的评论,所以我更新了以处理这些情况。
评论
double.MaxValue
所有溶液的混合物:-)
/// <summary>
/// Converts a numeric value into a string that represents the number expressed as a size value in bytes,
/// kilobytes, megabytes, or gigabytes, depending on the size.
/// </summary>
/// <param name="fileSize">The numeric value to be converted.</param>
/// <returns>The converted string.</returns>
public static string FormatByteSize(double fileSize)
{
FileSizeUnit unit = FileSizeUnit.B;
while (fileSize >= 1024 && unit < FileSizeUnit.YB)
{
fileSize = fileSize / 1024;
unit++;
}
return string.Format("{0:0.##} {1}", fileSize, unit);
}
/// <summary>
/// Converts a numeric value into a string that represents the number expressed as a size value in bytes,
/// kilobytes, megabytes, or gigabytes, depending on the size.
/// </summary>
/// <param name="fileInfo"></param>
/// <returns>The converted string.</returns>
public static string FormatByteSize(FileInfo fileInfo)
{
return FormatByteSize(fileInfo.Length);
}
}
public enum FileSizeUnit : byte
{
B,
KB,
MB,
GB,
TB,
PB,
EB,
ZB,
YB
}
此处发布了所请求函数的经过测试和显著优化的版本:
源代码:
public static string BytesToString(long value)
{
string suffix;
double readable;
switch (Math.Abs(value))
{
case >= 0x1000000000000000:
suffix = "EiB";
readable = value >> 50;
break;
case >= 0x4000000000000:
suffix = "PiB";
readable = value >> 40;
break;
case >= 0x10000000000:
suffix = "TiB";
readable = value >> 30;
break;
case >= 0x40000000:
suffix = "GiB";
readable = value >> 20;
break;
case >= 0x100000:
suffix = "MiB";
readable = value >> 10;
break;
case >= 0x400:
suffix = "KiB";
readable = value;
break;
default:
return value.ToString("0 B");
}
return (readable / 1024).ToString("0.## ", CultureInfo.InvariantCulture) + suffix;
}
和测试
[Test]
public void Should_convert_bytes_to_human_string()
{
Assert.That(BytesToString(0L), Is.EqualTo("0 B"));
Assert.That(BytesToString(1_024L), Is.EqualTo("1 KiB"));
Assert.That(BytesToString(1_024L * 1_024), Is.EqualTo("1 MiB"));
Assert.That(BytesToString(1_024L * 1_024 * 1_024), Is.EqualTo("1 GiB"));
Assert.That(BytesToString(1_024L * 1_024 * 1_024 * 1_024), Is.EqualTo("1 TiB"));
Assert.That(BytesToString(1_024L * 1_024 * 1_024 * 1_024 * 1_024), Is.EqualTo("1 PiB"));
Assert.That(BytesToString(1_024L * 1_024 * 1_024 * 1_024 * 1_024 * 1_024), Is.EqualTo("1 EiB"));
Assert.That(BytesToString(5_823_996_738L), Is.EqualTo("5.42 GiB"));
Assert.That(BytesToString(long.MaxValue), Is.EqualTo("8 EiB"));
Assert.That(BytesToString(long.MinValue+1), Is.EqualTo("-8 EiB"));
}
评论
Math.Abs
我的 2 美分:
- 千字节的前缀是 kB(小写 K)
- 由于这些函数用于演示目的,因此应提供区域性,例如:
string.Format(CultureInfo.CurrentCulture, "{0:0.##} {1}", fileSize, unit);
- 根据上下文,千字节可以是 1000 或 1024 字节。MB、GB 等也是如此。
评论
还有一种方法,物有所值。我喜欢上面提到的@humbads优化解决方案,因此复制了该原理,但我的实现方式略有不同。
我想关于它是否应该是一个扩展方法还有待商榷(因为并非所有的 long 都一定是字节大小),但我喜欢它们,而且当我下次需要它时,我可以找到它!
关于单位,我认为我一生中从未说过“Kibibyte”或“Mebibyte”,虽然我对这种强制执行而不是进化的标准持怀疑态度,但我想从长远来看,这将避免混淆。
public static class LongExtensions
{
private static readonly long[] numberOfBytesInUnit;
private static readonly Func<long, string>[] bytesToUnitConverters;
static LongExtensions()
{
numberOfBytesInUnit = new long[6]
{
1L << 10, // Bytes in a Kibibyte
1L << 20, // Bytes in a Mebibyte
1L << 30, // Bytes in a Gibibyte
1L << 40, // Bytes in a Tebibyte
1L << 50, // Bytes in a Pebibyte
1L << 60 // Bytes in a Exbibyte
};
// Shift the long (integer) down to 1024 times its number of units, convert to a double (real number),
// then divide to get the final number of units (units will be in the range 1 to 1023.999)
Func<long, int, string> FormatAsProportionOfUnit = (bytes, shift) => (((double)(bytes >> shift)) / 1024).ToString("0.###");
bytesToUnitConverters = new Func<long,string>[7]
{
bytes => bytes.ToString() + " B",
bytes => FormatAsProportionOfUnit(bytes, 0) + " KiB",
bytes => FormatAsProportionOfUnit(bytes, 10) + " MiB",
bytes => FormatAsProportionOfUnit(bytes, 20) + " GiB",
bytes => FormatAsProportionOfUnit(bytes, 30) + " TiB",
bytes => FormatAsProportionOfUnit(bytes, 40) + " PiB",
bytes => FormatAsProportionOfUnit(bytes, 50) + " EiB",
};
}
public static string ToReadableByteSizeString(this long bytes)
{
if (bytes < 0)
return "-" + Math.Abs(bytes).ToReadableByteSizeString();
int counter = 0;
while (counter < numberOfBytesInUnit.Length)
{
if (bytes < numberOfBytesInUnit[counter])
return bytesToUnitConverters[counter](bytes);
counter++;
}
return bytesToUnitConverters[counter](bytes);
}
}
看看我的 ByteSize 库。这是字节!System.TimeSpan
它为您处理转换和格式化。
var maxFileSize = ByteSize.FromKiloBytes(10);
maxFileSize.Bytes;
maxFileSize.MegaBytes;
maxFileSize.GigaBytes;
它还执行字符串表示和解析。
// ToString
ByteSize.FromKiloBytes(1024).ToString(); // 1 MB
ByteSize.FromGigabytes(.5).ToString(); // 512 MB
ByteSize.FromGigabytes(1024).ToString(); // 1 TB
// Parsing
ByteSize.Parse("5b");
ByteSize.Parse("1.55B");
有一个开源项目可以做到这一点,甚至更多。
7.Bits().ToString(); // 7 b
8.Bits().ToString(); // 1 B
(.5).Kilobytes().Humanize(); // 512 B
(1000).Kilobytes().ToString(); // 1000 KB
(1024).Kilobytes().Humanize(); // 1 MB
(.5).Gigabytes().Humanize(); // 512 MB
(1024).Gigabytes().ToString(); // 1 TB
https://github.com/MehdiK/Humanizer
我喜欢使用以下方法(它最多支持TB,这对于大多数情况来说已经足够了,但它可以很容易地扩展):
private string GetSizeString(long length)
{
long B = 0, KB = 1024, MB = KB * 1024, GB = MB * 1024, TB = GB * 1024;
double size = length;
string suffix = nameof(B);
if (length >= TB) {
size = Math.Round((double)length / TB, 2);
suffix = nameof(TB);
}
else if (length >= GB) {
size = Math.Round((double)length / GB, 2);
suffix = nameof(GB);
}
else if (length >= MB) {
size = Math.Round((double)length / MB, 2);
suffix = nameof(MB);
}
else if (length >= KB) {
size = Math.Round((double)length / KB, 2);
suffix = nameof(KB);
}
return $"{size} {suffix}";
}
请记住,这是为 C# 6.0 (2015) 编写的,因此可能需要对早期版本进行一些编辑。
如果尝试匹配 Windows 资源管理器的详细信息视图中显示的大小,则所需的代码如下:
[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
private static extern long StrFormatKBSize(
long qdw,
[MarshalAs(UnmanagedType.LPTStr)] StringBuilder pszBuf,
int cchBuf);
public static string BytesToString(long byteCount)
{
var sb = new StringBuilder(32);
StrFormatKBSize(byteCount, sb, sb.Capacity);
return sb.ToString();
}
这不仅会完全匹配资源管理器,还会提供为你翻译的字符串,并匹配 Windows 版本中的差异(例如,在 Win10 中,K = 1000 与以前的版本 K = 1024)。
评论
StrFormatKBSize()
StrFormatByteSize()
就像@NET3的解决方案一样。使用 shift 而不是除法来测试 的范围,因为除法需要更多的 CPU 成本。bytes
private static readonly string[] UNITS = new string[] { "B", "KB", "MB", "GB", "TB", "PB", "EB" };
public static string FormatSize(ulong bytes)
{
int c = 0;
for (c = 0; c < UNITS.Length; c++)
{
ulong m = (ulong)1 << ((c + 1) * 10);
if (bytes < m)
break;
}
double n = bytes / (double)((ulong)1 << (c * 10));
return string.Format("{0:0.##} {1}", n, UNITS[c]);
}
一些递归怎么样:
private static string ReturnSize(double size, string sizeLabel)
{
if (size > 1024)
{
if (sizeLabel.Length == 0)
return ReturnSize(size / 1024, "KB");
else if (sizeLabel == "KB")
return ReturnSize(size / 1024, "MB");
else if (sizeLabel == "MB")
return ReturnSize(size / 1024, "GB");
else if (sizeLabel == "GB")
return ReturnSize(size / 1024, "TB");
else
return ReturnSize(size / 1024, "PB");
}
else
{
if (sizeLabel.Length > 0)
return string.Concat(size.ToString("0.00"), sizeLabel);
else
return string.Concat(size.ToString("0.00"), "Bytes");
}
}
然后你称它为:
return ReturnSize(size, string.Empty);
评论
这是一个简洁的答案,可以自动确定单位。
public static string ToBytesCount(this long bytes)
{
int unit = 1024;
string unitStr = "B";
if (bytes < unit)
{
return string.Format("{0} {1}", bytes, unitStr);
}
int exp = (int)(Math.Log(bytes) / Math.Log(unit));
return string.Format("{0:##.##} {1}{2}", bytes / Math.Pow(unit, exp), "KMGTPEZY"[exp - 1], unitStr);
}
“b”代表比特,“B”代表字节,“KMGTPEZY”分别代表公斤、兆、千兆、tera、peta、exa、zetta 和 yotta
可以将其扩展以考虑 ISO/IEC80000:
public static string ToBytesCount(this long bytes, bool isISO = true)
{
int unit = isISO ? 1024 : 1000;
string unitStr = "B";
if (bytes < unit)
{
return string.Format("{0} {1}", bytes, unitStr);
}
int exp = (int)(Math.Log(bytes) / Math.Log(unit));
return string.Format("{0:##.##} {1}{2}{3}", bytes / Math.Pow(unit, exp), "KMGTPEZY"[exp - 1], isISO ? "i" : "", unitStr);
}
评论
o
byte
octet
o
b
我使用下面的 Long 扩展方法转换为人类可读的大小字符串。此方法是 Stack Overflow 上发布的同一问题的 Java 解决方案的 C# 实现,请点击此处。
/// <summary>
/// Convert a byte count into a human readable size string.
/// </summary>
/// <param name="bytes">The byte count.</param>
/// <param name="si">Whether or not to use SI units.</param>
/// <returns>A human readable size string.</returns>
public static string ToHumanReadableByteCount(
this long bytes
, bool si
)
{
var unit = si
? 1000
: 1024;
if (bytes < unit)
{
return $"{bytes} B";
}
var exp = (int) (Math.Log(bytes) / Math.Log(unit));
return $"{bytes / Math.Pow(unit, exp):F2} " +
$"{(si ? "kMGTPE" : "KMGTPE")[exp - 1] + (si ? string.Empty : "i")}B";
}
这是一个方法:Log10
using System;
class Program {
static string NumberFormat(double n) {
var n2 = (int)Math.Log10(n) / 3;
var n3 = n / Math.Pow(1e3, n2);
return String.Format("{0:f3}", n3) + new[]{"", " k", " M", " G"}[n2];
}
static void Main() {
var s = NumberFormat(9012345678);
Console.WriteLine(s == "9.012 G");
}
}
https://learn.microsoft.com/dotnet/api/system.math.log10
为了获得用户在其 Windows 环境中习惯的人类可读字符串,您应该使用:StrFormatByteSize()
using System.Runtime.InteropServices;
...
private long mFileSize;
[DllImport("Shlwapi.dll", CharSet = CharSet.Auto)]
public static extern int StrFormatByteSize(
long fileSize,
[MarshalAs(UnmanagedType.LPTStr)] StringBuilder buffer,
int bufferSize);
public string HumanReadableFileSize
{
get
{
var sb = new StringBuilder(20);
StrFormatByteSize(mFileSize, sb, 20);
return sb.ToString();
}
}
我在这里找到了这个:http://csharphelper.com/blog/2014/07/format-file-sizes-in-kb-mb-gb-and-so-forth-in-c/
这是 @deepee1 答案的 BigInteger 版本,它绕过了 long 的大小限制(因此支持 yottabyte,理论上支持 yottabyte,理论上支持之后的任何内容):
public static string ToBytesString(this BigInteger byteCount, string format = "N3")
{
string[] suf = { "B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "YiB" };
if (byteCount.IsZero)
{
return $"{0.0.ToString(format)} {suf[0]}";
}
var abs = BigInteger.Abs(byteCount);
var place = Convert.ToInt32(Math.Floor(BigInteger.Log(abs, 1024)));
var pow = Math.Pow(1024, place);
// since we need to do this with integer math, get the quotient and remainder
var quotient = BigInteger.DivRem(abs, new BigInteger(pow), out var remainder);
// convert the remainder to a ratio and add both back together as doubles
var num = byteCount.Sign * (Math.Floor((double)quotient) + ((double)remainder / pow));
return $"{num.ToString(format)} {suf[place]}";
}
1 行(加上前缀常量)
const String prefixes = " KMGTPEY";
/// <summary> Returns the human-readable file size for an arbitrary, 64-bit file size. </summary>
public static String HumanSize(UInt64 bytes)
=> Enumerable
.Range(0, prefixes.Length)
.Where(i => bytes < 1024U<<(i*10))
.Select(i => $"{(bytes>>(10*i-10))/1024:0.###} {prefixes[i]}B")
.First();
或者,如果要减少 LINQ 对象分配,请使用相同的 for 循环变体:
/// <summary>
/// Returns the human-readable file size for an arbitrary, 64-bit file size.
/// </summary>
public static String HumanSize(UInt64 bytes)
{
const String prefixes = " KMGTPEY";
for (var i = 0; i < prefixes.Length; i++)
if (bytes < 1024U<<(i*10))
return $"{(bytes>>(10*i-10))/1024:0.###} {prefixes[i]}B";
throw new ArgumentOutOfRangeException(nameof(bytes));
}
这个问题很老,但一个非常快的 C# 函数可能是:
public static string PrettyPrintBytes(long numBytes)
{
if (numBytes < 1024)
return $"{numBytes} B";
if (numBytes < 1048576)
return $"{numBytes / 1024d:0.##} KB";
if (numBytes < 1073741824)
return $"{numBytes / 1048576d:0.##} MB";
if (numBytes < 1099511627776)
return $"{numBytes / 1073741824d:0.##} GB";
if (numBytes < 1125899906842624)
return $"{numBytes / 1099511627776d:0.##} TB";
if (numBytes < 1152921504606846976)
return $"{numBytes / 1125899906842624d:0.##} PB";
return $"{numBytes / 1152921504606846976d:0.##} EB";
}
每次调用只有一个施法和一次除法,最多只有 6 个比较。在基准测试时,我发现字符串插值比使用 String.Format() 快得多。
评论
我编造了这个,它工作得很好。
public string[] DetermineDigitalSize(string filename)
{
string[] result = new string[2];
string[] sizes = { "B", "KB", "MB", "GB", "GB" };
double len = new FileInfo(filename).Length;
double adjustedSize = len;
double testSize = 0;
int order = 0;
while (order< sizes.Length-1)
{
testSize = adjustedSize / 1024;
if (testSize >= 1) { adjustedSize = testSize; order++; }
else { break; }
}
result[0] = $"{adjustedSize:f2}";
result[1] = sizes[order];
return result;
}
评论
经过大量数量级和小数点数的填充,我设法获得了一个与 WIN32.dll 库中的窗口功能非常相似的函数......
private string FormatBytesWithPrefix(double bytes)
{
string[] sizes = { "B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };
int order = 0;
double size = bytes;
while (size >= 1024 && order < sizes.Length - 1)
{
order++;
size /= 1024;
}
return $"{FormatNumberToThreeDigits(size)} {sizes[order]}";
}
private string FormatNumberToThreeDigits(double value)
{
if (value < 10)
{
// Format numbers less than 10 with 2 decimal places, rounded
return Math.Round(value, 2).ToString("F2");
}
else if (value < 100)
{
// Format numbers between 10 and 99 with 1 decimal place, rounded
return Math.Round(value, 1).ToString("F1");
}
else
{
// Format numbers 100 and above as whole value
return Math.Round(value).ToString("F0");
}
}
评论