.NET 是否提供了一种将字节转换为 KB、MB、GB 等的简单方法

本文关键字:KB 转换 MB GB 方法 简单 字节 是否 一种 NET | 更新日期: 2023-09-27 17:55:23

只是想知道.NET是否提供了一种干净的方法来做到这一点:

int64 x = 1000000;
string y = null;
if (x / 1024 == 0) {
    y = x + " bytes";
}
else if (x / (1024 * 1024) == 0) {
    y = string.Format("{0:n1} KB", x / 1024f);
}

等。。。

.NET 是否提供了一种将字节转换为 KB、MB、GB 等的简单方法

这是一个相当简洁的方法:

static readonly string[] SizeSuffixes = 
                   { "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };
static string SizeSuffix(Int64 value, int decimalPlaces = 1)
{
    if (decimalPlaces < 0) { throw new ArgumentOutOfRangeException("decimalPlaces"); }
    if (value < 0) { return "-" + SizeSuffix(-value, decimalPlaces); } 
    if (value == 0) { return string.Format("{0:n" + decimalPlaces + "} bytes", 0); }
    // mag is 0 for bytes, 1 for KB, 2, for MB, etc.
    int mag = (int)Math.Log(value, 1024);
    // 1L << (mag * 10) == 2 ^ (10 * mag) 
    // [i.e. the number of bytes in the unit corresponding to mag]
    decimal adjustedSize = (decimal)value / (1L << (mag * 10));
    // make adjustment when the value is large enough that
    // it would round up to 1000 or more
    if (Math.Round(adjustedSize, decimalPlaces) >= 1000)
    {
        mag += 1;
        adjustedSize /= 1024;
    }
    return string.Format("{0:n" + decimalPlaces + "} {1}", 
        adjustedSize, 
        SizeSuffixes[mag]);
}

这是我建议的原始实现,它可能稍微慢一些,但更容易遵循:

static readonly string[] SizeSuffixes = 
                  { "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };
static string SizeSuffix(Int64 value, int decimalPlaces = 1)
{
    if (value < 0) { return "-" + SizeSuffix(-value, decimalPlaces); } 
    int i = 0;
    decimal dValue = (decimal)value;
    while (Math.Round(dValue, decimalPlaces) >= 1000)
    {
        dValue /= 1024;
        i++;
    }
    return string.Format("{0:n" + decimalPlaces + "} {1}", dValue, SizeSuffixes[i]);
}
Console.WriteLine(SizeSuffix(100005000L));

要记住的一件事 - 在 SI 表示法中,"kilo"通常使用小写 k,而所有较大的单位都使用大写字母。Windows使用KB,MB,GB,所以我使用了上面的KB,但你可以考虑kB。

签出 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");

我会使用 Extension methodsMath.Pow 函数和 Enums 来解决它:

public static class MyExtension
{
    public enum SizeUnits
    {
        Byte, KB, MB, GB, TB, PB, EB, ZB, YB
    }
    public static string ToSize(this Int64 value, SizeUnits unit)
    {
        return (value / (double)Math.Pow(1024, (Int64)unit)).ToString("0.00");
    }
}

并像这样使用它:

string h = x.ToSize(MyExtension.SizeUnits.KB);

由于其他人都在发布他们的方法,我想我会发布我通常用于此的扩展方法:

编辑:添加了整数/长变体...并修复了一个复制粘贴错别字...

public static class Ext
{
    private const long OneKb = 1024;
    private const long OneMb = OneKb * 1024;
    private const long OneGb = OneMb * 1024;
    private const long OneTb = OneGb * 1024;
    public static string ToPrettySize(this int value, int decimalPlaces = 0)
    {
        return ((long)value).ToPrettySize(decimalPlaces);
    }
    public static string ToPrettySize(this long value, int decimalPlaces = 0)
    {
        var asTb = Math.Round((double)value / OneTb, decimalPlaces);
        var asGb = Math.Round((double)value / OneGb, decimalPlaces);
        var asMb = Math.Round((double)value / OneMb, decimalPlaces);
        var asKb = Math.Round((double)value / OneKb, decimalPlaces);
        string chosenValue = asTb > 1 ? string.Format("{0}Tb",asTb)
            : asGb > 1 ? string.Format("{0}Gb",asGb)
            : asMb > 1 ? string.Format("{0}Mb",asMb)
            : asKb > 1 ? string.Format("{0}Kb",asKb)
            : string.Format("{0}B", Math.Round((double)value, decimalPlaces));
        return chosenValue;
    }
}

我知道这已经是旧线程了。 但也许有人会寻找解决方案。这是我使用的和最简单的方法

public static string FormatFileSize(long bytes)
{
    var unit = 1024;
    if (bytes < unit) { return $"{bytes} B"; }
    var exp = (int)(Math.Log(bytes) / Math.Log(unit));
    return $"{bytes / Math.Pow(unit, exp):F2} {("KMGTPE")[exp - 1]}B";
}

获取文件夹大小(例如使用情况)

public static long GetFolderSize(string path, string ext, bool AllDir)
{
    var option = AllDir ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
    return new DirectoryInfo(path).EnumerateFiles("*" + ext, option).Sum(file => file.Length);
}

用法示例:

public static void TEST()
{
    string folder = @"C:'Users'User'Videos";
    var bytes = GetFolderSize(folder, "mp4", true); //or GetFolderSize(folder, "mp4", false) to get all single folder only
    var totalFileSize = FormatFileSize(bytes);
    Console.WriteLine(totalFileSize);
}

投票最多的答案的简短版本存在 TB 值问题。

我适当地调整了它以处理 tb 值并且仍然没有循环,并且还添加了对负值的一些错误检查。这是我的解决方案:

static readonly string[] SizeSuffixes = { "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };
static string SizeSuffix(long value, int decimalPlaces = 0)
{
    if (value < 0)
    {
        throw new ArgumentException("Bytes should not be negative", "value");
    }
    var mag = (int)Math.Max(0, Math.Log(value, 1024));
    var adjustedSize = Math.Round(value / Math.Pow(1024, mag), decimalPlaces);
    return String.Format("{0} {1}", adjustedSize, SizeSuffixes[mag]);
}

针对 C# 9.0 关系模式进行了更新

public const long OneKB = 1024;
public const long OneMB = OneKB * OneKB;
public const long OneGB = OneMB * OneKB;
public const long OneTB = OneGB * OneKB;
public static string BytesToHumanReadable(ulong bytes)
{
    return bytes switch
    {
        (< OneKB) => $"{bytes}B",
        (>= OneKB) and (< OneMB) => $"{bytes / OneKB}KB",
        (>= OneMB) and (< OneGB) => $"{bytes / OneMB}MB",
        (>= OneGB) and (< OneTB) => $"{bytes / OneMB}GB",
        (>= OneTB) => $"{bytes / OneTB}"
        //...
    };
}

@Servy的回答很好,很简洁。 我认为它可以更简单吗?

private static string[] suffixes = new [] { " B", " KB", " MB", " GB", " TB", " PB" };
public static string ToSize(double number, int precision = 2)
{
    // unit's number of bytes
    const double unit = 1024;
    // suffix counter
    int i = 0;
    // as long as we're bigger than a unit, keep going
    while(number > unit)
    {
        number /= unit;
        i++;
    }
    // apply precision and current suffix
    return Math.Round(number, precision) + suffixes[i];
}

No. 主要是因为它的需求相当小众,并且有太多可能的变化。 (是"KB"、"Kb"还是"Ko"? 兆字节是 1024 * 1024 字节,还是 1024 * 1000 字节?-是的,有些地方使用它!

这是一个比你的更容易扩展的选项,但不,库本身没有内置。

private static List<string> suffixes = new List<string> { " B", " KB", " MB", " GB", " TB", " PB" };
public static string Foo(int number)
{
    for (int i = 0; i < suffixes.Count; i++)
    {
        int temp = number / (int)Math.Pow(1024, i + 1);
        if (temp == 0)
            return (number / (int)Math.Pow(1024, i)) + suffixes[i];
    }
    return number.ToString();
}
    private string GetFileSize(double byteCount)
    {
        string size = "0 Bytes";
        if (byteCount >= 1073741824.0)
            size = String.Format("{0:##.##}", byteCount / 1073741824.0) + " GB";
        else if (byteCount >= 1048576.0)
            size = String.Format("{0:##.##}", byteCount / 1048576.0) + " MB";
        else if (byteCount >= 1024.0)
            size = String.Format("{0:##.##}", byteCount / 1024.0) + " KB";
        else if (byteCount > 0 && byteCount < 1024.0)
            size = byteCount.ToString() + " Bytes";
        return size;
    }
    private void btnBrowse_Click(object sender, EventArgs e)
    {
        if (openFile1.ShowDialog() == DialogResult.OK)
        {
            FileInfo thisFile = new FileInfo(openFile1.FileName);
            string info = "";
            info += "File: " + Path.GetFileName(openFile1.FileName);
            info += Environment.NewLine;
            info += "File Size: " + GetFileSize((int)thisFile.Length);
            label1.Text = info;
        }
    }

这也是一种方法(数字 1073741824.0 来自 1024*1024*1024 aka GB)

基于 NeverHopeless 的优雅解决方案:

private static readonly KeyValuePair<long, string>[] Thresholds = 
{
    // new KeyValuePair<long, string>(0, " Bytes"), // Don't devide by Zero!
    new KeyValuePair<long, string>(1, " Byte"),
    new KeyValuePair<long, string>(2, " Bytes"),
    new KeyValuePair<long, string>(1024, " KB"),
    new KeyValuePair<long, string>(1048576, " MB"), // Note: 1024 ^ 2 = 1026 (xor operator)
    new KeyValuePair<long, string>(1073741824, " GB"),
    new KeyValuePair<long, string>(1099511627776, " TB"),
    new KeyValuePair<long, string>(1125899906842620, " PB"),
    new KeyValuePair<long, string>(1152921504606850000, " EB"),
    // These don't fit into a int64
    // new KeyValuePair<long, string>(1180591620717410000000, " ZB"), 
    // new KeyValuePair<long, string>(1208925819614630000000000, " YB") 
};
/// <summary>
/// Returns x Bytes, kB, Mb, etc... 
/// </summary>
public static string ToByteSize(this long value)
{
    if (value == 0) return "0 Bytes"; // zero is plural
    for (int t = Thresholds.Length - 1; t > 0; t--)
        if (value >= Thresholds[t].Key) return ((double)value / Thresholds[t].Key).ToString("0.00") + Thresholds[t].Value;
    return "-" + ToByteSize(-value); // negative bytes (common case optimised to the end of this routine)
}

也许有过多的评论,但我倾向于留下它们,以防止自己在以后的访问中犯同样的错误......

No.

但是你可以像这样实现;

    static double ConvertBytesToMegabytes(long bytes)
    {
    return (bytes / 1024f) / 1024f;
    }
    static double ConvertKilobytesToMegabytes(long kilobytes)
    {
    return kilobytes / 1024f;
    }

另请查看 如何将以字节为单位的文件大小正确转换为兆字节或千兆字节?

我将这里的一些答案组合成两种效果很好的方法。下面的第二种方法将从字节字符串(如 1.5.1 GB)转换回字节(如 1621350140)作为长类型值。我希望这对寻找将字节转换为字符串并重新转换为字节的解决方案的其他人有用。

public static string BytesAsString(float bytes)
{
    string[] suffix = { "B", "KB", "MB", "GB", "TB" };
    int i;
    double doubleBytes = 0;
    for (i = 0; (int)(bytes / 1024) > 0; i++, bytes /= 1024)
    {
        doubleBytes = bytes / 1024.0;
    }
    return string.Format("{0:0.00} {1}", doubleBytes, suffix[i]);
}
public static long StringAsBytes(string bytesString)
{
    if (string.IsNullOrEmpty(bytesString))
    {
        return 0;
    }
    const long OneKb = 1024;
    const long OneMb = OneKb * 1024;
    const long OneGb = OneMb * 1024;
    const long OneTb = OneGb * 1024;
    double returnValue;
    string suffix = string.Empty;
    if (bytesString.IndexOf(" ") > 0)
    {
        returnValue = float.Parse(bytesString.Substring(0, bytesString.IndexOf(" ")));
        suffix = bytesString.Substring(bytesString.IndexOf(" ") + 1).ToUpperInvariant();
    }
    else
    {
        returnValue = float.Parse(bytesString.Substring(0, bytesString.Length - 2));
        suffix = bytesString.ToUpperInvariant().Substring(bytesString.Length - 2);
    }
    switch (suffix)
    {
        case "KB":
            {
                returnValue *= OneKb;
                break;
            }
        case "MB":
            {
                returnValue *= OneMb;
                break;
            }
        case "GB":
            {
                returnValue *= OneGb;
                break;
            }
        case "TB":
            {
                returnValue *= OneTb;
                break;
            }
        default:
            {
                break;
            }
    }
    return Convert.ToInt64(returnValue);
}

我选择了JerKimballs解决方案,并对此竖起了大拇指。但是,我想补充/指出,这确实是一个整体争议的问题。在我的研究中(出于其他原因),我提出了以下信息。

当普通人(我听说他们存在)谈到千兆字节时,他们指的是公制系统,其中 1000 从原始字节数 = = 千兆字节数的 3 次方。但是,当然还有IEC/JEDEC标准,这些标准在维基百科中得到了很好的总结,而不是1000的x的幂,而是1024。对于物理存储设备(我想是逻辑上的,例如亚马逊和其他设备)意味着公制与IEC之间的差异越来越大。因此,例如 1 TB == 1 TB 指标是 1000 的 4 次方,但 IEC 正式将类似的数字称为 1 TiB,将 1024 的 4 次方。但是,唉,在非技术应用程序中(我会按受众选择),规范是公制,在我自己的内部使用应用程序中,我解释了文档的差异。但出于显示目的,我什至不提供除公制之外的任何东西。在内部,即使它与我的应用程序无关,我也只存储字节并进行显示计算。

作为旁注,我发现 .Net 框架 AFAIK(我经常错了,感谢它的力量)即使在它的 4.5 版本中也没有在任何内部库中包含任何关于此内容的内容,这有点乏善可陈。人们会期望某种开源库在某个时候是NuGettable的,但我承认这是一个小烦恼。另一方面,System.IO.DriveInfo和其他也只有字节(一样长),这是相当清楚的。

一些递归怎么样:

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

然后你可以称之为:

ReturnSize(size, string.Empty);

我最近需要这个,并且需要将字节转换为长整型数字。

用法:Byte.Kb.ToLong(1)应该给1024。

public enum Byte
{
    Kb,
    Mb,
    Gb,
    Tb
}
public static class ByteSize
{
    private const long OneKb = 1024;
    private const long OneMb = OneKb * 1024;
    private const long OneGb = OneMb * 1024;
    private const long OneTb = OneGb * 1024;
    public static long ToLong(this Byte size, int value)
    {
        return size switch
        {
            Byte.Kb => value * OneKb,
            Byte.Mb => value * OneMb,
            Byte.Gb => value * OneGb,
            Byte.Tb => value * OneTb,
            _ => throw new NotImplementedException("This should never be hit.")
        };
    }
}
<小时 />

使用 xunit 进行测试:

[Theory]
[InlineData(Byte.Kb, 1, 1024)]
[InlineData(Byte.Kb, 2, 2048)]
[InlineData(Byte.Mb, 1, 1048576)]
[InlineData(Byte.Mb, 2, 2097152)]
[InlineData(Byte.Gb, 1, 1073741824)]
[InlineData(Byte.Gb, 2, 2147483648)]
[InlineData(Byte.Tb, 1, 1099511627776)]
[InlineData(Byte.Tb, 2, 2199023255552)]
public void ToLong_WhenConverting_ShouldMatchExpected(Byte size, int value, long expected)
{
    var result = size.ToLong(value);
    result.Should().Be(expected);
}

怎么样:

public void printMB(uint sizekB)   
{
    double sizeMB = (double) sizekB / 1024;
    Console.WriteLine("Size is " + sizeMB.ToString("0.00") + "MB");
}

例如,呼叫喜欢

printMB(123456);

将导致输出

"Size is 120,56 MB"
public static class MyExtension
{
    public static string ToPrettySize(this float Size)
    {
        return ConvertToPrettySize(Size, 0);
    }
    public static string ToPrettySize(this int Size)
    {
        return ConvertToPrettySize(Size, 0);
    }
    private static string ConvertToPrettySize(float Size, int R)
    {
        float F = Size / 1024f;
        if (F < 1)
        {
            switch (R)
            {
                case 0:
                    return string.Format("{0:0.00} byte", Size);
                case 1:
                    return string.Format("{0:0.00} kb", Size);
                case 2:
                    return string.Format("{0:0.00} mb", Size);
                case 3:
                    return string.Format("{0:0.00} gb", Size);
            }
        }
        return ConvertToPrettySize(F, ++R);
    }
}

如上所述,在对数的帮助下,递归是最喜欢的方式。

以下函数有 3 个参数:输入,输出的维度约束,即第三个参数。

int ByteReDim(unsigned long ival, int constraint, unsigned long *oval)
{
    int base = 1 + (int) log10(ival);
    (*oval) = ival;
    if (base > constraint) {
        (*oval) = (*oval) >> 10;
        return(1 + ByteReDim((*oval), constraint, oval));
    } else
        return(0);
}

现在让我们将 12GB 的 RAM 转换为几个单位:

int main(void)
{
    unsigned long RAM;
    int unit; // index of below symbols array
    char symbol[5] = {'B', 'K', 'M', 'G', 'T'};
    unit = ByteReDim(12884901888, 12, &RAM);
    printf("%lu%c'n", RAM, symbol[unit]); // output is 12884901888B
    unit = ByteReDim(12884901888, 9, &RAM);
    printf("%lu%c'n", RAM, symbol[unit]); // output is 12582912K
    unit = ByteReDim(12884901888, 6, &RAM);
    printf("%lu%c'n", RAM, symbol[unit]); // output is 12288M
    unit = ByteReDim(12884901888, 3, &RAM);
    printf("%lu%c'n", RAM, symbol[unit]); // output is 12G
}

我将其用于Windows(二进制前缀):

static readonly string[] BinaryPrefix = { "bytes", "KB", "MB", "GB", "TB" }; // , "PB", "EB", "ZB", "YB"
string GetMemoryString(double bytes)
{
    int counter = 0;
    double value = bytes;
    string text = "";
    do
    {
        text = value.ToString("0.0") + " " + BinaryPrefix[counter];
        value /= 1024;
        counter++;
    }
    while (Math.Floor(value) > 0 && counter < BinaryPrefix.Length);
    return text;
}

我已将其(几乎没有修改)合并到我的项目的 UWP 数据绑定转换器中,并认为它也可能对其他人有用。

代码为:

using System;
using System.Text;
using Windows.UI.Xaml.Data;
namespace MyApp.Converters
{
    public class ByteSizeConverter : IValueConverter
    {
        static readonly string[] sSizeSuffixes = { "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };
        // The number of decimal places the formatter should include in the scaled output - default 1dp
        public int DecimalPlaces { get; set; } = 1;
        public object Convert(object value, Type targetType, object parameter, string language)
        {
            Int64 intVal = System.Convert.ToInt64(value);
            return SizeSuffix(intVal);
        }
        public object ConvertBack(object value, Type targetType, object parameter, string language)
        {
            // TODO: Parse string into number and suffix
            //       Scale number by suffix multiplier to get bytes
            throw new NotImplementedException();
        }
        string SizeSuffix(Int64 value)
        {
            if (this.DecimalPlaces < 0) { throw new ArgumentOutOfRangeException(String.Format("DecimalPlaces = {0}", this.DecimalPlaces)); }
            if (value < 0) { return "-" + SizeSuffix(-value); }
            if (value == 0) { return string.Format("{0:n" + this.DecimalPlaces + "} bytes", 0); }
            // magnitude is 0 for bytes, 1 for KB, 2, for MB, etc.
            int magnitude = (int)Math.Log(value, 1024);
            // clip magnitude - only 8 values currently supported, this prevents out-of-bounds exception
            magnitude = Math.Min(magnitude, 8);
            // 1L << (magnitude * 10) == 2 ^ (10 * magnitude) [i.e. the number of bytes in the unit corresponding to magnitude]
            decimal adjustedSize = (decimal)value / (1L << (magnitude * 10));
            // make adjustment when the value is large enough that it would round up to 1000 or more
            if (Math.Round(adjustedSize, this.DecimalPlaces) >= 1000)
            {
                magnitude += 1;
                adjustedSize /= 1024;
            }
            return String.Format("{0:n" + this.DecimalPlaces + "} {1}", adjustedSize, sSizeSuffixes[magnitude]);
        }
    }
}

若要使用它,请将本地资源添加到用户控件或页面 XAML:

<UserControl.Resources>
    <converters:ByteSizeConverter x:Key="ByteFormat" DecimalPlaces="3" />
</UserControl.Resources>
在数据绑定

模板或数据绑定实例中引用它:

<TextBlock HorizontalAlignment="Left" VerticalAlignment="Center"
    Text="{x:Bind MyItem.FileSize_bytes, Mode=OneWay, Converter={StaticResource ByteFormat}}" />

嘿,普雷斯托。奇迹发生了。

> https://github.com/logary/logary/blob/master/src/Logary/DataModel.fs#L832-L837

let scaleBytes (value : float) : float * string =
    let log2 x = log x / log 2.
    let prefixes = [| ""; "Ki"; "Mi"; "Gi"; "Ti"; "Pi" |] // note the capital K and the 'i'
    let index = int (log2 value) / 10
    1. / 2.**(float index * 10.),
sprintf "%s%s" prefixes.[index] (Units.symbol Bytes)

(免责声明:我写了这段代码,甚至是链接中的代码!

这是我

对@drzaus答案的看法。 我对其进行了修改,以利用舍入误差来发挥我们的优势,并正确管理单位边界周围的问题。 它还处理负值。

将此C# Program放入 LinqPad:

// Kudos: https://stackoverflow.com/a/48467634/117797
void Main()
{
    0.ToFriendly().Dump();                      // 0 B
    857.ToFriendly().Dump();                    // 857 B
    (173*1024).ToFriendly().Dump();             // 173 KB
    (9541*1024).ToFriendly().Dump();            // 9.32 MB
    (5261890L*1024).ToFriendly().Dump();        // 5.02 GB
    1.ToFriendly().Dump();                      // 1 B
    1024.ToFriendly().Dump();                   // 1 KB
    1048576.ToFriendly().Dump();                // 1 MB
    1073741824.ToFriendly().Dump();             // 1 GB
    1099511627776.ToFriendly().Dump();          // 1 TB
    1125899906842620.ToFriendly().Dump();       // 1 PB
    1152921504606850000.ToFriendly().Dump();    // 1 EB
}
public static class Extensions
{
    static string[] _byteUnits = new[] { "B", "KB", "MB", "GB", "TB", "PB", "EB" };
    public static string ToFriendly(this int number, int decimals = 2)
    {
        return ((double)number).ToFriendly(decimals);
    }
    public static string ToFriendly(this long number, int decimals = 2)
    {
        return ((double)number).ToFriendly(decimals);
    }
    public static string ToFriendly(this double number, int decimals = 2)
    {
        const double divisor = 1024;
        int unitIndex = 0;
        var sign = number < 0 ? "-" : string.Empty;
        var value = Math.Abs(number);
        double lastValue = number;
        while (value > 1)
        {
            lastValue = value;
            // NOTE
            // The following introduces ever increasing rounding errors, but at these scales we don't care.
            // It also means we don't have to deal with problematic rounding errors due to dividing doubles.
            value = Math.Round(value / divisor, decimals);
            unitIndex++;
        }
        if (value < 1 && number != 0)
        {
            value = lastValue;
            unitIndex--;
        }
        return $"{sign}{value} {_byteUnits[unitIndex]}";
    }
}

输出为:

0 B
857 B
173 KB
9.32 MB
1.34 MB
5.02 GB
1 B
1 KB
1 MB
1 GB
1 TB
1 PB
1 EB
string Convert(float bytes)
{
    string[] Group = { "Bytes", "KB", "MB", "GB", "TB"};
    float B = bytes; int G = 0;
    while (B >= 1024 && G < 5)
    {
        B /= 1024;
        G += 1;
    }
    float truncated = (float)(Math.Truncate((double)B * 100.0) / 100.0);
    string load = (truncated + " " + Group[G]);
    return load;
}

这就是我的做法。

Console.Write(FileSizeInBytes > 1048576 ? FileSizeInBytes / 1048576f + " MB" : FileSizeInBytes / 1024f + " KB"); //1048576 = 1024 * 1024

我将 zackmark15 的代码组合成一个通用的文件或目录测量方法:

public static string PathSize(string path)
{
    if (String.IsNullOrEmpty(path))
        throw new ArgumentNullException(nameof(path));
    long bytes;
    if (File.Exists(path))
        bytes = new FileInfo(path).Length;
    else if (Directory.Exists(path))
        bytes = new DirectoryInfo(path).EnumerateFiles("*", SearchOption.AllDirectories).Sum(fileInfo => fileInfo.Length);
    else
        throw new ArgumentException("Path does not exist.", nameof(path));
    const long UNIT = 1024L;
    if (bytes < UNIT)
        return $"{bytes} bytes";
    var exp = (int)(Math.Log(bytes) / Math.Log(UNIT));
    return $"{bytes / Math.Pow(UNIT, exp):F2} {("KMGTPE")[exp - 1]}B";
}

好的...让我们使用窗口提供的内容...

[DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
private static extern long StrFormatByteSizeW(long qdw,
    [MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszBuf,
    int cchBuf);
public static string GetFileSize(this long number)
{
  var sb = new StringBuilder(32);
  StrFormatByteSizeW(number, sb, sb.Capacity);
  return sb.ToString();
}

正在寻找一个更干净的解决方案,但在我看来,我似乎找不到一个,所以在这里。

这旨在使用 File.Length 获取文件大小长整型值,然后将其转换为大小类型并四舍五入到小数点后两位:

10 字节变为 10B

1030KB 变为 1.01MB

16384KB 变为 16MB

2,097,152KB 变为 2GB

13,064,424KB 变为 12.46GB

public static string FormatFileSize(long fileSizeBytes)
{
    const int KB = 1024;
    string[] sizeLabels = { "B", "KB", "MB", "GB", "TB" };
    double fileSize = fileSizeBytes;
    int labelIndex = 0;
    while (fileSize >= KB && labelIndex < sizeLabels.Length - 1)
    {
        fileSize /= KB;
        labelIndex++;
    }
    fileSize = Math.Round(fileSize, 2);
    return $"{fileSize} {sizeLabels[labelIndex]}";
}

由于人们添加自己的算法作为这个问题的答案似乎风靡一时,这是我的。(虽然,诚然,我的主要动机是将其发布在我需要时可以再次找到的地方!

它是一种跨平台的 C# 扩展方法,用于实现 Windows StrFormatByteSize 函数,该函数提供具有三个或四个有效数字以及最多两个小数位的结果。它与StrFormatByteSize在一些小方面有所不同(见下文)。与 .NET 7 及更高版本兼容,尽管将其向后移植到以前的版本并不困难。

如果includeTotalBytes true ,则向字符串中添加字节数的总和,类似于 Windows 文件资源管理器的"属性"对话框中显示的内容。

法典

using System;
using System.Numerics;
internal static class BinaryIntegerExtensions
{
    private static readonly string[] sizeSuffixes = { "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB", "RB", "QB" };
    /// <summary>Converts the numeric value of this instance to a string that represents the number expressed as a size-dependent value in bytes, kilobytes, megabytes, etc, up to quettabytes.</summary>
    /// <remarks>The string expression units are based on powers of 2, represented by the colloquially-understood KB, MB, GB, etc, instead of the technically correct KiB, MiB, GiB, etc.</remarks>
    /// <param name="includeTotalBytes"><c>true</c> to append the total number of bytes to the string; otherwise, <c>false</c>.</param>
    /// <returns>The string representation of the value, expressed in bytes, kilobytes, megabytes, etc, up to quettabytes.</returns>
    /// <exception cref="OverflowException">The numeric value of this instance is out of range and cannot be converted.</exception>
    internal static string ToByteSizeString<T>(this T value, bool includeTotalBytes) where T : IBinaryInteger<T>
    {
        string result;
        if (T.IsZero(value))
            result = $"0 {sizeSuffixes[0]}";
        else
        {
            int magnitude, decimalPlaces;
            double absolute, fullResult, roundedResult;
            string bytesPart = string.Empty;
            absolute = double.CreateChecked(T.Abs(value));
            magnitude = Math.Min((int)Math.Floor(Math.Log(absolute, 1024)), sizeSuffixes.Length - 1);
            fullResult = T.Sign(value) * (absolute / Math.Pow(1024, magnitude));
            decimalPlaces = Math.Max(0, 2 - (int)Math.Floor(Math.Log10(fullResult)));
            roundedResult = Math.Round(fullResult, decimalPlaces, MidpointRounding.AwayFromZero);
            if (includeTotalBytes && (magnitude > 0))
                bytesPart = $" ({value:N0} {sizeSuffixes[0]})";
            result = $"{roundedResult:#,#.##} {sizeSuffixes[magnitude]}{bytesPart}";
        }
        return (result);
    }
}

使用示例

int test1 = 46432131;
string result1 = test1.ToByteSizeString(false);
// result1 == "44.3 MB"
ulong test2 = 1748413218964;
string result2 = test2.ToByteSizeString(false);
// result2 == "1.59 TB"
long test3 = 56431242;
string result3 = test3.ToByteSizeString(true);
// result3 == "53.8 MB (56,431,242 bytes)"
short test4 = 512;
string result4 = test4.ToByteSizeString(true);
// result4 == "512 bytes"
UInt128 test5 = UInt128.MaxValue;
string result5 = test5.ToByteSizeString(true);
// result5 = "268,435,456 QB (340,282,366,920,938,463,463,374,607,431,768,211,455 bytes)"

StrFormatByteSize的区别

  1. 结果字符串中的数字在适当的情况下进行分组。
  2. 结果会根据需要向上或向下舍入,而 StrFormatByteSize 会在显示多少位小数后截断。
  3. 负值按预期工作,而 StrFormatByteSize 仅为负值提供总字节数。