diff --git a/DataSizeUnits.sln.DotSettings b/DataSizeUnits.sln.DotSettings
index 11f2c26..2fe142f 100644
--- a/DataSizeUnits.sln.DotSettings
+++ b/DataSizeUnits.sln.DotSettings
@@ -1,3 +1,22 @@
- <data><IncludeFilters /><ExcludeFilters /></data>
- <data />
\ No newline at end of file
+ False
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
+ <Policy Inspect="True" Prefix="I" Suffix="" Style="AaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
+ <Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" />
+ <Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
+ <Policy Inspect="True" Prefix="T" Suffix="" Style="AaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
+
\ No newline at end of file
diff --git a/DataSizeUnits/DataSize.cs b/DataSizeUnits/DataSize.cs
index 19d506f..1ed43b4 100644
--- a/DataSizeUnits/DataSize.cs
+++ b/DataSizeUnits/DataSize.cs
@@ -1,225 +1,269 @@
using System;
+using System.Globalization;
namespace DataSizeUnits {
- public static class DataSize {
+ ///
+ /// An amount of digital data. Create instances using the constructor or struct declarations.
+ /// var kilobyte = new DataSize(1024);
+ /// var kilobyte = new DataSize(1, Unit.Kilobyte);
+ ///
+ [Serializable]
+ public struct DataSize {
- public static readonly IFormatProvider FORMATTER = new DataSizeFormatter();
-
- public enum Unit {
-
- BYTE,
- KILOBYTE,
- MEGABYTE,
- GIGABYTE,
- TERABYTE,
- PETABYTE,
- BIT,
- KILOBIT,
- MEGABIT,
- GIGABIT,
- TERABIT,
- PETABIT
+ public double Quantity;
+ public Unit Unit;
+ ///
+ /// Create a new instance with the given quantity of the given unit of data.
+ /// var kilobyte = new DataSize(1, Unit.Kilobyte);
+ ///
+ /// How much of the given data unit to represent.
+ /// The unit of measure of the given quantity of data.
+ public DataSize(double quantity, Unit unit) {
+ Quantity = quantity;
+ Unit = unit;
}
- private static ulong toBits(Unit source) {
- switch (source) {
- case Unit.BYTE:
- return 8L;
- case Unit.KILOBYTE:
- return 8L * 1024;
- case Unit.MEGABYTE:
- return 8L * 1024 * 1024;
- case Unit.GIGABYTE:
- return 8L * 1024 * 1024 * 1024;
- case Unit.TERABYTE:
- return 8L * 1024 * 1024 * 1024 * 1024;
- case Unit.PETABYTE:
- return 8L * 1024 * 1024 * 1024 * 1024 * 1024;
- case Unit.BIT:
- return 1;
- case Unit.KILOBIT:
- return 1000L;
- case Unit.MEGABIT:
- return 1000L * 1000;
- case Unit.GIGABIT:
- return 1000L * 1000 * 1000;
- case Unit.TERABIT:
- return 1000L * 1000 * 1000 * 1000;
- case Unit.PETABIT:
- return 1000L * 1000 * 1000 * 1000 * 1000;
- default:
- throw new ArgumentOutOfRangeException(nameof(source), source, null);
- }
- }
+ ///
+ /// Create a new instance with the given quantity of bytes.
+ /// var fileSize = new DataSize(new FileInfo(fileName).Length);
+ ///
+ /// How many bytes to represent.
+ public DataSize(long bytes): this(bytes, Unit.Byte) { }
- public static string toAbbreviation(Unit source) {
- switch (source) {
- case Unit.BYTE:
- return "B";
- case Unit.KILOBYTE:
- return "KB";
- case Unit.MEGABYTE:
- return "MB";
- case Unit.GIGABYTE:
- return "GB";
- case Unit.TERABYTE:
- return "TB";
- case Unit.PETABYTE:
- return "PB";
- case Unit.BIT:
- return "b";
- case Unit.KILOBIT:
- return "kb";
- case Unit.MEGABIT:
- return "mb";
- case Unit.GIGABIT:
- return "gb";
- case Unit.TERABIT:
- return "tb";
- case Unit.PETABIT:
- return "pb";
- default:
- throw new ArgumentOutOfRangeException(nameof(source), source, null);
- }
+ ///
+ /// Convert the data size to the automatically-chosen best fit unit. This will be the largest unit that represents
+ /// the data size as a number greater than or equal to one.
+ /// new DataSize(1024).Normalize().ToString(); → 1.00 KB
+ ///
+ /// true to choose a multiple of bits (bits, kilobits, megabits, etc.), or false (the default) to choose a multiple of bytes (bytes, kilobytes, megabytes, etc.).
+ /// A new instance with the normalized quantity and unit. The original instance is unchanged.
+ public DataSize Normalize(bool useBitsInsteadOfBytes = false) {
+ double inputBytes = ConvertToUnit(Unit.Byte).Quantity;
+ int orderOfMagnitude = (int) Math.Max(0, Math.Floor(Math.Log(Math.Abs(inputBytes), useBitsInsteadOfBytes ? 1000 : 1024)));
+ Unit outputUnit = ForMagnitude(orderOfMagnitude, useBitsInsteadOfBytes);
+ return ConvertToUnit(outputUnit);
}
- private static Unit forMagnitude(int orderOfMagnitude, bool useBytes) {
- switch (orderOfMagnitude) {
- case 0:
- return useBytes ? Unit.BYTE : Unit.BIT;
- case 1:
- return useBytes ? Unit.KILOBYTE : Unit.KILOBIT;
- case 2:
- return useBytes ? Unit.MEGABYTE : Unit.MEGABIT;
- case 3:
- return useBytes ? Unit.GIGABYTE : Unit.GIGABIT;
- case 4:
- return useBytes ? Unit.TERABYTE : Unit.TERABIT;
- case 5:
- return useBytes ? Unit.PETABYTE : Unit.PETABIT;
- default:
- throw new ArgumentOutOfRangeException(
- $"Supported orders of magnitude are between 0 (bit/byte) and 5 (petabit/petabyte) inclusive. {orderOfMagnitude} is outside the range [0,5].");
- }
+ ///
+ /// Convert the data size to the given unit.
+ /// new DataSize(1024).ConvertToUnit(Unit.Kilobyte).ToString(); → 1.00 KB
+ ///
+ /// The data size unit that the resulting instance should use.
+ /// A new instance with the converted quantity and unit. The original instance is unchanged.
+ public DataSize ConvertToUnit(Unit destinationUnit) {
+ return new DataSize(Quantity * CountBitsInUnit(Unit) / CountBitsInUnit(destinationUnit), destinationUnit);
}
- public static Unit forAbbreviation(string abbreviation) {
- switch (abbreviation.ToLowerInvariant()) {
+ ///
+ /// Get a data size unit from its string name or abbreviation.
+ /// Supports units of bits and bytes, including the SI units like kibibytes, as well as all their abbreviations.
+ /// Some abbreviations are case-insensitive, such as megabyte, but others are case-sensitive, like mb and MB because one means megabits and the other means megabytes.
+ /// For example, all the inputs that will be parsed as Unit.Megabyte are M, MB, megabyte, mbyte, mib, and mebibyte (the first two are case-sensitive).
+ /// Usage: Unit megabyte = DataSize.ParseUnit("megabyte");
+ ///
+ /// The name (e.g. kilobyte) or abbreviation (e.g. kB) of a data size unit.
+ /// The value that represents the matched data size unit.
+ /// The given name does not match any known units or their abbreviations.
+ public static Unit ParseUnit(string unitNameOrAbbreviation) {
+ switch (unitNameOrAbbreviation.ToLowerInvariant()) {
case "byte":
- return Unit.BYTE;
- case "bit":
- return Unit.BIT;
+ return Unit.Byte;
case "kilobyte":
case "kbyte":
- case "kibibit":
case "kib":
- case "kibit":
- return Unit.KILOBYTE;
- case "kilobit":
- case "kbit":
- return Unit.KILOBIT;
+ case "kibibyte":
+ return Unit.Kilobyte;
case "megabyte":
case "mbyte":
- case "mebibit":
case "mib":
- case "mibit":
- return Unit.MEGABYTE;
- case "megabit":
- case "mbit":
- return Unit.MEGABIT;
+ case "mebibyte":
+ return Unit.Megabyte;
case "gigabyte":
case "gbyte":
- case "gibibit":
case "gib":
- case "gibit":
- return Unit.GIGABYTE;
- case "gigabit":
- case "gbit":
- return Unit.GIGABIT;
+ case "gibibyte":
+ return Unit.Gigabyte;
case "terabyte":
case "tbyte":
- case "tebibit":
case "tib":
- case "tibit":
- return Unit.TERABYTE;
- case "terabit":
- case "tbit":
- return Unit.TERABIT;
+ case "tebibyte":
+ return Unit.Terabyte;
case "petabyte":
case "pbyte":
- case "pebibit":
case "pib":
- case "pibit":
- return Unit.PETABYTE;
+ case "pebibyte":
+ return Unit.Petabyte;
+ case "exabyte":
+ case "ebyte":
+ case "eib":
+ case "exbibyte":
+ return Unit.Exabyte;
+
+ case "bit":
+ return Unit.Bit;
+ case "kilobit":
+ case "kbit":
+ return Unit.Kilobit;
+ case "megabit":
+ case "mbit":
+ return Unit.Megabit;
+ case "gigabit":
+ case "gbit":
+ return Unit.Gigabit;
+ case "terabit":
+ case "tbit":
+ return Unit.Terabit;
case "petabit":
case "pbit":
- return Unit.PETABIT;
+ return Unit.Petabit;
+ case "exabit":
+ case "ebit":
+ return Unit.Exabit;
+
default:
- //not found in case-insensitive switch, continuing to case-sensitive switch
+ //not found in case-insensitive switch, continuing to case-sensitive switch below
break;
}
- switch (abbreviation) {
+ switch (unitNameOrAbbreviation) {
case "B":
- return Unit.BYTE;
- case "b":
- return Unit.BIT;
+ return Unit.Byte;
case "kB":
case "KB":
case "K":
- return Unit.KILOBYTE;
+ return Unit.Kilobyte;
+ case "MB":
+ case "M":
+ return Unit.Megabyte;
+ case "GB":
+ case "G":
+ return Unit.Gigabyte;
+ case "TB":
+ case "T":
+ return Unit.Terabyte;
+ case "PB":
+ case "P":
+ return Unit.Petabyte;
+ case "EB":
+ case "E":
+ return Unit.Exabyte;
+
+ case "b":
+ return Unit.Bit;
case "kb":
case "Kb":
case "k":
- return Unit.KILOBIT;
- case "MB":
- case "M":
- return Unit.MEGABYTE;
+ return Unit.Kilobit;
case "mb":
case "Mb":
case "m":
- return Unit.MEGABIT;
- case "GB":
- case "G":
- return Unit.GIGABYTE;
+ return Unit.Megabit;
case "Gb":
case "gb":
case "g":
- return Unit.GIGABIT;
- case "TB":
- case "T":
- return Unit.TERABYTE;
+ return Unit.Gigabit;
case "Tb":
case "tb":
case "t":
- return Unit.TERABIT;
- case "PB":
- case "P":
- return Unit.PETABYTE;
+ return Unit.Terabit;
case "Pb":
case "pb":
case "p":
- return Unit.PETABIT;
+ return Unit.Petabit;
+ case "Eb":
+ case "eb":
+ case "e":
+ return Unit.Exabit;
+
+ default:
+ throw new ArgumentOutOfRangeException("Unrecognized abbreviation for data size unit " + unitNameOrAbbreviation);
+ }
+ }
+
+ private static ulong CountBitsInUnit(Unit sourceUnit) {
+ switch (sourceUnit) {
+ case Unit.Byte:
+ return 8;
+ case Unit.Kilobyte:
+ return (ulong) 8 << 10;
+ case Unit.Megabyte:
+ return (ulong) 8 << 20;
+ case Unit.Gigabyte:
+ return (ulong) 8 << 30;
+ case Unit.Terabyte:
+ return (ulong) 8 << 40;
+ case Unit.Petabyte:
+ return (ulong) 8 << 50;
+ case Unit.Exabyte:
+ return (ulong) 8 << 60;
+
+ case Unit.Bit:
+ return 1;
+ case Unit.Kilobit:
+ return 1000L;
+ case Unit.Megabit:
+ return 1000L * 1000;
+ case Unit.Gigabit:
+ return 1000L * 1000 * 1000;
+ case Unit.Terabit:
+ return 1000L * 1000 * 1000 * 1000;
+ case Unit.Petabit:
+ return 1000L * 1000 * 1000 * 1000 * 1000;
+ case Unit.Exabit:
+ return 1000L * 1000 * 1000 * 1000 * 1000 * 1000;
+
default:
- throw new ArgumentOutOfRangeException("Unrecognized abbreviation for data size unit " + abbreviation);
+ throw new ArgumentOutOfRangeException(nameof(sourceUnit), sourceUnit, null);
}
}
- public static double convert(long inputBytes, Unit destinationScale) {
- return inputBytes * 8.0 / toBits(destinationScale);
+ private static Unit ForMagnitude(int orderOfMagnitude, bool useBitsInsteadOfBytes) {
+ switch (orderOfMagnitude) {
+ case 0:
+ return useBitsInsteadOfBytes ? Unit.Bit : Unit.Byte;
+ case 1:
+ return useBitsInsteadOfBytes ? Unit.Kilobit : Unit.Kilobyte;
+ case 2:
+ return useBitsInsteadOfBytes ? Unit.Megabit : Unit.Megabyte;
+ case 3:
+ return useBitsInsteadOfBytes ? Unit.Gigabit : Unit.Gigabyte;
+ case 4:
+ return useBitsInsteadOfBytes ? Unit.Terabit : Unit.Terabyte;
+ case 5:
+ return useBitsInsteadOfBytes ? Unit.Petabit : Unit.Petabyte;
+ default:
+ return useBitsInsteadOfBytes ? Unit.Exabit : Unit.Exabyte;
+ }
}
- public static double convert(double inputSize, Unit inputScale, Unit destinationScale) {
- return inputSize * toBits(inputScale) / toBits(destinationScale);
+ ///
+ /// Format as a string. The quantity is formatted as a number using the current culture's numeric formatting information,
+ /// such as thousands separators and precision. The unit's short abbreviation is appended after a space.
+ /// new DataSize(1536).ConvertToUnit(Unit.Kilobyte).ToString(); → 1.50 KB
+ ///
+ /// String with the formatted data quantity and unit abbreviation, separated by a space.
+ public override string ToString() {
+ return $"{Quantity:N} {Unit.ToAbbreviation()}";
}
- public static (double value, Unit unit) convert(long inputBytes, bool toBytesNotBits = true) {
- int orderOfMagnitude = (int) Math.Max(0, Math.Floor(Math.Log(Math.Abs(inputBytes), toBytesNotBits ? 1024 : 1000)));
- Unit unit = forMagnitude(orderOfMagnitude, toBytesNotBits);
- double scaledValue = convert(inputBytes, unit);
- return (scaledValue, unit);
+ ///
+ /// Format as a string. The quantity is formatted as a number using the current culture's numeric formatting information,
+ /// such as thousands separators. The number of digits after the decimal place is specified as the precision parameter,
+ /// overriding the culture's default numeric precision.
+ ///
+ /// Number of digits after the decimal place to use when formatting the quantity as a number. The
+ /// default for en-US is 2. To use the default for the current culture, pass the value -1, or call
+ /// .
+ /// String with the formatted data quantity and unit abbreviation, separated by a space.
+ public string ToString(int precision) {
+ var culture = (CultureInfo) CultureInfo.CurrentCulture.Clone();
+ if (precision >= 0) {
+ culture.NumberFormat.NumberDecimalDigits = precision;
+ }
+
+ return Quantity.ToString("N", culture) + " " + Unit.ToAbbreviation();
}
}
diff --git a/DataSizeUnits/DataSizeFormatter.cs b/DataSizeUnits/DataSizeFormatter.cs
index 107ea7b..0b8bcc3 100644
--- a/DataSizeUnits/DataSizeFormatter.cs
+++ b/DataSizeUnits/DataSizeFormatter.cs
@@ -4,7 +4,37 @@
namespace DataSizeUnits {
- internal class DataSizeFormatter: IFormatProvider, ICustomFormatter {
+ ///
+ /// An for formatting bytes with unit conversion, numeric precision, and unit abbreviation. The input value is always an of bytes.
+ /// You can also use the equivalent if you prefer to call a method on an existing instance.
+ /// The format string (e.g. A0) controls the unit and precision of the output string, and it is case-sensitive to distinguish between bit and byte units. It consists of a left (A) and right (0) side, both of which are optional.
+ /// The left side of the format string is the desired data size unit, such as MB for megabytes. It can be any unit abbreviation or name, from B to Petabyte and b to petabit. You can also supply A to have the formatter automatically normalize the data size to the best fit unit of bytes, or a to normalize to the best fit unit of bits. If this is omitted, it will default to A.
+ /// The right side of the format is the numeric precision of the output. It can be any non-negative integer, representing the number of digits after the decimal point. You can supply 0 to output an integer without any decimal point. Precision reduction uses the same rounding rules as other numeric formatting operations in .NET. If this is omitted, it defaults to the current culture's numeric precision, which is 2 for en-US.
+ /// In the below examples, the data size is 1,048,576 bytes.
+ /// Example: string.format(new DataSizeFormatter(), "{0:A0}", 1_048_576); // 1 MB
+ ///
+ /// Format stringExample output - description
+ /// A1.00 MB - convert to bytes and automatically normalize
+ /// A01 MB - convert to bytes and automatically normalize with 0 precision
+ /// a8.39 mb - convert to bits and automatically normalize
+ /// B01,048,576 B - convert to bytes with 0 precision
+ /// b08,388,608 b - convert to bits with 0 precision
+ /// KB1024.00 KB - convert to kilobytes
+ /// kb18388.6 kb - convert to kilobits with 1 digit of precision
+ /// MB1.00 MB - convert to megabytes
+ /// mb8.39 mb - convert to megabits
+ ///
+ ///
+ ///
+ /// var fileSize = 1474560;
+ /// string.format(new DataSizeFormatter(), "{0:A1}", fileSize); // 1.4 MB
+ /// string.format(new DataSizeFormatter(), "{0:A}", fileSize); // 1.41 MB (default culture precision)
+ /// string.format(new DataSizeFormatter(), "{0:KB0}", fileSize); // 1,440 KB
+ ///
+ ///
+ public class DataSizeFormatter: IFormatProvider, ICustomFormatter {
+
+ private const string DefaultFormat = "A";
public object GetFormat(Type formatType) {
return formatType == typeof(ICustomFormatter) ? this : null;
@@ -15,20 +45,23 @@ public string Format(string format, object arg, IFormatProvider formatProvider)
return null;
}
+ DataSize dataSize;
+ dataSize.Unit = Unit.Byte;
+
if (string.IsNullOrEmpty(format)) {
- format = "B";
+ format = DefaultFormat;
}
- long bytes;
try {
- bytes = Convert.ToInt64(arg);
+ long bytes = Convert.ToInt64(arg);
+ dataSize.Quantity = bytes;
} catch (Exception) {
- return handleOtherFormats(format, arg);
+ return HandleOtherFormats(format, arg);
}
string unitString = Regex.Match(format, @"^[a-z]+", RegexOptions.IgnoreCase).Value;
if (string.IsNullOrEmpty(unitString)) {
- unitString = "B";
+ unitString = DefaultFormat;
}
if (!int.TryParse(Regex.Match(format, @"\d+$").Value, out int precision)) {
@@ -36,29 +69,17 @@ public string Format(string format, object arg, IFormatProvider formatProvider)
}
if (unitString.ToLowerInvariant() == "a") {
- (double scaledValue, DataSize.Unit scaledUnit) = DataSize.convert(bytes, unitString == "A");
- return DataSizeFormatter.format(scaledValue, scaledUnit, precision);
+ return dataSize.Normalize(unitString == "a").ToString(precision);
} else {
try {
- DataSize.Unit unit = DataSize.forAbbreviation(unitString);
- double scaledValue = DataSize.convert(bytes, unit);
- return DataSizeFormatter.format(scaledValue, unit, precision);
+ return dataSize.ConvertToUnit(DataSize.ParseUnit(unitString)).ToString(precision);
} catch (ArgumentOutOfRangeException) {
- return handleOtherFormats(format, arg);
+ return HandleOtherFormats(format, arg);
}
}
}
- private static string format(double value, DataSize.Unit unit, int precision) {
- var culture = (CultureInfo) CultureInfo.CurrentCulture.Clone();
- if (precision >= 0) {
- culture.NumberFormat.NumberDecimalDigits = precision;
- }
-
- return value.ToString("N", culture) + " " + DataSize.toAbbreviation(unit);
- }
-
- private static string handleOtherFormats(string format, object arg) {
+ private static string HandleOtherFormats(string format, object arg) {
try {
if (arg is IFormattable formattable) {
return formattable.ToString(format, CultureInfo.CurrentCulture);
diff --git a/DataSizeUnits/DataSizeUnits.csproj b/DataSizeUnits/DataSizeUnits.csproj
index 7e49a6a..3b1bde9 100644
--- a/DataSizeUnits/DataSizeUnits.csproj
+++ b/DataSizeUnits/DataSizeUnits.csproj
@@ -2,17 +2,21 @@
netstandard2.0
- 1.0.0
+ 2.0.0Ben HutchisonDataSizeUnitsDataSizeUnits
- Deal with data size units in C# (bytes, kilobytes, megabytes, and others).
- 2019 Ben Hutchison
+ Convert and format data size units in .NET (bits, bytes, kilobits, kilobytes, and others).
+ 2020 Ben Hutchisonhttps://github.com/Aldaviva/DataSizeUnitshttps://github.com/Aldaviva/DataSizeUnits.gitgitApache-2.0
- data-size data-units file-size storage-space byte kilobyte megabyte gigabyte terabyte petabyte
+ data-size data-units file-size storage-space byte kilobyte megabyte gigabyte bit kilobit megabit gigabit
+ false
+ True
+ true
+ snupkg
diff --git a/DataSizeUnits/Unit.cs b/DataSizeUnits/Unit.cs
new file mode 100644
index 0000000..163215e
--- /dev/null
+++ b/DataSizeUnits/Unit.cs
@@ -0,0 +1,131 @@
+using System;
+
+namespace DataSizeUnits {
+
+ ///
+ /// Orders of magnitude of data, from bit and byte to exabit and exabyte.
+ /// Kilobits and other *bits units are multiples of 1000 of the next smaller unit. For example, a megabit is 1,000,000 bits (1000 * 1000).
+ /// Kilobytes and other *bytes units are multiples of 1024 of the next smaller unit. For example, a megabyte is 1,048,576 bytes (1024 * 1024).
+ ///
+ public enum Unit {
+
+ ///
+ /// 1 bit
+ ///
+ Bit,
+
+ ///
+ /// 8 bits
+ ///
+ Byte,
+
+ ///
+ /// 1000 bits
+ ///
+ Kilobit,
+
+ ///
+ /// 1024 bytes
+ ///
+ Kilobyte,
+
+ ///
+ /// 1000 kilobits, or 1,000,000 bits
+ ///
+ Megabit,
+
+ ///
+ /// 1024 kilobytes, or 1,048,576 bytes
+ ///
+ Megabyte,
+
+ ///
+ /// 1000 megabits, or 1,000,000,000 bits
+ ///
+ Gigabit,
+
+ ///
+ /// 1024 megabytes, or 1,073,741,824 bytes
+ ///
+ Gigabyte,
+
+ ///
+ /// 1000 gigabits, or 1,000,000,000,000 bits
+ ///
+ Terabit,
+
+ ///
+ /// 1024 gigabytes, or 1,099,511,627,776 bytes
+ ///
+ Terabyte,
+
+ ///
+ /// 1000 terabits, or 1,000,000,000,000,000 bits
+ ///
+ Petabit,
+
+ ///
+ /// 1024 terabytes, or 1,125,899,906,842,624 bytes
+ ///
+ Petabyte,
+
+ ///
+ /// 1000 petabits, or 1,000,000,000,000,000,000 bits
+ ///
+ Exabit,
+
+ ///
+ /// 1024 petabytes, or 1,152,921,504,606,846,976 bytes
+ ///
+ Exabyte
+
+ }
+
+ public static class UnitExtensions {
+
+ ///
+ /// Get the abbreviation for this unit.
+ /// Byte values are always uppercase: B, KB, MB, GB, TB, PB, EB.
+ /// Bit values are always lowercase: b, kb, mb, gb, tb, pb, eb.
+ ///
+ /// Two letter abbreviation (one letter for bit or byte).
+ public static string ToAbbreviation(this Unit unit) {
+ switch (unit) {
+ case Unit.Byte:
+ return "B";
+ case Unit.Kilobyte:
+ return "KB";
+ case Unit.Megabyte:
+ return "MB";
+ case Unit.Gigabyte:
+ return "GB";
+ case Unit.Terabyte:
+ return "TB";
+ case Unit.Petabyte:
+ return "PB";
+ case Unit.Exabyte:
+ return "EB";
+
+ case Unit.Bit:
+ return "b";
+ case Unit.Kilobit:
+ return "kb";
+ case Unit.Megabit:
+ return "mb";
+ case Unit.Gigabit:
+ return "gb";
+ case Unit.Terabit:
+ return "tb";
+ case Unit.Petabit:
+ return "pb";
+ case Unit.Exabit:
+ return "eb";
+
+ default:
+ throw new ArgumentOutOfRangeException(nameof(unit), unit, null);
+ }
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/Readme.md b/Readme.md
index 6ff47e0..f004125 100644
--- a/Readme.md
+++ b/Readme.md
@@ -1,53 +1,58 @@
# DataSizeUnits
-Deal with data size units in C# (bytes, kilobytes, megabytes, and others).
+Convert and format data size units in .NET (bits, bytes, kilobits, kilobytes, and others).
## Features
-- **Convert a number of bytes to different units** (kilobytes, megabytes, gigabytes, terabytes, petabytes)
- - 2,097,152 bytes to kilobytes → 2048 KB
+- **Convert** between many units of digital information, including bits, bytes, and their higher-order units (kilobits and kilobytes and the rest, up to and including exabits and exabytes)
+ - 150 Mbit → 17.8 MByte
```cs
- double scaled = DataSize.convert(2_097_152, Unit.KILOBYTE);
- // scaled == 2048
+ DataSize sizeInMegabytes = new DataSize(150, Unit.Megabit).ConvertToUnit(Unit.Megabyte);
+ // sizeInMegabytes.Quantity == 17.8
```
-- **Convert a number of bytes to an automatically-selected unit** based on its size
+
+- **Normalize** a number of bytes to an automatically-selected unit based on its magnitude
- 2,097,152 bytes → 2 MB
```cs
- (double scaled, Unit unit) = DataSize.convert(2_097_152);
- // scaled == 2
- // unit == Unit.MEGABYTE
+ DataSize normalized = new DataSize(2_097_152).Normalize();
+ // normalized.Quantity == 2.0
+ // normalized.Unit == Unit.Megabyte
```
- 2,097,152 bytes → 16.78 mbit
```cs
- (double scaled, Unit unit) = DataSize.convert(2_097_152, false);
- // scaled == 16.78
- // unit == Unit.MEGABIT
- ```
- - The unit will be automatically selected so the value is greater than or equal to 1 of that unit, and less than 1 of the next largest unit. For example, 2,097,152 bytes is greater than or equal to 1 MB and less than 1 GB, so it is converted to MB.
-- **Convert between any units**, including any combination of bits, bytes, and their higher-order units (kilobits and kilobytes and the rest, up to and including petabits and petabytes)
- - 150 Mbit → 17.8 MByte
- ```cs
- double scaled = DataSize.convert(150, Unit.MEGABIT, Unit.MEGABYTE);
- // scaled == 17.8
+ DataSize normalized = new DataSize(2_097_152).Normalize(true);
+ // normalized.Quantity == 16.78
+ // normalized.Unit == Unit.Megabit
```
-- **Interpret** different suffices
- - Megabyte, MByte, mebibit, mib, mibit, MB, and M are all megabytes
+ - The unit will be automatically selected so the value is greater than or equal to 1 of that unit, and less than 1 of the next largest unit. For example, 2,097,152 bytes is greater than or equal to 1 MB and less than 1 GB, so it is normalized to MB.
+
+- **Parse** unit names and abbreviations
+ - Megabyte, MByte, mebibyte, MiB, MB, and M are all megabytes
```cs
- Unit unit = DataSize.forAbbreviation("mebibit");
- // unit == Unit.MEGABYTE
+ Unit unit = DataSize.ParseUnit("mib");
+ // unit == Unit.Megabyte
```
- - Default suffices for each unit are of the short, case-sensitive forms.
+ - Default abbreviations for each unit are of the short, case-sensitive forms.
```cs
- string abbreviation = DataSize.toAbbreviation(Unit.TERABYTE);
+ string abbreviation = Unit.Terabyte.toAbbreviation();
// abbreviation == "TB"
```
+
- **Format** bytes as a string with different unit and precision options
- - `{1536:KB1}` → `1.5 KB`
+ - 1,536 bytes to kilobytes, 1 digit after the decimal point → `1.5 KB`
+ ```cs
+ string formatted = new DataSize(1536).ConvertToUnit(Unit.Kilobyte).ToString(1);
+ // formatted == "1.5 KB"
+ ```
+ ```cs
+ string formatted = string.Format(new DataSizeFormatter(), "Size: {0:KB1}", 1536);
+ // formatted == "Size: 1.5 KB"
+ ```
```cs
- string formatted = string.Format(DataSize.FORMATTER, "Size: {0:KB1}", 1536);
+ string formatted = string.Format(new DataSizeFormatter(), "Size: {0:A1}", 1536);
// formatted == "Size: 1.5 KB"
```
- - The format specifier (`KB1` above) is made up of two optional parts, the destination unit (`KB`) and the precision (`1`).
- - The destination unit is the data size unit to which you want the input bytes to be converted. You can pass any data size unit abbreviation. Case matters for ambiguous units, like `kB`/`KB`/`K` for kilobytes and `kb`/`k`/`Kb` for kilobits. Unambiguous units like `kilobyte`/`kbyte`/`kibibit`/`kib`/`kibit` can be provided in any case. You can also specify `A` to automatically select the unit of bytes and higher magnitudes, and likewise `a` for bits, which is the default behavior if you omit the destination unit.
- - The precision is the number of digits after the decimal place. If you omit this, it will use the default number format value for the culture of the current thread, for example 2. Set this to `0` if you want integers only.
+ - The format specifier (like `KB1` above) is made up of two optional parts, the destination unit (`KB`) and the precision (`1`).
+ - The destination unit (`KB`) is the data size unit to which you want the input bytes to be converted. You can pass any data size unit abbreviation. Case matters for ambiguous units, like `kB`/`KB`/`K` for kilobytes and `kb`/`k`/`Kb` for kilobits. Unambiguous units like `kilobyte`/`kbyte`/`kibibyte`/`kib` can be provided in any case. You can also specify **`A`** to automatically normalize the unit of bytes and higher magnitudes, which is the default behavior if you omit the destination unit, and **`a`** normalizes to bits.
+ - The precision (`1`) is the number of digits after the decimal place. If you omit this, it will use the default number format value for the culture of the current thread, for example 2. Set this to `0` if you want integers only.
## Install
[This package is available in the NuGet Gallery.](https://www.nuget.org/packages/DataSizeUnits/)
diff --git a/Tests/DataSizeFormatterTests.cs b/Tests/DataSizeFormatterTests.cs
index c7807aa..0876283 100644
--- a/Tests/DataSizeFormatterTests.cs
+++ b/Tests/DataSizeFormatterTests.cs
@@ -7,35 +7,37 @@ namespace Tests {
public class DataSizeFormatterTests {
[Theory]
- [MemberData(nameof(formatData))]
- [MemberData(nameof(precisionData))]
- [MemberData(nameof(unitData))]
- public void format(ulong inputBytes, string formatSyntax, string expectedOutput) {
+ [MemberData(nameof(FormatData))]
+ [MemberData(nameof(PrecisionData))]
+ [MemberData(nameof(UnitData))]
+ public void FormatUsingCustomFormatter(ulong inputBytes, string formatSyntax, string expectedOutput) {
string formatString = "{0:" + formatSyntax + "}";
- string actualOutput = string.Format(DataSize.FORMATTER, formatString, inputBytes);
- Assert.Equal(expectedOutput, actualOutput);
+ string actual = string.Format(new DataSizeFormatter(), formatString, inputBytes);
+ Assert.Equal(expectedOutput, actual);
}
- public static TheoryData formatData = new TheoryData {
+ public static TheoryData FormatData = new TheoryData {
{ 0, "", "0.00 B" },
{ 0, "A", "0.00 B" },
{ 1024, "A", "1.00 KB" },
+ { 1000, "a", "8.00 kb" },
{ 1024 * 1024, "A", "1.00 MB" },
{ 1024 * 1024 * 1024, "A", "1.00 GB" },
{ 1024L * 1024 * 1024 * 1024, "A", "1.00 TB" },
- { 1024L * 1024 * 1024 * 1024 * 1024, "A", "1.00 PB" }
+ { 1024L * 1024 * 1024 * 1024 * 1024, "A", "1.00 PB" },
+ { 1024L * 1024 * 1024 * 1024 * 1024 * 1024, "A", "1.00 EB" }
};
- public static TheoryData precisionData = new TheoryData {
+ public static TheoryData PrecisionData = new TheoryData {
{ 9_995_326_316_544, "A", "9.09 TB" },
{ 9_995_326_316_544, "A0", "9 TB" },
- { 9_995_326_316_544, "1", "9,995,326,316,544.0 B" },
+ { 9_995_326_316_544, "1", "9.1 TB" },
{ 9_995_326_316_544, "A1", "9.1 TB" },
{ 9_995_326_316_544, "A2", "9.09 TB" },
{ 9_995_326_316_544, "A3", "9.091 TB" }
};
- public static TheoryData unitData = new TheoryData {
+ public static TheoryData UnitData = new TheoryData {
{ 9_995_326_316_544, "B0", "9,995,326,316,544 B" },
{ 9_995_326_316_544, "K0", "9,761,060,856 KB" },
{ 9_995_326_316_544, "KB0", "9,761,060,856 KB" },
@@ -43,12 +45,14 @@ public void format(ulong inputBytes, string formatSyntax, string expectedOutput)
{ 9_995_326_316_544, "GB0", "9,309 GB" },
{ 9_995_326_316_544, "TB0", "9 TB" },
{ 9_995_326_316_544, "PB0", "0 PB" },
+ { 9_995_326_316_544, "EB0", "0 EB" },
{ 9_995_326_316_544, "byte0", "9,995,326,316,544 B" },
{ 9_995_326_316_544, "kilobyte0", "9,761,060,856 KB" },
{ 9_995_326_316_544, "megabyte0", "9,532,286 MB" },
{ 9_995_326_316_544, "gigabyte0", "9,309 GB" },
{ 9_995_326_316_544, "terabyte0", "9 TB" },
{ 9_995_326_316_544, "petabyte0", "0 PB" },
+ { 9_995_326_316_544, "exabyte0", "0 EB" },
{ 9_995_326_316_544_000, "b0", "79,962,610,532,352,000 b" },
{ 9_995_326_316_544_000, "kb0", "79,962,610,532,352 kb" },
{ 9_995_326_316_544_000, "k0", "79,962,610,532,352 kb" },
@@ -56,48 +60,62 @@ public void format(ulong inputBytes, string formatSyntax, string expectedOutput)
{ 9_995_326_316_544_000, "gb0", "79,962,611 gb" },
{ 9_995_326_316_544_000, "tb0", "79,963 tb" },
{ 9_995_326_316_544_000, "pb0", "80 pb" },
+ { 9_995_326_316_544_000, "eb0", "0 eb" },
{ 9_995_326_316_544_000, "bit0", "79,962,610,532,352,000 b" },
{ 9_995_326_316_544_000, "kilobit0", "79,962,610,532,352 kb" },
{ 9_995_326_316_544_000, "megabit0", "79,962,610,532 mb" },
{ 9_995_326_316_544_000, "gigabit0", "79,962,611 gb" },
{ 9_995_326_316_544_000, "terabit0", "79,963 tb" },
{ 9_995_326_316_544_000, "petabit0", "80 pb" },
+ { 9_995_326_316_544_000, "exabit0", "0 eb" }
};
- [Theory, MemberData(nameof(otherData))]
- public void handleOtherFormats(object otherInput, string expectedOutput) {
- string actualOutput = string.Format(DataSize.FORMATTER, "{0:D}", otherInput);
+ [Fact]
+ public void FormatUsingToString() {
+ string actual = new DataSize(1474560).ToString();
+ Assert.Equal("1,474,560.00 B", actual);
+ }
+
+ [Fact]
+ public void ConvertAndFormatUsingToString() {
+ string actual = new DataSize(1474560).Normalize().ToString();
+ Assert.Equal("1.41 MB", actual);
+ }
+
+ [Theory, MemberData(nameof(OtherData))]
+ public void HandleOtherFormats(object otherInput, string expectedOutput) {
+ string actualOutput = string.Format(new DataSizeFormatter(), "{0:D}", otherInput);
Assert.Equal(expectedOutput, actualOutput);
}
- public static TheoryData