Skip to content

Commit 6fb85f8

Browse files
committed
Merge in pull request #81 from develop
PR was based on develop, so have to manually merge it into master after changing the branching style (again).
2 parents 1aff0e2 + 7353c4b commit 6fb85f8

32 files changed

+1819
-964
lines changed

UnitsNet.Tests/CustomCode/ParseTests.cs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2020
// THE SOFTWARE.
2121

22-
using System;
2322
using System.Globalization;
2423
using NUnit.Framework;
2524
using UnitsNet.Units;
@@ -36,14 +35,14 @@ namespace UnitsNet.Tests.CustomCode
3635
[TestFixture]
3736
public class ParseTests
3837
{
39-
[TestCase("1km", Result=1000)]
38+
[TestCase("1km", Result = 1000)]
4039
[TestCase("1 km", Result = 1000)]
4140
[TestCase("1e-3 km", Result = 1)]
4241
[TestCase("5.5 m", Result = 5.5)]
4342
[TestCase("500,005 m", Result = 500005)]
4443
[TestCase(null, ExpectedExceptionName = "System.ArgumentNullException")]
4544
[TestCase("1", ExpectedExceptionName = "System.ArgumentException")]
46-
[TestCase("km", ExpectedExceptionName = "System.ArgumentException")]
45+
[TestCase("km", ExpectedExceptionName = "UnitsNet.UnitsNetException")]
4746
[TestCase("1 kg", ExpectedExceptionName = "UnitsNet.UnitsNetException")]
4847
public double ParseLengthToMetersUsEnglish(string s)
4948
{
@@ -52,6 +51,21 @@ public double ParseLengthToMetersUsEnglish(string s)
5251
return Length.Parse(s, usEnglish).Meters;
5352
}
5453

54+
[TestCase("1 ft 1 in", Result = 13)]
55+
[TestCase("1ft 1in", Result = 13)]
56+
[TestCase("1' 1\"", Result = 13)]
57+
[TestCase("1'1\"", Result = 13)]
58+
[TestCase("1ft1in", Result = 13)]
59+
[TestCase("1ft and 1in", Result = 13)]
60+
[TestCase("1ft monkey 1in", ExpectedExceptionName = "UnitsNet.UnitsNetException")]
61+
[TestCase("1ft 1invalid", ExpectedExceptionName = "UnitsNet.UnitsNetException")]
62+
public double ParseImperialLengthInchesUsEnglish(string s)
63+
{
64+
var usEnglish = CultureInfo.GetCultureInfo("en-US");
65+
66+
return Length.Parse(s, usEnglish).Inches;
67+
}
68+
5569
/// <exception cref="UnitsNetException">Error parsing string.</exception>
5670
[TestCase("5.5 m", Result = 5.5)]
5771
[TestCase("500 005 m", Result = 500005)]

UnitsNet/GeneratedCode/UnitClasses/Acceleration.g.cs

Lines changed: 62 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
// THE SOFTWARE.
2121

2222
using System;
23+
using System.Collections.Generic;
2324
using System.Globalization;
2425
using System.Text.RegularExpressions;
2526
using System.Linq;
@@ -232,17 +233,16 @@ public double As(AccelerationUnit unit)
232233
#region Parsing
233234

234235
/// <summary>
235-
/// Parse a string of the format "&lt;quantity&gt; &lt;unit&gt;".
236+
/// Parse a string with one or two quantities of the format "&lt;quantity&gt; &lt;unit&gt;".
236237
/// </summary>
237238
/// <example>
238239
/// Length.Parse("5.5 m", new CultureInfo("en-US"));
239240
/// </example>
240241
/// <exception cref="ArgumentNullException">The value of 'str' cannot be null. </exception>
241242
/// <exception cref="ArgumentException">
242-
/// Expected 2 words. Input string needs to be in the format "&lt;quantity&gt; &lt;unit
243-
/// &gt;".
243+
/// Expected string to have one or two pairs of quantity and unit in the format
244+
/// "&lt;quantity&gt; &lt;unit&gt;". Eg. "5.5 m" or "1ft 2in"
244245
/// </exception>
245-
/// <exception cref="UnitsNetException">Error parsing string.</exception>
246246
public static Acceleration Parse(string str, IFormatProvider formatProvider = null)
247247
{
248248
if (str == null) throw new ArgumentNullException("str");
@@ -254,41 +254,70 @@ public static Acceleration Parse(string str, IFormatProvider formatProvider = nu
254254
var numRegex = string.Format(@"[\d., {0}{1}]*\d", // allows digits, dots, commas, and spaces in the quantity (must end in digit)
255255
numFormat.NumberGroupSeparator, // adds provided (or current) culture's group separator
256256
numFormat.NumberDecimalSeparator); // adds provided (or current) culture's decimal separator
257-
var regexString = string.Format("(?<value>[-+]?{0}{1}{2}{3}",
258-
numRegex, // capture base (integral) Quantity value
259-
@"(?:[eE][-+]?\d+)?)", // capture exponential (if any), end of Quantity capturing
260-
@"\s?", // ignore whitespace (allows both "1kg", "1 kg")
261-
@"(?<unit>\S+)"); // capture Unit (non-whitespace) input
262-
263-
var regex = new Regex(regexString);
264-
GroupCollection groups = regex.Match(str.Trim()).Groups;
265-
266-
var valueString = groups["value"].Value;
267-
var unitString = groups["unit"].Value;
268-
269-
if (valueString == "" || unitString == "")
257+
var exponentialRegex = @"(?:[eE][-+]?\d+)?)";
258+
var regexString = string.Format(@"(?:\s*(?<value>[-+]?{0}{1}{2}{3})?{4}{5}",
259+
numRegex, // capture base (integral) Quantity value
260+
exponentialRegex, // capture exponential (if any), end of Quantity capturing
261+
@"\s?", // ignore whitespace (allows both "1kg", "1 kg")
262+
@"(?<unit>[^\s\d,]+)", // capture Unit (non-whitespace) input
263+
@"(and)?,?", // allow "and" & "," separators between quantities
264+
@"(?<invalid>[a-z]*)?"); // capture invalid input
265+
266+
var quantities = ParseWithRegex(regexString, str, formatProvider);
267+
if (quantities.Count == 0)
270268
{
271-
var ex = new ArgumentException(
272-
"Expected valid quantity and unit. Input string needs to be in the format \"<quantity><unit> or <quantity> <unit>\".", "str");
273-
ex.Data["input"] = str;
274-
ex.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString();
275-
throw ex;
269+
throw new ArgumentException(
270+
"Expected string to have at least one pair of quantity and unit in the format"
271+
+ " \"&lt;quantity&gt; &lt;unit&gt;\". Eg. \"5.5 m\" or \"1ft 2in\"");
276272
}
273+
return quantities.Aggregate((x, y) => x + y);
274+
}
277275

278-
try
279-
{
280-
AccelerationUnit unit = ParseUnit(unitString, formatProvider);
281-
double value = double.Parse(valueString, formatProvider);
276+
/// <summary>
277+
/// Parse a string given a particular regular expression.
278+
/// </summary>
279+
/// <exception cref="UnitsNetException">Error parsing string.</exception>
280+
private static List<Acceleration> ParseWithRegex(string regexString, string str, IFormatProvider formatProvider = null)
281+
{
282+
var regex = new Regex(regexString);
283+
MatchCollection matches = regex.Matches(str.Trim());
284+
var converted = new List<Acceleration>();
282285

283-
return From(value, unit);
284-
}
285-
catch (Exception e)
286+
foreach (Match match in matches)
286287
{
287-
var newEx = new UnitsNetException("Error parsing string.", e);
288-
newEx.Data["input"] = str;
289-
newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString();
290-
throw newEx;
288+
GroupCollection groups = match.Groups;
289+
290+
var valueString = groups["value"].Value;
291+
var unitString = groups["unit"].Value;
292+
if (groups["invalid"].Value != "")
293+
{
294+
var newEx = new UnitsNetException("Invalid string detected: " + groups["invalid"].Value);
295+
newEx.Data["input"] = str;
296+
newEx.Data["matched value"] = valueString;
297+
newEx.Data["matched unit"] = unitString;
298+
newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString();
299+
throw newEx;
300+
}
301+
if (valueString == "" && unitString == "") continue;
302+
303+
try
304+
{
305+
AccelerationUnit unit = ParseUnit(unitString, formatProvider);
306+
double value = double.Parse(valueString, formatProvider);
307+
308+
converted.Add(From(value, unit));
309+
}
310+
catch (Exception ex)
311+
{
312+
var newEx = new UnitsNetException("Error parsing string.", ex);
313+
newEx.Data["input"] = str;
314+
newEx.Data["matched value"] = valueString;
315+
newEx.Data["matched unit"] = unitString;
316+
newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString();
317+
throw newEx;
318+
}
291319
}
320+
return converted;
292321
}
293322

294323
/// <summary>

UnitsNet/GeneratedCode/UnitClasses/AmplitudeRatio.g.cs

Lines changed: 62 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
// THE SOFTWARE.
2121

2222
using System;
23+
using System.Collections.Generic;
2324
using System.Globalization;
2425
using System.Text.RegularExpressions;
2526
using System.Linq;
@@ -280,17 +281,16 @@ public double As(AmplitudeRatioUnit unit)
280281
#region Parsing
281282

282283
/// <summary>
283-
/// Parse a string of the format "&lt;quantity&gt; &lt;unit&gt;".
284+
/// Parse a string with one or two quantities of the format "&lt;quantity&gt; &lt;unit&gt;".
284285
/// </summary>
285286
/// <example>
286287
/// Length.Parse("5.5 m", new CultureInfo("en-US"));
287288
/// </example>
288289
/// <exception cref="ArgumentNullException">The value of 'str' cannot be null. </exception>
289290
/// <exception cref="ArgumentException">
290-
/// Expected 2 words. Input string needs to be in the format "&lt;quantity&gt; &lt;unit
291-
/// &gt;".
291+
/// Expected string to have one or two pairs of quantity and unit in the format
292+
/// "&lt;quantity&gt; &lt;unit&gt;". Eg. "5.5 m" or "1ft 2in"
292293
/// </exception>
293-
/// <exception cref="UnitsNetException">Error parsing string.</exception>
294294
public static AmplitudeRatio Parse(string str, IFormatProvider formatProvider = null)
295295
{
296296
if (str == null) throw new ArgumentNullException("str");
@@ -302,41 +302,70 @@ public static AmplitudeRatio Parse(string str, IFormatProvider formatProvider =
302302
var numRegex = string.Format(@"[\d., {0}{1}]*\d", // allows digits, dots, commas, and spaces in the quantity (must end in digit)
303303
numFormat.NumberGroupSeparator, // adds provided (or current) culture's group separator
304304
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)
318316
{
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+
+ " \"&lt;quantity&gt; &lt;unit&gt;\". Eg. \"5.5 m\" or \"1ft 2in\"");
324320
}
321+
return quantities.Aggregate((x, y) => x + y);
322+
}
325323

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>();
330333

331-
return From(value, unit);
332-
}
333-
catch (Exception e)
334+
foreach (Match match in matches)
334335
{
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+
}
339367
}
368+
return converted;
340369
}
341370

342371
/// <summary>

0 commit comments

Comments
 (0)