@@ -12,70 +12,81 @@ public static class HumanBytesExtensions
1212 private static readonly string [ ] SizeSuffixes = { "B" , "KB" , "MB" , "GB" , "TB" , "PB" } ;
1313
1414 /// <summary>
15- /// Converts a number of bytes into a human-readable string (e.g. "1.18 MB").
15+ /// Converts a number of bytes into a human-readable string
16+ /// using either SI (decimal: KB, MB, GB) or IEC (binary: KiB, MiB, GiB) units.
1617 /// </summary>
1718 /// <param name="bytes">The size in bytes.</param>
1819 /// <param name="decimalPlaces">Number of decimal places to display.</param>
19- public static string ToHumanBytes ( this long bytes , int decimalPlaces = 2 )
20+ /// <param name="standard">Whether to use SI (base 1000) or IEC (base 1024) units.</param>
21+ /// <returns>A formatted string such as "1.23 MB" (SI) or "1.18 MiB" (IEC).</returns>
22+ public static string ToHumanBytes ( this long bytes , int decimalPlaces = 2 , UnitStandard standard = UnitStandard . IEC )
2023 {
2124 if ( bytes < 0 )
2225 throw new ArgumentOutOfRangeException ( nameof ( bytes ) , "Value must be non-negative." ) ;
2326
27+ var suffixes = standard == UnitStandard . SI ? SiSuffixes : IecSuffixes ;
28+
2429 if ( bytes == 0 )
25- return $ "0 { SizeSuffixes [ 0 ] } ";
30+ return $ "0 { suffixes [ 0 ] . Symbol } ";
2631
27- int mag = ( int ) Math . Log ( bytes , 1024 ) ;
28- if ( mag >= SizeSuffixes . Length )
32+ var mag = suffixes . Length - 1 ;
33+ for ( int i = 1 ; i < suffixes . Length ; i ++ )
2934 {
30- mag = SizeSuffixes . Length - 1 ; // clamp to PB
35+ if ( bytes < suffixes [ i ] . Factor )
36+ {
37+ mag = i - 1 ;
38+ break ;
39+ }
3140 }
3241
33- double adjustedSize = bytes / Math . Pow ( 1024 , mag ) ;
34-
42+ double adjusted = bytes / suffixes [ mag ] . Factor ;
3543 return string . Format ( CultureInfo . InvariantCulture ,
36- $ "{{0:F{ decimalPlaces } }} {{1}}", adjustedSize , SizeSuffixes [ mag ] ) ;
44+ $ "{{0:F{ decimalPlaces } }} {{1}}", adjusted , suffixes [ mag ] . Symbol ) ;
3745 }
3846
3947 /// <summary>
40- /// Parses a human-readable size string (e.g. "2.5 GB") back into bytes.
48+ /// Parses a human-readable size string (e.g. "2.5 GB" or "2.5 GiB")
49+ /// back into bytes, using either SI (decimal) or IEC (binary) interpretation.
4150 /// </summary>
42- /// <param name="input">The input string.</param>
43- public static long ToBytes ( this string input )
51+ /// <param name="input">The input string, e.g. "1 KB", "1 KiB", "1 MB".</param>
52+ /// <param name="standard">Whether to interpret units as SI (base 1000) or IEC (base 1024).</param>
53+ /// <returns>The size in bytes.</returns>
54+ public static long ToBytes ( this string input , UnitStandard standard = UnitStandard . IEC )
4455 {
4556 if ( string . IsNullOrWhiteSpace ( input ) )
4657 throw new ArgumentNullException ( nameof ( input ) ) ;
4758
4859 input = input . Trim ( ) ;
60+ var suffixes = standard == UnitStandard . SI ? SiSuffixes : IecSuffixes ;
4961
50- // Order suffixes by length so "KB" matches before "B"
51- foreach ( var suffix in SizeSuffixes . OrderByDescending ( s => s . Length ) )
62+ foreach ( var ( symbol , factor ) in suffixes . OrderByDescending ( s => s . Symbol . Length ) )
5263 {
53- if ( input . EndsWith ( suffix , StringComparison . OrdinalIgnoreCase ) )
64+ if ( input . EndsWith ( symbol , StringComparison . OrdinalIgnoreCase ) )
5465 {
55- string numberPart = input . Substring ( 0 , input . Length - suffix . Length ) . TrimEnd ( ) ;
56-
57- if ( double . TryParse ( numberPart , NumberStyles . Float , CultureInfo . InvariantCulture , out double value ) )
58- {
59- int index = Array . IndexOf ( SizeSuffixes , suffix ) ;
60- return ( long ) ( value * Math . Pow ( 1024 , index ) ) ;
61- }
62-
63- throw new FormatException ( $ "Invalid number format: { numberPart } ") ;
66+ var numberPart = input . Substring ( 0 , input . Length - symbol . Length ) . Trim ( ) ;
67+ if ( ! double . TryParse ( numberPart , NumberStyles . Float , CultureInfo . InvariantCulture , out var value ) )
68+ throw new FormatException ( $ "Invalid number format: { numberPart } ") ;
69+ return ( long ) ( value * factor ) ;
6470 }
6571 }
6672
6773 throw new FormatException ( $ "Invalid size suffix in: { input } ") ;
6874 }
6975
7076 /// <summary>
71- /// Safely parses a human-readable string into bytes.
72- /// Returns false if parsing fails.
77+ /// Safely parses a human-readable string into bytes, using either SI (decimal) or IEC (binary) units.
7378 /// </summary>
74- public static bool TryParseHumanBytes ( this string input , out long result )
79+ /// <param name="input">The input string (e.g. "1 KB", "1 KiB", "2.5 MB").</param>
80+ /// <param name="result">The parsed byte value if successful, or 0 if parsing fails.</param>
81+ /// <param name="standard">Whether to interpret units as SI (base 1000) or IEC (base 1024).</param>
82+ /// <returns>
83+ /// <c>true</c> if parsing was successful; otherwise <c>false</c>.
84+ /// </returns>
85+ public static bool TryParseHumanBytes ( this string input , out long result , UnitStandard standard = UnitStandard . IEC )
7586 {
7687 try
7788 {
78- result = input . ToBytes ( ) ;
89+ result = input . ToBytes ( standard ) ;
7990 return true ;
8091 }
8192 catch
@@ -84,5 +95,25 @@ public static bool TryParseHumanBytes(this string input, out long result)
8495 return false ;
8596 }
8697 }
98+
99+ private static readonly ( string Symbol , double Factor ) [ ] SiSuffixes =
100+ {
101+ ( "B" , 1d ) ,
102+ ( "KB" , 1e3 ) ,
103+ ( "MB" , 1e6 ) ,
104+ ( "GB" , 1e9 ) ,
105+ ( "TB" , 1e12 ) ,
106+ ( "PB" , 1e15 )
107+ } ;
108+
109+ private static readonly ( string Symbol , double Factor ) [ ] IecSuffixes =
110+ {
111+ ( "B" , 1d ) ,
112+ ( "KiB" , 1024d ) ,
113+ ( "MiB" , Math . Pow ( 1024 , 2 ) ) ,
114+ ( "GiB" , Math . Pow ( 1024 , 3 ) ) ,
115+ ( "TiB" , Math . Pow ( 1024 , 4 ) ) ,
116+ ( "PiB" , Math . Pow ( 1024 , 5 ) )
117+ } ;
87118 }
88119}
0 commit comments