Skip to content

SvenGroot/Ookii.BinarySize

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Ookii.BinarySize

Ookii.BinarySize is a modern library for parsing and displaying quantities of bytes, using human-readable representation.

It provides functionality to parse numeric values that end with a multiple-byte unit, such as B, KB, MiB, kilobyte, mebibytes, and so on, and to format them for display in the same way. It can automatically choose the best unit, or you can choose the one you want, based on the format string.

  • Supports units with SI prefixes ("KB", "MB", "GB", "TB", "PB", and "EB"), and IEC prefixes ("KiB", "MiB", "GiB", "TiB", "PiB", and "EiB"), with and without the "B".
  • Also supports unabbreviated units for both parsing and formatting: "byte", "kilobyte", "megabyte", "gigabyte", "terabyte", "petabyte", and "exabyte", and their IEC equivalents "kibibyte", "mebibyte", "gibibyte", "tebibyte", "pebibyte", and "exbibyte".
  • Interpret SI prefixes as either powers of two or powers of ten.
  • Parse and store values up to approximately positive and negative 8 EiB, using Int64 (long) as the underlying storage.
  • Support for localizing units and prefixes.
  • Provided as a library for .Net Standard 2.0, .Net Standard 2.1, and .Net 6.0 and up.
  • Implements arithmetic and binary operators, and supports .Net 7 generic math.
  • Trim-friendly.

Besides display formatting and parsing user input, BinarySize provides everything needed to easily use human-readable byte sizes in places such as configuration files, serialized XML and JSON, and command line arguments.

Ookii.BinarySize comes in two packages; the core functionality is in Ookii.BinarySize, and additional extension methods for IAsyncEnumerable<T> are available in the Ookii.BinarySize.Async package. Both are available on NuGet.

Package Version
Ookii.BinarySize NuGet
Ookii.BinarySize.Async NuGet

Keep reading to get started, or check out the samples. You can also try it out online on .Net Fiddle.

To use the library, store your size values as a BinarySize structure, which supports formatting and parsing, along with overloaded operators and other operations provided for convenience.

Formatting

To create a string from a BinarySize value, use the BinarySize.ToString() method, or use the value directly in a compound formatting string.

The default format for BinarySize will automatically use the largest unit where the value is a whole number, using IEC units and a "B" suffix, and a space between the number and the unit. For example, "42 MiB".

You can use a format string to customize the output. This format string can consist of just a multi-byte unit, to use default number formatting followed by that unit, or it can use a numeric format string followed by a unit. Spaces around the unit are preserved. For example "KB", " MiB", "#.0 G", as well as simply "B", are all accepted format strings.

To use unabbreviated units, end the format string with "byte". This will automatically expand the unit and pick between the singular and plural version of "byte" or "bytes". For example, "Mibyte".

To automatically pick the largest unit where the value is a whole number, use "A" instead of an explicit size prefix, or use "S" to pick the largest prefix where the value is larger than 1, but may have a fractional component. This also works with long units; e.g. "Abyte" or "Sibyte". The default format is equivalent to " AiB".

For example, the following displays a value using several different formats:

var value = BinarySize.FromGibi(2.5);
Console.WriteLine($"{value: B} is equal to:");
Console.WriteLine($"Default formatting: {value}");
Console.WriteLine($"Automatic formatting: {value: AB}");
Console.WriteLine($"Shortest formatting: {value:#.0 SiB}");
Console.WriteLine($"Explicit formatting: {value:#,###Ki}");
Console.WriteLine($"Unabbreviated formatting: {value:#.0 Sibyte}");

This outputs the following:

2684354560 B is equal to:
Default formatting: 2560 MiB
Automatic formatting: 2560 MB
Shortest formatting: 2.5 GiB
Explicit formatting: 2,621,440Ki
Unabbreviated formatting: 2.5 gibibytes

In the example above, the default format displays the value in MiB because it is not a whole number of GiB. The "SiB" format string does use the GiB unit, because it allows factional values.

The formatting above uses powers of two for both SI and IEC units. This means that 1 KB is the same as 1 KiB, both equal to 1,024 bytes, 1 MB is equal to 1 MiB, both equaling 1,048,576 bytes, and so on.

If you wish to use the IEC standard where only IEC units are powers of two, and SI units are always powers of ten, this can be done by using a lower-case unit prefix in the format string, without including an 'i'. This applies to explicit units ("k", "m", "g", "t", "p" and "e"), as well as the automatic units "a" and "s", and works with both abbreviated and unabbreviated units.

With this option, 1 kB equals 1,000 bytes, 1 MB equals 1,000,000 bytes, and so on.

The abbreviated unit prefixes will always be output as uppercase, even if they are lowercase in the format string. The only exception is the decimal version of "kilo", which is a lowercase "k" to conform to the SI standard.

var value = BinarySize.FromGibi(2.5);
Console.WriteLine($"{value: B} is equal to (decimal):");
Console.WriteLine($"Automatic formatting: {value: aB}");
Console.WriteLine($"Shortest formatting: {value:#.0 sB}");
Console.WriteLine($"Explicit formatting: {value:#,###k}");
Console.WriteLine($"Unabbreviated formatting: {value:#.0 sbyte}");
Console.WriteLine();

value = (BinarySize)2500000000;
Console.WriteLine($"And {value: B} is equal to (decimal):");
Console.WriteLine($"Automatic formatting: {value: aB}");
Console.WriteLine($"Shortest formatting: {value:#.0 sB}");
Console.WriteLine($"Explicit formatting: {value:#,###k}");
Console.WriteLine($"Unabbreviated formatting: {value:#.0 sbyte}");

This outputs the following:

2684354560 B is equal to (decimal):
Automatic formatting: 2684354560 B
Shortest formatting: 2.7 GB
Explicit formatting: 2,684,355k
Unabbreviated formatting: 2.7 gigabytes

And 2500000000 B is equal to (decimal):
Automatic formatting: 2500 MB
Shortest formatting: 2.5 GB
Explicit formatting: 2,500,000k
Unabbreviated formatting: 2.5 gigabytes

Note that using "aB" formatted 2.5 GiB as plain bytes, since there is no higher decimal prefix that could be used while keeping a whole number.

See BinarySize.ToString() for full documentation on the format string.

Parsing

To parse a string value into a BinarySize, use the BinarySize.Parse() and BinarySize.TryParse() methods. The input can be a number, optionally followed by an SI or IEC multi-byte unit, and optionally ending with a 'B' character. The case of the unit, and any spacing surrounding it, will be ignored.

The following code parses several strings with different styles of unit, and displays their values in bytes on the console.

var values = new[] { "100", "100B", "10 KB", "2.5 MiB", "5G" };
foreach (var value in values)
{
    var size = BinarySize.Parse(value, CultureInfo.InvariantCulture);
    Console.WriteLine($"'{value}' == {size.Value} bytes");
}

This would print the following output.

'100' == 100 bytes
'100B' == 100 bytes
'10 KB' == 10240 bytes
'2.5 MiB' == 2621440 bytes
'5G' == 5368709120 bytes

Just as with formatting, the default behavior is to treat both SI and IEC units as powers of two, as can be seen by the values for "10 KB" and "5G".

To use the IEC standard of interpreting SI units as powers of ten, we can use the BinarySizeOptions.UseIecStandard flag.

var values = new[] { "100", "100B", "10 KB", "2.5 MiB", "5G" };
foreach (var value in values)
{
    var size = BinarySize.Parse(value, BinarySizeOptions.UseIecStandard, NumberStyles.Number, CultureInfo.InvariantCulture);
    Console.WriteLine($"'{value}' == {size: byte}");
}

Which gives this output instead.

'100' == 100 bytes
'100B' == 100 bytes
'10 KB' == 10000 bytes
'2.5 MiB' == 2621440 bytes
'5G' == 5000000000 bytes

As you can see, IEC units ("MiB" in this example) are not affected by this option.

If you want to always treat SI units as powers of ten when parsing, or do so in a context where you cannot specify a BinarySizeOptions value (such as a value that is being serialized), you can instead use the IecBinarySize structure; this is a wrapper around BinarySize which defaults to this parsing behavior.

By default, parsing only accepts abbreviated units. To also allow parsing of full units (e.g. "kilobytes"), use the BinarySizeOptions.AllowLongUnits flag. Use the BinarySizeOptions.AllowLongUnitsOnly flag to disallow the use of abbreviated units, and accept only unabbreviated ones. Both singular and plural units will be accepted regardless of the actual value, and case is ignored.

var values = new[] { "100 bytes", "10 Kilobytes", "2.5 mebibyte", "5giga" };
foreach (var value in values)
{
    var size = BinarySize.Parse(value, BinarySizeOptions.AllowLongUnits, NumberStyles.Number, CultureInfo.InvariantCulture);
    Console.WriteLine($"'{value}' == {size: byte}");
}

Which gives this output.

'100 bytes' == 100 bytes
'10 Kilobytes' == 10240 bytes
'2.5 mebibyte' == 2621440 bytes
'5giga' == 5368709120 bytes

This can also be combined with the BinarySizeOptions.UseIecStandard flag.

Localization

By default, Ookii.BinarySize uses English-language units, regardless of the culture used for formatting or parsing. However, it's possible to create custom localized units using the BinaryUnitInfo class.

The BinaryUnitInfo class defines the strings used for both the abbreviated (short) and unabbreviated (long) versions of base unit ("B" or "byte"), and the various SI and IEC prefixes. When using a custom BinaryUnitInfo, these strings will be used instead of the defaults for both parsing and formatting. The BinaryUnitInfo class also allows you to add a separator in between the prefix and base unit, and to specify how string comparisons are performed when parsing.

BinaryUnitInfo implements the IFormatProvider interface, so it can be used directly with the Parse() and ToString() methods. If this is done, the current culture is used for number formatting.

To use an explicit culture in combination with a custom BinaryUnitInfo object, you can use the WithBinaryUnitInfo() extension method. This returns a new CultureInfo object that wraps the original one, but also supports the provided BinaryUnitInfo object.

For example, the below creates a custom BinaryUnitInfo using French units, and combines it with a French CultureInfo to use the appropriate number formatting.

var unitInfo = new BinaryUnitInfo()
{
    LongByte = "octet",
    LongBytes = "octets",
    LongConnector = "-",
    LongMega = "méga",
    LongMebi = "mébi",
    LongTera = "téra",
    LongTebi = "tébi",
    LongPeta = "péta",
    LongPebi = "pébi",
    // For short (abbreviated) units, only "B" is replaced with "o" in French. The prefixes are the
    // same.
    ShortByte = "o",
    ShortBytes = "o",
};

var culture = CultureInfo.GetCultureInfo("fr-FR").WithBinaryUnitInfo(unitInfo);
var stringValue = binarySizeValue.ToString(" Sibyte", culture);

For a complete example, check out the localization sample.

Other features

Besides offering formatting and parsing, Ookii.BinarySize aims to make using it as convenient as possible, and offers several features for that purpose.

The BinarySize and IecBinarySize structures can be used in contexts where they are automatically serialized, such as configuration files, serialized XML or JSON data, XAML, and others, because they provide a TypeConverter, a JsonConverter, and implement IXmlSerializable.

Ookii.BinarySize also supports modern .Net functionality. It supports parsing from a ReadOnlySpan<char>, and formatting with ISpanFormattable. The .Net 7.0 version of the library also implements ISpanParsable<TSelf>, and supports the interfaces for generic math.

Requirements

Ookii.BinarySize is a class library for use in your own applications for Microsoft .Net. Assemblies are provided targeting the following:

  • .Net Standard 2.0
  • .Net Standard 2.1
  • .Net 6.0
  • .Net 7.0 and later

Building and testing

To build Ookii.BinarySize, make sure you have the following installed:

To build the library, tests and samples, simply use the dotnet build command in the src directory. You can run the unit tests using dotnet test. The tests should pass on all platforms (Windows and Linux have been tested).

The tests are built and run for .Net 8.0, .Net 7.0, .Net 6.0, and .Net Framework 4.8. Running the .Net Framework tests on a non-Windows platform may require the use of Mono.

Ookii.BinarySize uses a strongly-typed resources file, which will not update correctly unless the Resources.resx file is edited with Microsoft Visual Studio. I could not find a way to make this work correctly with both Visual Studio and the dotnet command.

The class library documentation is generated using Sandcastle Help File Builder.

Learn more

About

Ookii.BinarySize is a modern library for parsing and displaying quantities of bytes, using human-readable representation with units such as "KB", "MiB", etc.

Resources

License

Stars

Watchers

Forks