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

只是想知道.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);
}

等等。

173030 次浏览

没有。主要是因为这是一个相当小众的需求,有太多的可能的变化。(是“ KB”,“ KB”还是“ Ko”?兆字节是1024 * 1024字节,还是1024 * 1000字节?——是的,有些地方就是这么做的!)

没有。

但是您可以像这样实现;

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


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

还有 如何正确地将文件大小(以字节为单位)转换为兆或千兆字节?

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

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

这里有一个相当简洁的方法来做到这一点:

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。

因为其他人都在发布他们的方法,所以我想我应该发布我通常使用的扩展方法:

编辑: 添加了 int/long 变体... 修复了一个意大利面的拼写错误..。

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

我已经将这里的一些答案组合成了两种非常有效的方法。下面的第二个方法将从字节字符串(如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);
}

投票最多的答案的简短版本存在结核病价值观的问题。

我适当地调整了它来处理 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]);
}

检查 字节大小库。它是字节的 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");

这个怎么样:

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"

我会用 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);
    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)

我选择了 JerKimball 的解决方案,并且赞成。 然而,我想补充/指出,这确实是一个整体上存在争议的问题。在我的研究中(出于其他原因) ,我提出了以下几点信息。

当正常人(我听说过他们的存在)谈到千兆字节时,他们指的是公制系统,其中1000的3次方从原来的字节数 = = 千兆字节数。 然而,当然还有 IEC/JEDEC 标准,维基百科很好地总结了这些标准,它们不是1000的 x 次方,而是1024次方。 对于物理存储设备来说(我想逻辑上来说,比如亚马逊和其他公司) ,公制和 IEC 之间的差异越来越大。 例如,1TB = = 1TB 的度量是1000的4次方,但是 IEC 官方将相似的数字定义为1TiB,1024的4次方。 但是,唉,在非技术性的应用程序中(我会根据受众) ,规范是公制的,而在我自己的内部使用的应用程序中,目前我解释了文档中的差异。但是,为了展示的目的,我甚至不提供任何东西,但公制。在内部,即使它与我的应用程序无关,我也只是存储字节并进行显示计算。

作为一个旁注,我发现它有点暗淡。网络框架 AFAIK (我经常错误的感谢权力) ,即使在它的4.5化身没有包含任何关于这在任何图书馆内部。在某种程度上,人们会期望某种开源库是 NuGettable,但我承认这是一个小麻烦。另一方面,System.IO.DriveInfo 等也只有字节(只有这么长) ,这是相当清楚的。

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)

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

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

来点递归怎么样:

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

如上所述,在对数的帮助下,递归是最受欢迎的方法。

下面的函数有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 内存转换成几个单位:

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
}

基于 NeverHopless 的优雅解决方案:

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

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

我把这个用于 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 DataBinding Converter 中,并且认为它可能对其他人也有用。

密码是:

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

要使用它,请将本地资源添加到 UserControl 或 PageXAML:

<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}}" />

很快,奇迹发生了。

我知道这已经是老生常谈了,但是也许有人会找到解决的办法。 这是我用的,最简单的方法

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

以下是我对@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

我最近需要这样做,并要求将字节数转换为长数。

用法: 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);
}
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;
}

更新为 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}"
//...
};
}

我就是这么做的。

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