我如何获得一个人类可读的文件大小字节缩写使用。net ?

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


当前回答

下面是@ deepe1的BigInteger版本的答案,它绕过了long的大小限制(因此支持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]}";
}

其他回答

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

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

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

为了获得人类可读的字符串,就像用户在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/

1-liner(加上前缀常量)

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

又多了一种方法,不管怎样。我喜欢上面提到的@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);
    }
}