20
20
// THE SOFTWARE.
21
21
22
22
using System ;
23
+ using System . Collections . Generic ;
23
24
using System . Globalization ;
24
25
using System . Text . RegularExpressions ;
25
26
using System . Linq ;
@@ -280,17 +281,16 @@ public double As(AmplitudeRatioUnit unit)
280
281
#region Parsing
281
282
282
283
/// <summary>
283
- /// Parse a string of the format "<quantity> <unit>".
284
+ /// Parse a string with one or two quantities of the format "<quantity> <unit>".
284
285
/// </summary>
285
286
/// <example>
286
287
/// Length.Parse("5.5 m", new CultureInfo("en-US"));
287
288
/// </example>
288
289
/// <exception cref="ArgumentNullException">The value of 'str' cannot be null. </exception>
289
290
/// <exception cref="ArgumentException">
290
- /// Expected 2 words. Input string needs to be in the format "<quantity> <unit
291
- /// & gt;".
291
+ /// Expected string to have one or two pairs of quantity and unit in the format
292
+ /// "<quantity& gt; <unit>". Eg. "5.5 m" or "1ft 2in"
292
293
/// </exception>
293
- /// <exception cref="UnitsNetException">Error parsing string.</exception>
294
294
public static AmplitudeRatio Parse ( string str , IFormatProvider formatProvider = null )
295
295
{
296
296
if ( str == null ) throw new ArgumentNullException ( "str" ) ;
@@ -302,41 +302,70 @@ public static AmplitudeRatio Parse(string str, IFormatProvider formatProvider =
302
302
var numRegex = string . Format ( @"[\d., {0}{1}]*\d" , // allows digits, dots, commas, and spaces in the quantity (must end in digit)
303
303
numFormat . NumberGroupSeparator , // adds provided (or current) culture's group separator
304
304
numFormat . NumberDecimalSeparator ) ; // adds provided (or current) culture's decimal separator
305
- var regexString = string . Format ( "(?<value>[-+]?{0}{1}{2}{3}" ,
306
- numRegex , // capture base (integral) Quantity value
307
- @"(?:[eE][-+]?\d+)?)" , // capture exponential (if any), end of Quantity capturing
308
- @"\s?" , // ignore whitespace (allows both "1kg", "1 kg")
309
- @"(?<unit>\S+)" ) ; // capture Unit (non-whitespace) input
310
-
311
- var regex = new Regex ( regexString ) ;
312
- GroupCollection groups = regex . Match ( str . Trim ( ) ) . Groups ;
313
-
314
- var valueString = groups [ "value" ] . Value ;
315
- var unitString = groups [ "unit" ] . Value ;
316
-
317
- if ( valueString == "" || unitString == "" )
305
+ var exponentialRegex = @"(?:[eE][-+]?\d+)?)" ;
306
+ var regexString = string . Format ( @"(?:\s*(?<value>[-+]?{0}{1}{2}{3})?{4}{5}" ,
307
+ numRegex , // capture base (integral) Quantity value
308
+ exponentialRegex , // capture exponential (if any), end of Quantity capturing
309
+ @"\s?" , // ignore whitespace (allows both "1kg", "1 kg")
310
+ @"(?<unit>[^\s\d,]+)" , // capture Unit (non-whitespace) input
311
+ @"(and)?,?" , // allow "and" & "," separators between quantities
312
+ @"(?<invalid>[a-z]*)?" ) ; // capture invalid input
313
+
314
+ var quantities = ParseWithRegex ( regexString , str , formatProvider ) ;
315
+ if ( quantities . Count == 0 )
318
316
{
319
- var ex = new ArgumentException (
320
- "Expected valid quantity and unit. Input string needs to be in the format \" <quantity><unit> or <quantity> <unit>\" ." , "str" ) ;
321
- ex . Data [ "input" ] = str ;
322
- ex . Data [ "formatprovider" ] = formatProvider == null ? null : formatProvider . ToString ( ) ;
323
- throw ex ;
317
+ throw new ArgumentException (
318
+ "Expected string to have at least one pair of quantity and unit in the format"
319
+ + " \" <quantity> <unit>\" . Eg. \" 5.5 m\" or \" 1ft 2in\" " ) ;
324
320
}
321
+ return quantities . Aggregate ( ( x , y ) => x + y ) ;
322
+ }
325
323
326
- try
327
- {
328
- AmplitudeRatioUnit unit = ParseUnit ( unitString , formatProvider ) ;
329
- double value = double . Parse ( valueString , formatProvider ) ;
324
+ /// <summary>
325
+ /// Parse a string given a particular regular expression.
326
+ /// </summary>
327
+ /// <exception cref="UnitsNetException">Error parsing string.</exception>
328
+ private static List < AmplitudeRatio > ParseWithRegex ( string regexString , string str , IFormatProvider formatProvider = null )
329
+ {
330
+ var regex = new Regex ( regexString ) ;
331
+ MatchCollection matches = regex . Matches ( str . Trim ( ) ) ;
332
+ var converted = new List < AmplitudeRatio > ( ) ;
330
333
331
- return From ( value , unit ) ;
332
- }
333
- catch ( Exception e )
334
+ foreach ( Match match in matches )
334
335
{
335
- var newEx = new UnitsNetException ( "Error parsing string." , e ) ;
336
- newEx . Data [ "input" ] = str ;
337
- newEx . Data [ "formatprovider" ] = formatProvider == null ? null : formatProvider . ToString ( ) ;
338
- throw newEx ;
336
+ GroupCollection groups = match . Groups ;
337
+
338
+ var valueString = groups [ "value" ] . Value ;
339
+ var unitString = groups [ "unit" ] . Value ;
340
+ if ( groups [ "invalid" ] . Value != "" )
341
+ {
342
+ var newEx = new UnitsNetException ( "Invalid string detected: " + groups [ "invalid" ] . Value ) ;
343
+ newEx . Data [ "input" ] = str ;
344
+ newEx . Data [ "matched value" ] = valueString ;
345
+ newEx . Data [ "matched unit" ] = unitString ;
346
+ newEx . Data [ "formatprovider" ] = formatProvider == null ? null : formatProvider . ToString ( ) ;
347
+ throw newEx ;
348
+ }
349
+ if ( valueString == "" && unitString == "" ) continue ;
350
+
351
+ try
352
+ {
353
+ AmplitudeRatioUnit unit = ParseUnit ( unitString , formatProvider ) ;
354
+ double value = double . Parse ( valueString , formatProvider ) ;
355
+
356
+ converted . Add ( From ( value , unit ) ) ;
357
+ }
358
+ catch ( Exception ex )
359
+ {
360
+ var newEx = new UnitsNetException ( "Error parsing string." , ex ) ;
361
+ newEx . Data [ "input" ] = str ;
362
+ newEx . Data [ "matched value" ] = valueString ;
363
+ newEx . Data [ "matched unit" ] = unitString ;
364
+ newEx . Data [ "formatprovider" ] = formatProvider == null ? null : formatProvider . ToString ( ) ;
365
+ throw newEx ;
366
+ }
339
367
}
368
+ return converted ;
340
369
}
341
370
342
371
/// <summary>
0 commit comments