如何使用 .NET 获得人类可读的文件大小(以字节为单位)缩写?

How do I get a human-readable file size in bytes abbreviation using .NET?

提问人:Larsenal 提问时间:11/12/2008 最后编辑:shA.tLarsenal 更新时间:11/20/2023 访问量:200159

问:

如何使用 .NET 获得人类可读的文件大小(以字节为单位)缩写?

示例: 输入 7,326,629 并显示 6.98 MB

C# .NET vb.net 文件大小 人类可读

评论

0赞 Kiquenet 7/18/2013
stackoverflow.com/questions/128618/c-file-size-format-provider 是怎么回事?
1赞 vapcguy 4/1/2015
stackoverflow.com/questions/14488796/......

答:

14赞 TcKs 11/12/2008 #1
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 );

评论

0赞 nawfal 2/9/2014
好答案。当文件大小太小时应该有问题,在这种情况下 / 1024 返回 0。您可以使用小数类型和调用或其他东西。Math.Ceiling
2赞 Peter Crabtree 11/12/2008 #2

我假设您正在寻找“1.4 MB”而不是“1468006 字节”?

我不认为在.NET中有一种内置的方法可以做到这一点。您只需要弄清楚哪个单位是合适的,并对其进行格式化。

编辑:这里有一些示例代码可以做到这一点:

http://www.codeproject.com/KB/cpp/formatsize.aspx

423赞 David Thibault 11/12/2008 #3

这可能不是最有效或最优化的方法,但如果你不熟悉对数数学,它更容易阅读,并且对于大多数方案来说应该足够快。

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]);

评论

16赞 Francois Botha 11/28/2010
我相信您可以使用 Math.Log 来确定顺序,而不是使用 while 循环。
1赞 Constantin 2/11/2011
@Francois博塔,确实是:)stackoverflow.com/questions/281640/......
23赞 Peter 8/9/2013
@Constantin这取决于操作系统?Windows 仍然将 1024 字节计数为 1 KB,1 MB = 1024 KB,就我个人而言,我想将 KiB 扔出窗外,只使用 1024 计算每一件事?...
7赞 ANeves 11/19/2013
@Petoj它不依赖于操作系统,则该定义与操作系统无关。来自维基百科:The unit was established by the International Electrotechnical Commission (IEC) in 1998 and has been accepted for use by all major standards organizations
3赞 Myke Black 9/8/2014
我更喜欢这个代码,因为它似乎运行得更快,但我稍微修改了它以允许不同的小数位数。较小的数字最好显示 2 位小数,例如 1.38MB,而较大的数字需要更少的小数,例如 246k 或 23.5KB:
9赞 bobwienholt 11/12/2008 #4
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]);

评论

0赞 TcKs 11/12/2008
您应该检查: while(size >= 1024 && s < 后缀。长度 )。
0赞 bobwienholt 11/12/2008
不。。。64 位有符号整数不能超出 ZB...它代表数字 2^70。
0赞 RandomNickName42 7/11/2009
我自己最喜欢这个答案,但这里的每个人都提出了非常低效的解决方案,你应该使用“尺寸 = 尺寸 >> 10”的转变比除法快得多......我认为在那里有额外的希腊语说明符是件好事,因为在不久的将来,一个可行的 DLR 函数将不需要“长尺寸......”你可能在 128 位矢量 CPU 上,或者可以容纳 ZB 和更大的;)
4赞 Pete 4/6/2010
在金属上进行 C 编码的时代,位移比除法更有效。您是否在 .NET 中进行了性能测试,以查看位移是否真的更有效?不久前,我查看了 xor-swap 的状态,发现它在 .NET 中实际上比使用临时变量更慢。
76赞 Bob 11/12/2008 #5
[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

评论

43赞 Bart 4/30/2011
我可能是个菜鸟,但用像 pinvoke 这样的巨型大炮来杀死那只鸭子是一个很大的误用。
31赞 Andrew 9/23/2011
这是资源管理器使用的吗?如果是这样,那么对于让人们将您向他们展示的文件大小与资源管理器显示的文件大小相匹配非常有用。
1赞 Matthew Lock 4/18/2017
@Bart菜鸟需要一段时间才能学会其中的智慧:“我们应该忘记小效率,比如说大约 97% 的时间:过早的优化是万恶之源”ubiquity.acm.org/article.cfm?id=1513451
2赞 Bart 4/18/2017
@Matthew我知道这句话,它是我的最爱之一。但我评论的重点不是解决效率问题,而是解决纯度问题。PInvoke 的继电器是我们安全管理世界中的最后也是终极武器。我们为什么要带来任何风险,有一天这个外部会失败或被删除,而我们已经完美地管理了这个任务的代码?我们是否应该依赖它来测试我们的代码?它会在 linux 上运行吗?等等等等。这么多额外的问题,我认为与投票得分最高的答案相比,没有潜在的收益。
2赞 Herohtar 1/1/2020
这绝对不是这样做的方法。如果您想完全匹配操作系统显示的大小,那么在非常特定的情况下,对于仅限 Windows 的程序,它可能会有一些用处;但是,在 Windows 10 中,该函数使用基数 10 而不是基数 2(1 KB = 1000 字节而不是 1024 字节),因此相同的代码将根据运行它的 Windows 版本生成不同的输出。最后,如果您正在编写跨平台代码,这是完全无用的。
24赞 Constantin 2/11/2011 #6

还有一种外观设计方法,没有任何类型的循环,并且支持负大小(对于文件大小增量之类的东西很有意义):

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);
    }
}
368赞 deepee1 2/12/2011 #7

使用日志来解决问题。

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.FloorConvert.ToInt32Floor

编辑2:
有一些关于负大小和0字节大小的评论,所以我更新了以处理这些情况。

评论

7赞 IvanL 8/10/2012
我想警告的是,虽然这个答案确实是一段简短的代码,但它并不是最优化的。我想让你看看@humbads发布的方法。我运行了微测试,通过这两种方法发送了 10 000 000 个随机生成的文件大小,这带来了他的方法快 ~30% 的数字。然而,我对他的方法进行了一些进一步的清理(不必要的任务和选角)。此外,我运行了一个负大小的测试(当您比较文件时),而 humbads 方法可以完美地处理此问题,此 Log 方法将引发异常!
1赞 dasheddot 1/5/2013
是的,您应该为负大小添加 Math.Abs。此外,如果大小正好为 0,则代码不会处理大小写。
0赞 Jayson Ragasa 12/10/2013
Math.Abs, Math.Floor, Math.Log, 转换为整数, Math.Round, Math.Pow, Math.Sign, 加法, 乘法, 除法?这不就是在处理器上制造了一个巨大的峰值吗?这可能比@humbads代码慢
0赞 BrunoLM 12/28/2013
失败(位置 = 102)double.MaxValue
5赞 NET3 5/13/2012 #8

所有溶液的混合物:-)

    /// <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
}
132赞 humbads 6/21/2012 #9

此处发布了所请求函数的经过测试和显著优化的版本:

C# 人类可读文件大小 - 优化函数

源代码:

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"));
}

评论

0赞 kspearrin 7/1/2017
很好的答案。谢谢,为什么不直接使用?Math.Abs
2赞 humbads 8/3/2017
(i < 0 ? -i : i) 比 Math.Abs 快约 15%。对于 100 万次调用,Math.Abs 在我的机器上慢了 0.5 毫秒——3.2 毫秒对 3.7 毫秒。
4赞 JohnC 7/2/2020
应该是“MiB”、“KiB”等吗?
0赞 humbads 7/3/2020
@JohnC,这取决于观众。对于常见用法,我会坚持使用“MB”、“GB”等,如果受众非常技术化,如开发人员、工程师等,我会将其更改为“MiB”、“GiB”等。
0赞 JohnC 7/4/2020
也许两者都使用开关并使用 1024 或 1000 作为开关;我现在在“现实世界”中越来越多地看到 MiB
1赞 Berend 9/13/2012 #10

我的 2 美分:

  • 千字节的前缀是 kB(小写 K)
  • 由于这些函数用于演示目的,因此应提供区域性,例如:string.Format(CultureInfo.CurrentCulture, "{0:0.##} {1}", fileSize, unit);
  • 根据上下文,千字节可以是 1000 或 1024 字节。MB、GB 等也是如此。

评论

3赞 Superbest 9/26/2012
千字节表示 1000 字节 (wolframalpha.com/input/?i=kilobyte),它不依赖于上下文。正如维基百科所说,它在历史上取决于上下文,它在 1998 年进行了法律上的更改,事实上的变化始于 2005 年左右,当时 TB 硬盘驱动器引起了公众的注意。1024 字节的术语是 kibibyte。根据区域性切换它们的代码会产生不正确的信息。
0赞 Magnetron 6/7/2021
@Superbest告诉 Windows。如果处于 Windows 上下文中,则 KB 为 1024,因此它取决于上下文。
2赞 Giles 2/25/2013 #11

还有一种方法,物有所值。我喜欢上面提到的@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);
    }
}
32赞 Omar 3/13/2014 #12

看看我的 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");
9赞 Jernej Novak 4/14/2014 #13

有一个开源项目可以做到这一点,甚至更多。

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

http://humanizr.net/#bytesize

https://github.com/MehdiK/Humanizer

16赞 Mark 8/5/2015 #14

我喜欢使用以下方法(它最多支持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) 编写的,因此可能需要对早期版本进行一些编辑。

7赞 Metalogic 3/8/2016 #15

如果尝试匹配 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)。

评论

0赞 TarmoPikaro 4/4/2016
此代码不会编译,您需要指定来自哪个函数的 dll。所以整个函数原型听起来是这样的: [DllImport(“shlwapi.dll”, CharSet = CharSet.Auto, SetLastError = true)] public static extern long StrFormatKBSize(long qdw, [MarshalAs(UnmanagedType.LPTStr)] StringBuilder pszBuf, int cchBuf);让我成为第一个支持这个解决方案的人。如果轮子已经发明了,为什么要重新发明轮子?这是所有 C# 程序员的典型方法,但不幸的是,C# 无法达到 C++ 达到的所有目标。
0赞 TarmoPikaro 4/4/2016
还有一个错误修复:Int64.MaxValue 达到 9,223,372,036,854,775,807,这需要分配 25+ 的缓冲区大小 - 我已将其四舍五入为 32 以防万一(而不是像上面的演示代码中的 11 那样)。
0赞 Metalogic 4/6/2016
谢谢@TarmoPikaro。当我从我的工作代码中复制时,我错过了 DllImport。还根据您的建议增加了缓冲区大小。好渔获!
0赞 jstuardo 8/16/2019
这仅显示 KB 单位。这个想法是根据值显示最大的单位。
0赞 Metalogic 8/17/2019
为此,您可以采用上面的代码并使用 .StrFormatKBSize()StrFormatByteSize()
5赞 alvinsay 6/7/2017 #16

就像@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]);
}
2赞 RooiWillie 10/18/2017 #17

一些递归怎么样:

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);

评论

1赞 soccer7 8/31/2018
很好,但它吃了 CPU
11赞 DKH 3/28/2018 #18

这是一个简洁的答案,可以自动确定单位。

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);
}

评论

1赞 Max R. 2/6/2019
对于想知道为什么会有 KMGTPE 之后的人:它的法语(是法语)。对于任何其他语言,只需将 替换为obyteoctetob
4赞 masterwok 11/21/2018 #19

我使用下面的 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";
}
0赞 Zombo 11/18/2020 #20

这是一个方法: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

2赞 Kim Homann 2/2/2021 #21

为了获得用户在其 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/

0赞 dkackman 9/6/2021 #22

这是 @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赞 David V McKay 3/22/2022 #23

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));
}
-2赞 SN74H74N 3/26/2022 #24

这个问题很老,但一个非常快的 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() 快得多。

评论

1赞 baltermia 3/29/2022
它可能很快,但我不会说它很现代。
0赞 SN74H74N 3/30/2022
@baltermia 你是对的。我想我指的是字符串插值,当提出原始问题时,这不是一回事。
0赞 albertovilches 9/10/2022
如果这是最快的解决方案,那么它应该包括性能测试,并将速度与其他解决方案进行比较。它应该得到每个人的赞成。但老实说,这个解决方案应该包括负/零大小写和 si 格式(使用 1000 或 1024)
0赞 user9575469 11/27/2022 #25

我编造了这个,它工作得很好。

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;
}

评论

1赞 MD Zand 11/30/2022
这个问题在4年多前就被问过,它已经接受了答案。请添加一些有关您添加新答案的原因的详细信息
1赞 12/1/2022
只是想帮忙。为您准备的功能,
0赞 Adrian Hum 7/21/2023 #26

经过大量数量级和小数点数的填充,我设法获得了一个与 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");
    }
}