@@ -6,20 +6,31 @@ namespace ByteFlow
66{
77 /// <summary>
88 /// Provides extension methods for converting between raw byte counts and human-readable sizes.
9+ /// Supports both IEC (binary: KiB, MiB, GiB) and SI (decimal: KB, MB, GB) unit standards,
10+ /// and allows culture-aware formatting and parsing.
911 /// </summary>
1012 public static class HumanBytesExtensions
1113 {
12- private static readonly string [ ] SizeSuffixes = { "B" , "KB" , "MB" , "GB" , "TB" , "PB" } ;
13-
1414 /// <summary>
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.
15+ /// Converts a number of bytes into a human-readable string using either
16+ /// SI (decimal: KB, MB, GB) or IEC (binary: KiB, MiB, GiB) units.
1717 /// </summary>
1818 /// <param name="bytes">The size in bytes.</param>
1919 /// <param name="decimalPlaces">Number of decimal places to display.</param>
2020 /// <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 )
21+ /// <param name="formatProvider">
22+ /// The culture to use for formatting (e.g. decimal separator).
23+ /// Defaults to <see cref="CultureInfo.InvariantCulture"/>.
24+ /// </param>
25+ /// <returns>
26+ /// A formatted string such as "1.23 MB" (SI, en-US) or "1,23 MB" (SI, de-DE).
27+ /// </returns>
28+ /// <exception cref="ArgumentOutOfRangeException">Thrown if <paramref name="bytes"/> is negative.</exception>
29+ public static string ToHumanBytes (
30+ this long bytes ,
31+ int decimalPlaces = 2 ,
32+ UnitStandard standard = UnitStandard . IEC ,
33+ IFormatProvider formatProvider = null )
2334 {
2435 if ( bytes < 0 )
2536 throw new ArgumentOutOfRangeException ( nameof ( bytes ) , "Value must be non-negative." ) ;
@@ -40,18 +51,28 @@ public static string ToHumanBytes(this long bytes, int decimalPlaces = 2, UnitSt
4051 }
4152
4253 double adjusted = bytes / suffixes [ mag ] . Factor ;
43- return string . Format ( CultureInfo . InvariantCulture ,
44- $ "{{0:F{ decimalPlaces } }} {{1}}", adjusted , suffixes [ mag ] . Symbol ) ;
54+ string number = adjusted . ToString ( $ "F{ decimalPlaces } ", formatProvider ?? CultureInfo . InvariantCulture ) ;
55+
56+ return $ "{ number } { suffixes [ mag ] . Symbol } ";
4557 }
4658
4759 /// <summary>
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.
60+ /// Parses a human-readable size string (e.g. "2.5 GB" or "2, 5 GiB") back into bytes,
61+ /// using either SI (decimal) or IEC (binary) interpretation.
5062 /// </summary>
51- /// <param name="input">The input string, e.g. "1 KB", "1 KiB", "1 MB".</param>
63+ /// <param name="input">The input string, e.g. "1 KB", "1 KiB", "2.5 MB".</param>
5264 /// <param name="standard">Whether to interpret units as SI (base 1000) or IEC (base 1024).</param>
65+ /// <param name="formatProvider">
66+ /// The culture to use for parsing (e.g. decimal separator).
67+ /// Defaults to <see cref="CultureInfo.InvariantCulture"/>.
68+ /// </param>
5369 /// <returns>The size in bytes.</returns>
54- public static long ToBytes ( this string input , UnitStandard standard = UnitStandard . IEC )
70+ /// <exception cref="ArgumentNullException">Thrown if <paramref name="input"/> is null or whitespace.</exception>
71+ /// <exception cref="FormatException">Thrown if the input string cannot be parsed.</exception>
72+ public static long ToBytes (
73+ this string input ,
74+ UnitStandard standard = UnitStandard . IEC ,
75+ IFormatProvider formatProvider = null )
5576 {
5677 if ( string . IsNullOrWhiteSpace ( input ) )
5778 throw new ArgumentNullException ( nameof ( input ) ) ;
@@ -64,29 +85,61 @@ public static long ToBytes(this string input, UnitStandard standard = UnitStanda
6485 if ( input . EndsWith ( symbol , StringComparison . OrdinalIgnoreCase ) )
6586 {
6687 var numberPart = input . Substring ( 0 , input . Length - symbol . Length ) . Trim ( ) ;
67- if ( ! double . TryParse ( numberPart , NumberStyles . Float , CultureInfo . InvariantCulture , out var value ) )
88+ if ( ! double . TryParse (
89+ numberPart ,
90+ NumberStyles . Float ,
91+ formatProvider ?? CultureInfo . InvariantCulture ,
92+ out var value ) )
6893 throw new FormatException ( $ "Invalid number format: { numberPart } ") ;
94+
6995 return ( long ) ( value * factor ) ;
7096 }
7197 }
7298
7399 throw new FormatException ( $ "Invalid size suffix in: { input } ") ;
74100 }
75101
102+ /// <summary>
103+ /// Safely parses a human-readable string into bytes.
104+ /// Returns <c>true</c> if parsing succeeds; otherwise <c>false</c>.
105+ /// </summary>
106+ /// <param name="input">The input string.</param>
107+ /// <param name="result">The parsed byte value if successful, or 0 otherwise.</param>
108+ /// <returns><c>true</c> if parsing was successful; otherwise <c>false</c>.</returns>
109+ public static bool TryParseHumanBytes ( this string input , out long result )
110+ {
111+ try
112+ {
113+ result = input . ToBytes ( ) ;
114+ return true ;
115+ }
116+ catch
117+ {
118+ result = 0 ;
119+ return false ;
120+ }
121+ }
122+
76123 /// <summary>
77124 /// Safely parses a human-readable string into bytes, using either SI (decimal) or IEC (binary) units.
78125 /// </summary>
79126 /// <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>
127+ /// <param name="result">The parsed byte value if successful, or 0 otherwise .</param>
81128 /// <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 )
129+ /// <param name="formatProvider">
130+ /// The culture to use for parsing (e.g. decimal separator).
131+ /// Defaults to <see cref="CultureInfo.InvariantCulture"/>.
132+ /// </param>
133+ /// <returns><c>true</c> if parsing was successful; otherwise <c>false</c>.</returns>
134+ public static bool TryParseHumanBytes (
135+ this string input ,
136+ out long result ,
137+ UnitStandard standard = UnitStandard . IEC ,
138+ IFormatProvider formatProvider = null )
86139 {
87140 try
88141 {
89- result = input . ToBytes ( standard ) ;
142+ result = input . ToBytes ( standard , formatProvider ) ;
90143 return true ;
91144 }
92145 catch
@@ -96,6 +149,8 @@ public static bool TryParseHumanBytes(this string input, out long result, UnitSt
96149 }
97150 }
98151
152+ // --- Unit suffix definitions ---
153+
99154 private static readonly ( string Symbol , double Factor ) [ ] SiSuffixes =
100155 {
101156 ( "B" , 1d ) ,
0 commit comments