A simple "human scale" number to string converter






4.88/5 (5 votes)
I needed to show file sizes in a "human friendly format" recently, so I thought I'd knock up a simple method to do it. So the file sizes such as 10123456789 bytes would display as 10.1Gb and so forth.
Introduction
I needed to show file sizes in a "human friendly format" recently, so I thought I'd knock up a simple method to do it. So the file sizes such as 10123456789 bytes would display as 10.1Gb and so forth. Simple, but a pain, since it breaks your concentration to "knock up" a method. I'm sure there are other versions out there, but...some of them are far too complicated for their own good!
The code
Easy to read, I think: it uses an array of ISO suffixes (in ISO they are prefixes, because they come before the quantity type: "G" is the prefix, "b" is the bytes in "12.3 Gb")
/// <summary>
/// Suffix size values (ISO only goes up to "Yotta", but a 64bit int
/// can't even get there - the last two are for completeness.)
/// </summary>
///
private static string[] suffixes = new string[] { "", "k", "M", "G", "T", "P", "E", "Z", "Y" };
/// <summary>
/// Converts a long value to a human scale count
/// Number Human Scale
/// 1 1
/// 10 10
/// 101 101
/// 1012 1.01k
/// 10123 10.1k
/// 101234 101k
/// 1012345 1.01M
/// 10123456 10.1M
/// 101234567 101M
/// 1012345678 1.01G
/// ....
/// </summary>
/// <param name="value">Value to scale</param>
/// <param name="suffix">If supplied, appended to the output string</param>
/// <param name="spacer">If supplied, it separates the numbers and the ISO size indicator</param>
/// <param name="positiveIndicator">If supplied, it prefixes the number</param>
/// <returns></returns>
public static string HumanScale(long value, string suffix = "", string spacer = "", string positiveIndicator = "")
{
string result = Math.Abs(value).ToString();
int digits = result.Length;
if (digits >= 4)
{
digits--; // Groups = (Length - 1) / sizeOfGroup;
// digitsBeforeDecimal = ((Length - 1) % 3) + 1
// This avoids testing and "move down" of groups count
// when we don't want a decimal at all: 123M for example.
int groups = digits / 3;
int digitsBeforeDecimal = digits % 3;
StringBuilder sb = new StringBuilder();
foreach (char c in result.Substring(0, 3))
{
if (digitsBeforeDecimal == -1) sb.Append(System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator);
digitsBeforeDecimal--;
sb.Append(c);
}
result = string.Format("{0}{1}{2}{3}", sb, spacer, suffixes[groups], suffix);
}
return string.Format("{0}{1}", value < 0 ? System.Globalization.CultureInfo.CurrentCulture.NumberFormat.NegativeSign : positiveIndicator, result);
}
Using it is also simple:
long l = 1;
for (int i = 0; i <= 24; i++)
{
Console.WriteLine("{0,2} {1,24} {2}", i, l, HumanScale(l));
l = (l * 10) + (i % 10);
}
Generates:
0 1 1
1 10 10
2 101 101
3 1012 1.01k
4 10123 10.1k
5 101234 101k
6 1012345 1.01M
7 10123456 10.1M
8 101234567 101M
9 1012345678 1.01G
10 10123456789 10.1G
11 101234567890 101G
12 1012345678901 1.01T
13 10123456789012 10.1T
14 101234567890123 101T
15 1012345678901234 1.01P
16 10123456789012345 10.1P
17 101234567890123456 101P
18 1012345678901234567 1.01E
19 -8323287284697205938 -8.32E
20 9000847521575698709 9.00E
21 -2225245152790770990 -2.22E
22 -3805707454198158283 -3.80E
23 -1163586394562479596 -1.16E
24 6810880128084755659 6.81E
Note that the long
value overflows long before Yottabytes are reached!
History
2014-05-25 Original version
2014-05-25 XML comments un-mucked up. Editor removed closing tags, and so forth - so they would have been invalid in Visual Studio when copy / pasted. I do dislike this editor, sometimes...