在C#中将字节转换为GB?

38

我在重构一些旧代码时发现以下代码行,用于将字节转换为GB。

decimal GB = KB / 1024 / 1024 / 1024;

有没有更好的方法来重构下面这段代码?

更新

我想说的是字节转千兆字节。我给出了错误的信息。


15
"将 decimal GB = KB / 1024 / 1024; 改为 decimal GB = KB / 1024 / 1024; 更好一些..." - sth
11
代码除了多一个部分外,没有任何问题;它的意图非常清晰,不会使用过多冗余语言,也没有性能问题。为什么您想要进行重构呢? - Pavel Minaev
1
我认为这很好,尽管我可能会将GB = bytes * 1E-9; - kenny
1
抱歉我要扮演那个人,但是应该是吉比字节而不是千兆字节。 - vallentin
12个回答

117

我在这里开发了这种方法,适用于TB。

private static string FormatBytes(long bytes)
{
    string[] Suffix = { "B", "KB", "MB", "GB", "TB" };
    int i;
    double dblSByte = bytes;
    for (i = 0; i < Suffix.Length && bytes >= 1024; i++, bytes /= 1024) 
    {
        dblSByte = bytes / 1024.0;
    }

    return String.Format("{0:0.##} {1}", dblSByte, Suffix[i]);
}

我同意@ktutnik关于第二部分的看法! - Shimmy Weitzhandler
这是如何工作的?你在if/else中缺少返回类型。 - Kimtho6
是的,自从我上次在这里发布代码以来,有人修改了它。 - JLopez
当字节数达到PB时,代码会出现错误。 - nhahtdh

19

如果精确度不是很重要,可以使用double:

double gb = kb / 1048576D

我同意Pavel的看法 - 实际上,没有必要重构这段代码……事实上,如果这是你的代码库中最大的问题,那么我认为你可能正在使用编写得最好的软件。



2
对于使用double类型的+1,我认为原始实现的意图更清晰。 - Marc
1
@Marc 可能吧。不过我认为 gb = kb... 这部分是一个相当好的线索 :) - Rex M
你可以通过声明一个有意义的常量来提高1048576的可读性... - mezoid

8

原始代码简洁易读,变量名合理,自我说明性强;我不会对其进行修改。

如果您一定要重构代码,可以在数字类型上创建一组扩展方法:

public static double BytesToKilobytes(this Int32 bytes)
{
    return bytes / 1024d;
}
public static double BytesToMegabytes(this Int32 bytes)
{
    return bytes / 1024d / 1024d;
}
public static double KilobytesToBytes(this double kilobytes)
{
    return kilobytes * 1024d;
}

//You can then do something like:
double filesize = 32.5d;
double bytes = filesize.KilobytesToBytes();

但是,除非你的代码几乎只是将字节转换为千字节等,否则所有这些都只会使智能感知变得混乱,而没有真正的收益。


4

我写了一个小实用程序类,可以在不同的单位之间进行转换,希望对您有帮助。

#region StorageDifferential
/// <summary>
/// Converts between Base 2 or Base 10 storage units [TB, GB, MB, KB, Bytes]
/// </summary>
public enum Differential : int
{
    /// <summary>
    /// Convert Bytes to Kilobytes
    /// </summary>
    ByteToKilo,
    /// <summary>
    /// Convert Bytes to Megabytes
    /// </summary>
    ByteToMega,
    /// <summary>
    /// Convert Bytes to Gigabytes
    /// </summary>
    ByteToGiga,
    /// <summary>
    /// Convert Bytes to Teraytes
    /// </summary>
    ByteToTera,
    /// <summary>
    /// Convert Kilobytes to Bytes
    /// </summary>
    KiloToByte,
    /// <summary>
    /// Convert Kilobytes to Megabytes
    /// </summary>
    KiloToMega,
    /// <summary>
    /// Convert Kilobytes to Gigabytes
    /// </summary>
    KiloToGiga,
    /// <summary>
    /// Convert Kilobytes to Terabytes
    /// </summary>
    KiloToTera,
    /// <summary>
    /// Convert Megabytes to Bytes
    /// </summary>
    MegaToByte,
    /// <summary>
    /// Convert Megabytes to Kilobytes
    /// </summary>
    MegaToKilo,
    /// <summary>
    /// Convert Megabytes to Gigabytes
    /// </summary>
    MegaToGiga,
    /// <summary>
    /// Convert Megabytes to Terabytes
    /// </summary>
    MegaToTera,
    /// <summary>
    /// Convert Gigabytes to Bytes
    /// </summary>
    GigaToByte,
    /// <summary>
    /// Convert Gigabytes to Kilobytes
    /// </summary>
    GigaToKilo,
    /// <summary>
    /// Convert Gigabytes to Megabytes
    /// </summary>
    GigaToMega,
    /// <summary>
    /// Convert Gigabytes to Terabytes
    /// </summary>
    GigaToTerra,
    /// <summary>
    /// Convert Terabyte to Bytes
    /// </summary>
    TeraToByte,
    /// <summary>
    /// Convert Terabyte to Kilobytes
    /// </summary>
    TeraToKilo,
    /// <summary>
    /// Convert Terabytes to Megabytes
    /// </summary>
    TeraToMega,
    /// <summary>
    /// Convert Terabytes to Gigabytes
    /// </summary>
    TeraToGiga,
}
#endregion

#region Storage Sizes
/// <summary>
/// Enumeration of recognized storage sizes [in Bytes]
/// </summary>
public enum StorageSizes : long
{
    /// <summary>
    /// Base 10 Conversion
    /// </summary>
    KILOBYTE = 1000,
    MEGABYTE = 1000000,
    GIGABYTE = 1000000000,
    TERABYTE = 1000000000000,
    /// <summary>
    /// Base 2 Conversion
    /// </summary>
    KIBIBYTE = 1024,
    MEBIBYTE = 1048576,
    GIBIBYTE = 1073741824,
    TEBIBYTE = 1099511627776,
}
#endregion

#region StorageBase
/// <summary>
/// Storage powers 10 based or 1024 based
/// </summary>
public enum StorageBase : int
{
    /// <summary>
    /// 1024 Base power, Typically used in memory measurements
    /// </summary>
    BASE2,
    /// <summary>
    /// 10 Base power, Used in storage mediums like harddrives
    /// </summary>
    BASE10,
}
#endregion

/// <summary>
/// Convert between base 1024 storage units [TB, GB, MB, KB, Byte]
/// </summary>
public static class StorageConverter
{
    /// <summary>
    /// Convert between base 1024 storage units [TB, GB, MB, KB, Byte]
    /// </summary>
    /// <param name="SizeDifferential">Storage conversion differential [enum]</param>
    /// <param name="UnitSize">Size as mutiple of unit type units [double]</param>
    /// <param name="BaseUnit">Size of the base power [enum]</param>
    /// <returns>Converted unit size [double]</returns>
    public static double Convert(Differential SizeDifferential, double UnitSize, StorageBase BaseUnit = StorageBase.BASE10)
    {
        if (UnitSize < 0.000000000001) return 0;

        double POWER1 = 1000;
        double POWER2 = 1000000;
        double POWER3 = 1000000000;
        double POWER4 = 1000000000000;

        if (BaseUnit == StorageBase.BASE2)
        {
            POWER1 = 1024;
            POWER2 = 1048576;
            POWER3 = 1073741824;
            POWER4 = 1099511627776;
        }

        switch (SizeDifferential)
        {
            case Differential.ByteToKilo:
                return UnitSize / POWER1;
            case Differential.ByteToMega:
                return UnitSize / POWER2;
            case Differential.ByteToGiga:
                return UnitSize / POWER3;
            case Differential.ByteToTera:
                return UnitSize / POWER4;
            case Differential.KiloToByte:
                return UnitSize * POWER1;
            case Differential.KiloToMega:
                return UnitSize / POWER1;
            case Differential.KiloToGiga:
                return UnitSize / POWER2;
            case Differential.KiloToTera:
                return UnitSize / POWER3;
            case Differential.MegaToByte:
                return UnitSize * POWER2;
            case Differential.MegaToKilo:
                return UnitSize * POWER1;
            case Differential.MegaToGiga:
                return UnitSize / POWER1;
            case Differential.MegaToTera:
                return UnitSize / POWER2;
            case Differential.GigaToByte:
                return UnitSize * POWER3;
            case Differential.GigaToKilo:
                return UnitSize * POWER2;
            case Differential.GigaToMega:
                return UnitSize * POWER1;
            case Differential.GigaToTerra:
                return UnitSize / POWER1;
            case Differential.TeraToByte:
                return UnitSize * POWER4;
            case Differential.TeraToKilo:
                return UnitSize * POWER3;
            case Differential.TeraToMega:
                return UnitSize * POWER2;
            case Differential.TeraToGiga:
                return UnitSize * POWER1;
        }

        return 0;
    }
}

这是这个问题上最好的答案。我测试了几个答案,但没有一个能为我转换正确的大小。我猜这是因为它们不支持十进制。 - Mecanik

3

这是对JLopez优秀回答的小改进。在这里,您可以选择是否具有单位说明,千字节单位使用小写的"k"(大写的"k"用于开尔文)。

//note: this is the JLopez answer!!
/// <summary>
/// Return size in human readable form
/// </summary>
/// <param name="bytes">Size in bytes</param>
/// <param name="useUnit ">Includes measure unit (default: false)</param>
/// <returns>Readable value</returns>
public static string FormatBytes(long bytes, bool useUnit = false)
    {
        string[] Suffix = { " B", " kB", " MB", " GB", " TB" };
        double dblSByte = bytes;
        int i;
        for (i = 0; i < Suffix.Length && bytes >= 1024; i++, bytes /= 1024)
        {
            dblSByte = bytes / 1024.0;
        }
        return $"{dblSByte:0.##}{(useUnit ? Suffix[i] : null)}";
    }

2

个人认为应该这样写:decimal GB = KB / (1024 * 1024); 但是按照原来的代码编写也没有必要重构。


2

嗯,这个公式是错误的(一GB只有大约一百万KB,而不是十亿),但除此之外,它还是正确的。任何习惯使用这些数字的人都会知道它的含义。

需要注意的一件事情(我不知道C#是否存在这个问题)是,如果x不是基本类型,编译器可能无法优化x/1024/1024。对于C和整数而言,编译器可以轻松地将其转换为“盲目快速移位20位”的指令。

如果decimal是一个类而不是基本类型,编译器可能需要执行两个除法操作。这是否对速度产生任何真正的影响(或者它是否发生)超出了我的知识范围。

我建议更改实际变量名称。这对编译后的代码没有任何实际影响,但我更喜欢较长的变量名而不是缩写,所以我会选择kiloBytes/gigaBytes或类似的名称。根据您的编码标准,KB/GB太容易与常量混淆。


十进制是一种内置类型,而不是一个类。但它也不支持位移操作,尽管这在某些情况下是有意义的。也许编译器可以在这种情况下使用它们,即使程序员不能。 - Ben Voigt

2
为了确保编译器预先计算除数:
decimal GB = KB / (1024 * 1024);

请注意,您实际上计算的是GiB(吉比字节),而不是GB(千兆字节)。如果您真的想计算GB,则应该是:
decimal GB = KB / (1000 * 1000);

1
通常用法中,gigabyte 用于表示 1024^3 字节,mega 表示 1024^2,kilo 表示 1024。这可能在技术上不是正确的,但我认为这里没有人会介意。 - snarf
是的,大多数人并不知道这个标准已经存在了十多年,这就是为什么我做了一个注释... - Guffa
“标准”实际上并不存在,因为它改变了单词的普通含义,并为常见事物创造了新词。这就是为什么除了制定它的行业外,真正没有人接受这个标准。 - Oliver Friedrich
1
@BeowulfOF:你说的不是真的。我见过很多程序使用标准单位。这是一个真正的标准,由IEC定义,并被IEEE和CIPM等多个大型标准化机构接受。该标准没有改变单位的通用含义,因为本来就没有通用含义。以“1.44 MB”软盘为例,其容量既不是1.44 MB也不是1.44 MiB,而是1.44 * 1024 * 1000字节... - Guffa

2

理论上,这样做更快(预先计算常数以进行乘法而不是除法)。可能没有经常使用到,但以防万一。

double const KbToGbFactor = 1d / 1024 /1024;

double gb = kb * KbToGbFactor;

1
我相信当程序编译时,这将被优化。不过,是否在此处使用const更多是品味问题。 - Spoike
我不太确定 - http://blogs.msdn.com/ericlippert/archive/2009/06/11/what-does-the-optimize-switch-do.aspx - Kenan E. K.
我喜欢这个答案的“清晰度”(在我的脑海中),但不是为了“优化”。唉 :-) - user166390

2

我需要将第三方组件字面量大小(例如“0字节”,“1.1 MB”)转换为通用的字节大小,所以我这样使用:

        private static long UnformatBytes(string sizeInWords)
    {
        if(string.IsNullOrWhiteSpace(sizeInWords))
            return -1;

        string size = sizeInWords.Split(' ').FirstOrDefault();
        double result;
        if (string.IsNullOrWhiteSpace(size) || !double.TryParse(size, out result))
        {
            Debugger.Break();
            return -1;
        }

        int pow;

        if (sizeInWords.IndexOf("byte", StringComparison.OrdinalIgnoreCase) > -1)
            pow = 0;
        else if (sizeInWords.IndexOf("kb", StringComparison.OrdinalIgnoreCase) > -1)
            pow = 1;
        else if (sizeInWords.IndexOf("mb", StringComparison.OrdinalIgnoreCase) > -1)
            pow = 2;
        else if (sizeInWords.IndexOf("gb", StringComparison.OrdinalIgnoreCase) > -1)
            pow = 3;
        else if (sizeInWords.IndexOf("tb", StringComparison.OrdinalIgnoreCase) > -1)
            pow = 4;
        else
            return -1;

        return System.Convert.ToInt64((result * Math.Pow(1024, pow)));
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接