diff --git a/UnitsNet.Tests/CustomCode/ParseTests.cs b/UnitsNet.Tests/CustomCode/ParseTests.cs index 22a662733b..37113abb0b 100644 --- a/UnitsNet.Tests/CustomCode/ParseTests.cs +++ b/UnitsNet.Tests/CustomCode/ParseTests.cs @@ -19,7 +19,6 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -using System; using System.Globalization; using NUnit.Framework; using UnitsNet.Units; @@ -36,14 +35,14 @@ namespace UnitsNet.Tests.CustomCode [TestFixture] public class ParseTests { - [TestCase("1km", Result=1000)] + [TestCase("1km", Result = 1000)] [TestCase("1 km", Result = 1000)] [TestCase("1e-3 km", Result = 1)] [TestCase("5.5 m", Result = 5.5)] [TestCase("500,005 m", Result = 500005)] [TestCase(null, ExpectedExceptionName = "System.ArgumentNullException")] [TestCase("1", ExpectedExceptionName = "System.ArgumentException")] - [TestCase("km", ExpectedExceptionName = "System.ArgumentException")] + [TestCase("km", ExpectedExceptionName = "UnitsNet.UnitsNetException")] [TestCase("1 kg", ExpectedExceptionName = "UnitsNet.UnitsNetException")] public double ParseLengthToMetersUsEnglish(string s) { @@ -52,6 +51,21 @@ public double ParseLengthToMetersUsEnglish(string s) return Length.Parse(s, usEnglish).Meters; } + [TestCase("1 ft 1 in", Result = 13)] + [TestCase("1ft 1in", Result = 13)] + [TestCase("1' 1\"", Result = 13)] + [TestCase("1'1\"", Result = 13)] + [TestCase("1ft1in", Result = 13)] + [TestCase("1ft and 1in", Result = 13)] + [TestCase("1ft monkey 1in", ExpectedExceptionName = "UnitsNet.UnitsNetException")] + [TestCase("1ft 1invalid", ExpectedExceptionName = "UnitsNet.UnitsNetException")] + public double ParseImperialLengthInchesUsEnglish(string s) + { + var usEnglish = CultureInfo.GetCultureInfo("en-US"); + + return Length.Parse(s, usEnglish).Inches; + } + /// Error parsing string. [TestCase("5.5 m", Result = 5.5)] [TestCase("500 005 m", Result = 500005)] diff --git a/UnitsNet/GeneratedCode/UnitClasses/Acceleration.g.cs b/UnitsNet/GeneratedCode/UnitClasses/Acceleration.g.cs index 13a1c39159..39bcaeef10 100644 --- a/UnitsNet/GeneratedCode/UnitClasses/Acceleration.g.cs +++ b/UnitsNet/GeneratedCode/UnitClasses/Acceleration.g.cs @@ -20,6 +20,7 @@ // THE SOFTWARE. using System; +using System.Collections.Generic; using System.Globalization; using System.Text.RegularExpressions; using System.Linq; @@ -232,17 +233,16 @@ public double As(AccelerationUnit unit) #region Parsing /// - /// Parse a string of the format "<quantity> <unit>". + /// Parse a string with one or two quantities of the format "<quantity> <unit>". /// /// /// Length.Parse("5.5 m", new CultureInfo("en-US")); /// /// The value of 'str' cannot be null. /// - /// Expected 2 words. Input string needs to be in the format "<quantity> <unit - /// >". + /// Expected string to have one or two pairs of quantity and unit in the format + /// "<quantity> <unit>". Eg. "5.5 m" or "1ft 2in" /// - /// Error parsing string. public static Acceleration Parse(string str, IFormatProvider formatProvider = null) { if (str == null) throw new ArgumentNullException("str"); @@ -254,41 +254,70 @@ public static Acceleration Parse(string str, IFormatProvider formatProvider = nu var numRegex = string.Format(@"[\d., {0}{1}]*\d", // allows digits, dots, commas, and spaces in the quantity (must end in digit) numFormat.NumberGroupSeparator, // adds provided (or current) culture's group separator numFormat.NumberDecimalSeparator); // adds provided (or current) culture's decimal separator - var regexString = string.Format("(?[-+]?{0}{1}{2}{3}", - numRegex, // capture base (integral) Quantity value - @"(?:[eE][-+]?\d+)?)", // capture exponential (if any), end of Quantity capturing - @"\s?", // ignore whitespace (allows both "1kg", "1 kg") - @"(?\S+)"); // capture Unit (non-whitespace) input - - var regex = new Regex(regexString); - GroupCollection groups = regex.Match(str.Trim()).Groups; - - var valueString = groups["value"].Value; - var unitString = groups["unit"].Value; - - if (valueString == "" || unitString == "") + var exponentialRegex = @"(?:[eE][-+]?\d+)?)"; + var regexString = string.Format(@"(?:\s*(?[-+]?{0}{1}{2}{3})?{4}{5}", + numRegex, // capture base (integral) Quantity value + exponentialRegex, // capture exponential (if any), end of Quantity capturing + @"\s?", // ignore whitespace (allows both "1kg", "1 kg") + @"(?[^\s\d,]+)", // capture Unit (non-whitespace) input + @"(and)?,?", // allow "and" & "," separators between quantities + @"(?[a-z]*)?"); // capture invalid input + + var quantities = ParseWithRegex(regexString, str, formatProvider); + if (quantities.Count == 0) { - var ex = new ArgumentException( - "Expected valid quantity and unit. Input string needs to be in the format \" or \".", "str"); - ex.Data["input"] = str; - ex.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw ex; + throw new ArgumentException( + "Expected string to have at least one pair of quantity and unit in the format" + + " \"<quantity> <unit>\". Eg. \"5.5 m\" or \"1ft 2in\""); } + return quantities.Aggregate((x, y) => x + y); + } - try - { - AccelerationUnit unit = ParseUnit(unitString, formatProvider); - double value = double.Parse(valueString, formatProvider); + /// + /// Parse a string given a particular regular expression. + /// + /// Error parsing string. + private static List ParseWithRegex(string regexString, string str, IFormatProvider formatProvider = null) + { + var regex = new Regex(regexString); + MatchCollection matches = regex.Matches(str.Trim()); + var converted = new List(); - return From(value, unit); - } - catch (Exception e) + foreach (Match match in matches) { - var newEx = new UnitsNetException("Error parsing string.", e); - newEx.Data["input"] = str; - newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw newEx; + GroupCollection groups = match.Groups; + + var valueString = groups["value"].Value; + var unitString = groups["unit"].Value; + if (groups["invalid"].Value != "") + { + var newEx = new UnitsNetException("Invalid string detected: " + groups["invalid"].Value); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } + if (valueString == "" && unitString == "") continue; + + try + { + AccelerationUnit unit = ParseUnit(unitString, formatProvider); + double value = double.Parse(valueString, formatProvider); + + converted.Add(From(value, unit)); + } + catch (Exception ex) + { + var newEx = new UnitsNetException("Error parsing string.", ex); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } } + return converted; } /// diff --git a/UnitsNet/GeneratedCode/UnitClasses/AmplitudeRatio.g.cs b/UnitsNet/GeneratedCode/UnitClasses/AmplitudeRatio.g.cs index da92e1910d..415bfd80fc 100644 --- a/UnitsNet/GeneratedCode/UnitClasses/AmplitudeRatio.g.cs +++ b/UnitsNet/GeneratedCode/UnitClasses/AmplitudeRatio.g.cs @@ -20,6 +20,7 @@ // THE SOFTWARE. using System; +using System.Collections.Generic; using System.Globalization; using System.Text.RegularExpressions; using System.Linq; @@ -280,17 +281,16 @@ public double As(AmplitudeRatioUnit unit) #region Parsing /// - /// Parse a string of the format "<quantity> <unit>". + /// Parse a string with one or two quantities of the format "<quantity> <unit>". /// /// /// Length.Parse("5.5 m", new CultureInfo("en-US")); /// /// The value of 'str' cannot be null. /// - /// Expected 2 words. Input string needs to be in the format "<quantity> <unit - /// >". + /// Expected string to have one or two pairs of quantity and unit in the format + /// "<quantity> <unit>". Eg. "5.5 m" or "1ft 2in" /// - /// Error parsing string. public static AmplitudeRatio Parse(string str, IFormatProvider formatProvider = null) { if (str == null) throw new ArgumentNullException("str"); @@ -302,41 +302,70 @@ public static AmplitudeRatio Parse(string str, IFormatProvider formatProvider = var numRegex = string.Format(@"[\d., {0}{1}]*\d", // allows digits, dots, commas, and spaces in the quantity (must end in digit) numFormat.NumberGroupSeparator, // adds provided (or current) culture's group separator numFormat.NumberDecimalSeparator); // adds provided (or current) culture's decimal separator - var regexString = string.Format("(?[-+]?{0}{1}{2}{3}", - numRegex, // capture base (integral) Quantity value - @"(?:[eE][-+]?\d+)?)", // capture exponential (if any), end of Quantity capturing - @"\s?", // ignore whitespace (allows both "1kg", "1 kg") - @"(?\S+)"); // capture Unit (non-whitespace) input - - var regex = new Regex(regexString); - GroupCollection groups = regex.Match(str.Trim()).Groups; - - var valueString = groups["value"].Value; - var unitString = groups["unit"].Value; - - if (valueString == "" || unitString == "") + var exponentialRegex = @"(?:[eE][-+]?\d+)?)"; + var regexString = string.Format(@"(?:\s*(?[-+]?{0}{1}{2}{3})?{4}{5}", + numRegex, // capture base (integral) Quantity value + exponentialRegex, // capture exponential (if any), end of Quantity capturing + @"\s?", // ignore whitespace (allows both "1kg", "1 kg") + @"(?[^\s\d,]+)", // capture Unit (non-whitespace) input + @"(and)?,?", // allow "and" & "," separators between quantities + @"(?[a-z]*)?"); // capture invalid input + + var quantities = ParseWithRegex(regexString, str, formatProvider); + if (quantities.Count == 0) { - var ex = new ArgumentException( - "Expected valid quantity and unit. Input string needs to be in the format \" or \".", "str"); - ex.Data["input"] = str; - ex.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw ex; + throw new ArgumentException( + "Expected string to have at least one pair of quantity and unit in the format" + + " \"<quantity> <unit>\". Eg. \"5.5 m\" or \"1ft 2in\""); } + return quantities.Aggregate((x, y) => x + y); + } - try - { - AmplitudeRatioUnit unit = ParseUnit(unitString, formatProvider); - double value = double.Parse(valueString, formatProvider); + /// + /// Parse a string given a particular regular expression. + /// + /// Error parsing string. + private static List ParseWithRegex(string regexString, string str, IFormatProvider formatProvider = null) + { + var regex = new Regex(regexString); + MatchCollection matches = regex.Matches(str.Trim()); + var converted = new List(); - return From(value, unit); - } - catch (Exception e) + foreach (Match match in matches) { - var newEx = new UnitsNetException("Error parsing string.", e); - newEx.Data["input"] = str; - newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw newEx; + GroupCollection groups = match.Groups; + + var valueString = groups["value"].Value; + var unitString = groups["unit"].Value; + if (groups["invalid"].Value != "") + { + var newEx = new UnitsNetException("Invalid string detected: " + groups["invalid"].Value); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } + if (valueString == "" && unitString == "") continue; + + try + { + AmplitudeRatioUnit unit = ParseUnit(unitString, formatProvider); + double value = double.Parse(valueString, formatProvider); + + converted.Add(From(value, unit)); + } + catch (Exception ex) + { + var newEx = new UnitsNetException("Error parsing string.", ex); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } } + return converted; } /// diff --git a/UnitsNet/GeneratedCode/UnitClasses/Angle.g.cs b/UnitsNet/GeneratedCode/UnitClasses/Angle.g.cs index 36e0c9e2dd..fcefc4b82a 100644 --- a/UnitsNet/GeneratedCode/UnitClasses/Angle.g.cs +++ b/UnitsNet/GeneratedCode/UnitClasses/Angle.g.cs @@ -20,6 +20,7 @@ // THE SOFTWARE. using System; +using System.Collections.Generic; using System.Globalization; using System.Text.RegularExpressions; using System.Linq; @@ -272,17 +273,16 @@ public double As(AngleUnit unit) #region Parsing /// - /// Parse a string of the format "<quantity> <unit>". + /// Parse a string with one or two quantities of the format "<quantity> <unit>". /// /// /// Length.Parse("5.5 m", new CultureInfo("en-US")); /// /// The value of 'str' cannot be null. /// - /// Expected 2 words. Input string needs to be in the format "<quantity> <unit - /// >". + /// Expected string to have one or two pairs of quantity and unit in the format + /// "<quantity> <unit>". Eg. "5.5 m" or "1ft 2in" /// - /// Error parsing string. public static Angle Parse(string str, IFormatProvider formatProvider = null) { if (str == null) throw new ArgumentNullException("str"); @@ -294,41 +294,70 @@ public static Angle Parse(string str, IFormatProvider formatProvider = null) var numRegex = string.Format(@"[\d., {0}{1}]*\d", // allows digits, dots, commas, and spaces in the quantity (must end in digit) numFormat.NumberGroupSeparator, // adds provided (or current) culture's group separator numFormat.NumberDecimalSeparator); // adds provided (or current) culture's decimal separator - var regexString = string.Format("(?[-+]?{0}{1}{2}{3}", - numRegex, // capture base (integral) Quantity value - @"(?:[eE][-+]?\d+)?)", // capture exponential (if any), end of Quantity capturing - @"\s?", // ignore whitespace (allows both "1kg", "1 kg") - @"(?\S+)"); // capture Unit (non-whitespace) input - - var regex = new Regex(regexString); - GroupCollection groups = regex.Match(str.Trim()).Groups; - - var valueString = groups["value"].Value; - var unitString = groups["unit"].Value; - - if (valueString == "" || unitString == "") + var exponentialRegex = @"(?:[eE][-+]?\d+)?)"; + var regexString = string.Format(@"(?:\s*(?[-+]?{0}{1}{2}{3})?{4}{5}", + numRegex, // capture base (integral) Quantity value + exponentialRegex, // capture exponential (if any), end of Quantity capturing + @"\s?", // ignore whitespace (allows both "1kg", "1 kg") + @"(?[^\s\d,]+)", // capture Unit (non-whitespace) input + @"(and)?,?", // allow "and" & "," separators between quantities + @"(?[a-z]*)?"); // capture invalid input + + var quantities = ParseWithRegex(regexString, str, formatProvider); + if (quantities.Count == 0) { - var ex = new ArgumentException( - "Expected valid quantity and unit. Input string needs to be in the format \" or \".", "str"); - ex.Data["input"] = str; - ex.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw ex; + throw new ArgumentException( + "Expected string to have at least one pair of quantity and unit in the format" + + " \"<quantity> <unit>\". Eg. \"5.5 m\" or \"1ft 2in\""); } + return quantities.Aggregate((x, y) => x + y); + } - try - { - AngleUnit unit = ParseUnit(unitString, formatProvider); - double value = double.Parse(valueString, formatProvider); + /// + /// Parse a string given a particular regular expression. + /// + /// Error parsing string. + private static List ParseWithRegex(string regexString, string str, IFormatProvider formatProvider = null) + { + var regex = new Regex(regexString); + MatchCollection matches = regex.Matches(str.Trim()); + var converted = new List(); - return From(value, unit); - } - catch (Exception e) + foreach (Match match in matches) { - var newEx = new UnitsNetException("Error parsing string.", e); - newEx.Data["input"] = str; - newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw newEx; + GroupCollection groups = match.Groups; + + var valueString = groups["value"].Value; + var unitString = groups["unit"].Value; + if (groups["invalid"].Value != "") + { + var newEx = new UnitsNetException("Invalid string detected: " + groups["invalid"].Value); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } + if (valueString == "" && unitString == "") continue; + + try + { + AngleUnit unit = ParseUnit(unitString, formatProvider); + double value = double.Parse(valueString, formatProvider); + + converted.Add(From(value, unit)); + } + catch (Exception ex) + { + var newEx = new UnitsNetException("Error parsing string.", ex); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } } + return converted; } /// diff --git a/UnitsNet/GeneratedCode/UnitClasses/Area.g.cs b/UnitsNet/GeneratedCode/UnitClasses/Area.g.cs index ffc2e3b545..746d1ca247 100644 --- a/UnitsNet/GeneratedCode/UnitClasses/Area.g.cs +++ b/UnitsNet/GeneratedCode/UnitClasses/Area.g.cs @@ -20,6 +20,7 @@ // THE SOFTWARE. using System; +using System.Collections.Generic; using System.Globalization; using System.Text.RegularExpressions; using System.Linq; @@ -392,17 +393,16 @@ public double As(AreaUnit unit) #region Parsing /// - /// Parse a string of the format "<quantity> <unit>". + /// Parse a string with one or two quantities of the format "<quantity> <unit>". /// /// /// Length.Parse("5.5 m", new CultureInfo("en-US")); /// /// The value of 'str' cannot be null. /// - /// Expected 2 words. Input string needs to be in the format "<quantity> <unit - /// >". + /// Expected string to have one or two pairs of quantity and unit in the format + /// "<quantity> <unit>". Eg. "5.5 m" or "1ft 2in" /// - /// Error parsing string. public static Area Parse(string str, IFormatProvider formatProvider = null) { if (str == null) throw new ArgumentNullException("str"); @@ -414,41 +414,70 @@ public static Area Parse(string str, IFormatProvider formatProvider = null) var numRegex = string.Format(@"[\d., {0}{1}]*\d", // allows digits, dots, commas, and spaces in the quantity (must end in digit) numFormat.NumberGroupSeparator, // adds provided (or current) culture's group separator numFormat.NumberDecimalSeparator); // adds provided (or current) culture's decimal separator - var regexString = string.Format("(?[-+]?{0}{1}{2}{3}", - numRegex, // capture base (integral) Quantity value - @"(?:[eE][-+]?\d+)?)", // capture exponential (if any), end of Quantity capturing - @"\s?", // ignore whitespace (allows both "1kg", "1 kg") - @"(?\S+)"); // capture Unit (non-whitespace) input - - var regex = new Regex(regexString); - GroupCollection groups = regex.Match(str.Trim()).Groups; - - var valueString = groups["value"].Value; - var unitString = groups["unit"].Value; - - if (valueString == "" || unitString == "") + var exponentialRegex = @"(?:[eE][-+]?\d+)?)"; + var regexString = string.Format(@"(?:\s*(?[-+]?{0}{1}{2}{3})?{4}{5}", + numRegex, // capture base (integral) Quantity value + exponentialRegex, // capture exponential (if any), end of Quantity capturing + @"\s?", // ignore whitespace (allows both "1kg", "1 kg") + @"(?[^\s\d,]+)", // capture Unit (non-whitespace) input + @"(and)?,?", // allow "and" & "," separators between quantities + @"(?[a-z]*)?"); // capture invalid input + + var quantities = ParseWithRegex(regexString, str, formatProvider); + if (quantities.Count == 0) { - var ex = new ArgumentException( - "Expected valid quantity and unit. Input string needs to be in the format \" or \".", "str"); - ex.Data["input"] = str; - ex.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw ex; + throw new ArgumentException( + "Expected string to have at least one pair of quantity and unit in the format" + + " \"<quantity> <unit>\". Eg. \"5.5 m\" or \"1ft 2in\""); } + return quantities.Aggregate((x, y) => x + y); + } - try - { - AreaUnit unit = ParseUnit(unitString, formatProvider); - double value = double.Parse(valueString, formatProvider); + /// + /// Parse a string given a particular regular expression. + /// + /// Error parsing string. + private static List ParseWithRegex(string regexString, string str, IFormatProvider formatProvider = null) + { + var regex = new Regex(regexString); + MatchCollection matches = regex.Matches(str.Trim()); + var converted = new List(); - return From(value, unit); - } - catch (Exception e) + foreach (Match match in matches) { - var newEx = new UnitsNetException("Error parsing string.", e); - newEx.Data["input"] = str; - newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw newEx; + GroupCollection groups = match.Groups; + + var valueString = groups["value"].Value; + var unitString = groups["unit"].Value; + if (groups["invalid"].Value != "") + { + var newEx = new UnitsNetException("Invalid string detected: " + groups["invalid"].Value); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } + if (valueString == "" && unitString == "") continue; + + try + { + AreaUnit unit = ParseUnit(unitString, formatProvider); + double value = double.Parse(valueString, formatProvider); + + converted.Add(From(value, unit)); + } + catch (Exception ex) + { + var newEx = new UnitsNetException("Error parsing string.", ex); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } } + return converted; } /// diff --git a/UnitsNet/GeneratedCode/UnitClasses/Density.g.cs b/UnitsNet/GeneratedCode/UnitClasses/Density.g.cs index 0693da3b03..c0892ed6c1 100644 --- a/UnitsNet/GeneratedCode/UnitClasses/Density.g.cs +++ b/UnitsNet/GeneratedCode/UnitClasses/Density.g.cs @@ -20,6 +20,7 @@ // THE SOFTWARE. using System; +using System.Collections.Generic; using System.Globalization; using System.Text.RegularExpressions; using System.Linq; @@ -412,17 +413,16 @@ public double As(DensityUnit unit) #region Parsing /// - /// Parse a string of the format "<quantity> <unit>". + /// Parse a string with one or two quantities of the format "<quantity> <unit>". /// /// /// Length.Parse("5.5 m", new CultureInfo("en-US")); /// /// The value of 'str' cannot be null. /// - /// Expected 2 words. Input string needs to be in the format "<quantity> <unit - /// >". + /// Expected string to have one or two pairs of quantity and unit in the format + /// "<quantity> <unit>". Eg. "5.5 m" or "1ft 2in" /// - /// Error parsing string. public static Density Parse(string str, IFormatProvider formatProvider = null) { if (str == null) throw new ArgumentNullException("str"); @@ -434,41 +434,70 @@ public static Density Parse(string str, IFormatProvider formatProvider = null) var numRegex = string.Format(@"[\d., {0}{1}]*\d", // allows digits, dots, commas, and spaces in the quantity (must end in digit) numFormat.NumberGroupSeparator, // adds provided (or current) culture's group separator numFormat.NumberDecimalSeparator); // adds provided (or current) culture's decimal separator - var regexString = string.Format("(?[-+]?{0}{1}{2}{3}", - numRegex, // capture base (integral) Quantity value - @"(?:[eE][-+]?\d+)?)", // capture exponential (if any), end of Quantity capturing - @"\s?", // ignore whitespace (allows both "1kg", "1 kg") - @"(?\S+)"); // capture Unit (non-whitespace) input - - var regex = new Regex(regexString); - GroupCollection groups = regex.Match(str.Trim()).Groups; - - var valueString = groups["value"].Value; - var unitString = groups["unit"].Value; - - if (valueString == "" || unitString == "") + var exponentialRegex = @"(?:[eE][-+]?\d+)?)"; + var regexString = string.Format(@"(?:\s*(?[-+]?{0}{1}{2}{3})?{4}{5}", + numRegex, // capture base (integral) Quantity value + exponentialRegex, // capture exponential (if any), end of Quantity capturing + @"\s?", // ignore whitespace (allows both "1kg", "1 kg") + @"(?[^\s\d,]+)", // capture Unit (non-whitespace) input + @"(and)?,?", // allow "and" & "," separators between quantities + @"(?[a-z]*)?"); // capture invalid input + + var quantities = ParseWithRegex(regexString, str, formatProvider); + if (quantities.Count == 0) { - var ex = new ArgumentException( - "Expected valid quantity and unit. Input string needs to be in the format \" or \".", "str"); - ex.Data["input"] = str; - ex.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw ex; + throw new ArgumentException( + "Expected string to have at least one pair of quantity and unit in the format" + + " \"<quantity> <unit>\". Eg. \"5.5 m\" or \"1ft 2in\""); } + return quantities.Aggregate((x, y) => x + y); + } - try - { - DensityUnit unit = ParseUnit(unitString, formatProvider); - double value = double.Parse(valueString, formatProvider); + /// + /// Parse a string given a particular regular expression. + /// + /// Error parsing string. + private static List ParseWithRegex(string regexString, string str, IFormatProvider formatProvider = null) + { + var regex = new Regex(regexString); + MatchCollection matches = regex.Matches(str.Trim()); + var converted = new List(); - return From(value, unit); - } - catch (Exception e) + foreach (Match match in matches) { - var newEx = new UnitsNetException("Error parsing string.", e); - newEx.Data["input"] = str; - newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw newEx; + GroupCollection groups = match.Groups; + + var valueString = groups["value"].Value; + var unitString = groups["unit"].Value; + if (groups["invalid"].Value != "") + { + var newEx = new UnitsNetException("Invalid string detected: " + groups["invalid"].Value); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } + if (valueString == "" && unitString == "") continue; + + try + { + DensityUnit unit = ParseUnit(unitString, formatProvider); + double value = double.Parse(valueString, formatProvider); + + converted.Add(From(value, unit)); + } + catch (Exception ex) + { + var newEx = new UnitsNetException("Error parsing string.", ex); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } } + return converted; } /// diff --git a/UnitsNet/GeneratedCode/UnitClasses/Duration.g.cs b/UnitsNet/GeneratedCode/UnitClasses/Duration.g.cs index 3f44d89f11..cfcbb1e617 100644 --- a/UnitsNet/GeneratedCode/UnitClasses/Duration.g.cs +++ b/UnitsNet/GeneratedCode/UnitClasses/Duration.g.cs @@ -20,6 +20,7 @@ // THE SOFTWARE. using System; +using System.Collections.Generic; using System.Globalization; using System.Text.RegularExpressions; using System.Linq; @@ -412,17 +413,16 @@ public double As(DurationUnit unit) #region Parsing /// - /// Parse a string of the format "<quantity> <unit>". + /// Parse a string with one or two quantities of the format "<quantity> <unit>". /// /// /// Length.Parse("5.5 m", new CultureInfo("en-US")); /// /// The value of 'str' cannot be null. /// - /// Expected 2 words. Input string needs to be in the format "<quantity> <unit - /// >". + /// Expected string to have one or two pairs of quantity and unit in the format + /// "<quantity> <unit>". Eg. "5.5 m" or "1ft 2in" /// - /// Error parsing string. public static Duration Parse(string str, IFormatProvider formatProvider = null) { if (str == null) throw new ArgumentNullException("str"); @@ -434,41 +434,70 @@ public static Duration Parse(string str, IFormatProvider formatProvider = null) var numRegex = string.Format(@"[\d., {0}{1}]*\d", // allows digits, dots, commas, and spaces in the quantity (must end in digit) numFormat.NumberGroupSeparator, // adds provided (or current) culture's group separator numFormat.NumberDecimalSeparator); // adds provided (or current) culture's decimal separator - var regexString = string.Format("(?[-+]?{0}{1}{2}{3}", - numRegex, // capture base (integral) Quantity value - @"(?:[eE][-+]?\d+)?)", // capture exponential (if any), end of Quantity capturing - @"\s?", // ignore whitespace (allows both "1kg", "1 kg") - @"(?\S+)"); // capture Unit (non-whitespace) input - - var regex = new Regex(regexString); - GroupCollection groups = regex.Match(str.Trim()).Groups; - - var valueString = groups["value"].Value; - var unitString = groups["unit"].Value; - - if (valueString == "" || unitString == "") + var exponentialRegex = @"(?:[eE][-+]?\d+)?)"; + var regexString = string.Format(@"(?:\s*(?[-+]?{0}{1}{2}{3})?{4}{5}", + numRegex, // capture base (integral) Quantity value + exponentialRegex, // capture exponential (if any), end of Quantity capturing + @"\s?", // ignore whitespace (allows both "1kg", "1 kg") + @"(?[^\s\d,]+)", // capture Unit (non-whitespace) input + @"(and)?,?", // allow "and" & "," separators between quantities + @"(?[a-z]*)?"); // capture invalid input + + var quantities = ParseWithRegex(regexString, str, formatProvider); + if (quantities.Count == 0) { - var ex = new ArgumentException( - "Expected valid quantity and unit. Input string needs to be in the format \" or \".", "str"); - ex.Data["input"] = str; - ex.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw ex; + throw new ArgumentException( + "Expected string to have at least one pair of quantity and unit in the format" + + " \"<quantity> <unit>\". Eg. \"5.5 m\" or \"1ft 2in\""); } + return quantities.Aggregate((x, y) => x + y); + } - try - { - DurationUnit unit = ParseUnit(unitString, formatProvider); - double value = double.Parse(valueString, formatProvider); + /// + /// Parse a string given a particular regular expression. + /// + /// Error parsing string. + private static List ParseWithRegex(string regexString, string str, IFormatProvider formatProvider = null) + { + var regex = new Regex(regexString); + MatchCollection matches = regex.Matches(str.Trim()); + var converted = new List(); - return From(value, unit); - } - catch (Exception e) + foreach (Match match in matches) { - var newEx = new UnitsNetException("Error parsing string.", e); - newEx.Data["input"] = str; - newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw newEx; + GroupCollection groups = match.Groups; + + var valueString = groups["value"].Value; + var unitString = groups["unit"].Value; + if (groups["invalid"].Value != "") + { + var newEx = new UnitsNetException("Invalid string detected: " + groups["invalid"].Value); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } + if (valueString == "" && unitString == "") continue; + + try + { + DurationUnit unit = ParseUnit(unitString, formatProvider); + double value = double.Parse(valueString, formatProvider); + + converted.Add(From(value, unit)); + } + catch (Exception ex) + { + var newEx = new UnitsNetException("Error parsing string.", ex); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } } + return converted; } /// diff --git a/UnitsNet/GeneratedCode/UnitClasses/ElectricCurrent.g.cs b/UnitsNet/GeneratedCode/UnitClasses/ElectricCurrent.g.cs index 99328f11f4..c3cafee71e 100644 --- a/UnitsNet/GeneratedCode/UnitClasses/ElectricCurrent.g.cs +++ b/UnitsNet/GeneratedCode/UnitClasses/ElectricCurrent.g.cs @@ -20,6 +20,7 @@ // THE SOFTWARE. using System; +using System.Collections.Generic; using System.Globalization; using System.Text.RegularExpressions; using System.Linq; @@ -332,17 +333,16 @@ public double As(ElectricCurrentUnit unit) #region Parsing /// - /// Parse a string of the format "<quantity> <unit>". + /// Parse a string with one or two quantities of the format "<quantity> <unit>". /// /// /// Length.Parse("5.5 m", new CultureInfo("en-US")); /// /// The value of 'str' cannot be null. /// - /// Expected 2 words. Input string needs to be in the format "<quantity> <unit - /// >". + /// Expected string to have one or two pairs of quantity and unit in the format + /// "<quantity> <unit>". Eg. "5.5 m" or "1ft 2in" /// - /// Error parsing string. public static ElectricCurrent Parse(string str, IFormatProvider formatProvider = null) { if (str == null) throw new ArgumentNullException("str"); @@ -354,41 +354,70 @@ public static ElectricCurrent Parse(string str, IFormatProvider formatProvider = var numRegex = string.Format(@"[\d., {0}{1}]*\d", // allows digits, dots, commas, and spaces in the quantity (must end in digit) numFormat.NumberGroupSeparator, // adds provided (or current) culture's group separator numFormat.NumberDecimalSeparator); // adds provided (or current) culture's decimal separator - var regexString = string.Format("(?[-+]?{0}{1}{2}{3}", - numRegex, // capture base (integral) Quantity value - @"(?:[eE][-+]?\d+)?)", // capture exponential (if any), end of Quantity capturing - @"\s?", // ignore whitespace (allows both "1kg", "1 kg") - @"(?\S+)"); // capture Unit (non-whitespace) input - - var regex = new Regex(regexString); - GroupCollection groups = regex.Match(str.Trim()).Groups; - - var valueString = groups["value"].Value; - var unitString = groups["unit"].Value; - - if (valueString == "" || unitString == "") + var exponentialRegex = @"(?:[eE][-+]?\d+)?)"; + var regexString = string.Format(@"(?:\s*(?[-+]?{0}{1}{2}{3})?{4}{5}", + numRegex, // capture base (integral) Quantity value + exponentialRegex, // capture exponential (if any), end of Quantity capturing + @"\s?", // ignore whitespace (allows both "1kg", "1 kg") + @"(?[^\s\d,]+)", // capture Unit (non-whitespace) input + @"(and)?,?", // allow "and" & "," separators between quantities + @"(?[a-z]*)?"); // capture invalid input + + var quantities = ParseWithRegex(regexString, str, formatProvider); + if (quantities.Count == 0) { - var ex = new ArgumentException( - "Expected valid quantity and unit. Input string needs to be in the format \" or \".", "str"); - ex.Data["input"] = str; - ex.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw ex; + throw new ArgumentException( + "Expected string to have at least one pair of quantity and unit in the format" + + " \"<quantity> <unit>\". Eg. \"5.5 m\" or \"1ft 2in\""); } + return quantities.Aggregate((x, y) => x + y); + } - try - { - ElectricCurrentUnit unit = ParseUnit(unitString, formatProvider); - double value = double.Parse(valueString, formatProvider); + /// + /// Parse a string given a particular regular expression. + /// + /// Error parsing string. + private static List ParseWithRegex(string regexString, string str, IFormatProvider formatProvider = null) + { + var regex = new Regex(regexString); + MatchCollection matches = regex.Matches(str.Trim()); + var converted = new List(); - return From(value, unit); - } - catch (Exception e) + foreach (Match match in matches) { - var newEx = new UnitsNetException("Error parsing string.", e); - newEx.Data["input"] = str; - newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw newEx; + GroupCollection groups = match.Groups; + + var valueString = groups["value"].Value; + var unitString = groups["unit"].Value; + if (groups["invalid"].Value != "") + { + var newEx = new UnitsNetException("Invalid string detected: " + groups["invalid"].Value); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } + if (valueString == "" && unitString == "") continue; + + try + { + ElectricCurrentUnit unit = ParseUnit(unitString, formatProvider); + double value = double.Parse(valueString, formatProvider); + + converted.Add(From(value, unit)); + } + catch (Exception ex) + { + var newEx = new UnitsNetException("Error parsing string.", ex); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } } + return converted; } /// diff --git a/UnitsNet/GeneratedCode/UnitClasses/ElectricPotential.g.cs b/UnitsNet/GeneratedCode/UnitClasses/ElectricPotential.g.cs index 54ead91545..8e7a03f632 100644 --- a/UnitsNet/GeneratedCode/UnitClasses/ElectricPotential.g.cs +++ b/UnitsNet/GeneratedCode/UnitClasses/ElectricPotential.g.cs @@ -20,6 +20,7 @@ // THE SOFTWARE. using System; +using System.Collections.Generic; using System.Globalization; using System.Text.RegularExpressions; using System.Linq; @@ -312,17 +313,16 @@ public double As(ElectricPotentialUnit unit) #region Parsing /// - /// Parse a string of the format "<quantity> <unit>". + /// Parse a string with one or two quantities of the format "<quantity> <unit>". /// /// /// Length.Parse("5.5 m", new CultureInfo("en-US")); /// /// The value of 'str' cannot be null. /// - /// Expected 2 words. Input string needs to be in the format "<quantity> <unit - /// >". + /// Expected string to have one or two pairs of quantity and unit in the format + /// "<quantity> <unit>". Eg. "5.5 m" or "1ft 2in" /// - /// Error parsing string. public static ElectricPotential Parse(string str, IFormatProvider formatProvider = null) { if (str == null) throw new ArgumentNullException("str"); @@ -334,41 +334,70 @@ public static ElectricPotential Parse(string str, IFormatProvider formatProvider var numRegex = string.Format(@"[\d., {0}{1}]*\d", // allows digits, dots, commas, and spaces in the quantity (must end in digit) numFormat.NumberGroupSeparator, // adds provided (or current) culture's group separator numFormat.NumberDecimalSeparator); // adds provided (or current) culture's decimal separator - var regexString = string.Format("(?[-+]?{0}{1}{2}{3}", - numRegex, // capture base (integral) Quantity value - @"(?:[eE][-+]?\d+)?)", // capture exponential (if any), end of Quantity capturing - @"\s?", // ignore whitespace (allows both "1kg", "1 kg") - @"(?\S+)"); // capture Unit (non-whitespace) input - - var regex = new Regex(regexString); - GroupCollection groups = regex.Match(str.Trim()).Groups; - - var valueString = groups["value"].Value; - var unitString = groups["unit"].Value; - - if (valueString == "" || unitString == "") + var exponentialRegex = @"(?:[eE][-+]?\d+)?)"; + var regexString = string.Format(@"(?:\s*(?[-+]?{0}{1}{2}{3})?{4}{5}", + numRegex, // capture base (integral) Quantity value + exponentialRegex, // capture exponential (if any), end of Quantity capturing + @"\s?", // ignore whitespace (allows both "1kg", "1 kg") + @"(?[^\s\d,]+)", // capture Unit (non-whitespace) input + @"(and)?,?", // allow "and" & "," separators between quantities + @"(?[a-z]*)?"); // capture invalid input + + var quantities = ParseWithRegex(regexString, str, formatProvider); + if (quantities.Count == 0) { - var ex = new ArgumentException( - "Expected valid quantity and unit. Input string needs to be in the format \" or \".", "str"); - ex.Data["input"] = str; - ex.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw ex; + throw new ArgumentException( + "Expected string to have at least one pair of quantity and unit in the format" + + " \"<quantity> <unit>\". Eg. \"5.5 m\" or \"1ft 2in\""); } + return quantities.Aggregate((x, y) => x + y); + } - try - { - ElectricPotentialUnit unit = ParseUnit(unitString, formatProvider); - double value = double.Parse(valueString, formatProvider); + /// + /// Parse a string given a particular regular expression. + /// + /// Error parsing string. + private static List ParseWithRegex(string regexString, string str, IFormatProvider formatProvider = null) + { + var regex = new Regex(regexString); + MatchCollection matches = regex.Matches(str.Trim()); + var converted = new List(); - return From(value, unit); - } - catch (Exception e) + foreach (Match match in matches) { - var newEx = new UnitsNetException("Error parsing string.", e); - newEx.Data["input"] = str; - newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw newEx; + GroupCollection groups = match.Groups; + + var valueString = groups["value"].Value; + var unitString = groups["unit"].Value; + if (groups["invalid"].Value != "") + { + var newEx = new UnitsNetException("Invalid string detected: " + groups["invalid"].Value); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } + if (valueString == "" && unitString == "") continue; + + try + { + ElectricPotentialUnit unit = ParseUnit(unitString, formatProvider); + double value = double.Parse(valueString, formatProvider); + + converted.Add(From(value, unit)); + } + catch (Exception ex) + { + var newEx = new UnitsNetException("Error parsing string.", ex); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } } + return converted; } /// diff --git a/UnitsNet/GeneratedCode/UnitClasses/ElectricResistance.g.cs b/UnitsNet/GeneratedCode/UnitClasses/ElectricResistance.g.cs index 377bf7cff9..d466d08420 100644 --- a/UnitsNet/GeneratedCode/UnitClasses/ElectricResistance.g.cs +++ b/UnitsNet/GeneratedCode/UnitClasses/ElectricResistance.g.cs @@ -20,6 +20,7 @@ // THE SOFTWARE. using System; +using System.Collections.Generic; using System.Globalization; using System.Text.RegularExpressions; using System.Linq; @@ -272,17 +273,16 @@ public double As(ElectricResistanceUnit unit) #region Parsing /// - /// Parse a string of the format "<quantity> <unit>". + /// Parse a string with one or two quantities of the format "<quantity> <unit>". /// /// /// Length.Parse("5.5 m", new CultureInfo("en-US")); /// /// The value of 'str' cannot be null. /// - /// Expected 2 words. Input string needs to be in the format "<quantity> <unit - /// >". + /// Expected string to have one or two pairs of quantity and unit in the format + /// "<quantity> <unit>". Eg. "5.5 m" or "1ft 2in" /// - /// Error parsing string. public static ElectricResistance Parse(string str, IFormatProvider formatProvider = null) { if (str == null) throw new ArgumentNullException("str"); @@ -294,41 +294,70 @@ public static ElectricResistance Parse(string str, IFormatProvider formatProvide var numRegex = string.Format(@"[\d., {0}{1}]*\d", // allows digits, dots, commas, and spaces in the quantity (must end in digit) numFormat.NumberGroupSeparator, // adds provided (or current) culture's group separator numFormat.NumberDecimalSeparator); // adds provided (or current) culture's decimal separator - var regexString = string.Format("(?[-+]?{0}{1}{2}{3}", - numRegex, // capture base (integral) Quantity value - @"(?:[eE][-+]?\d+)?)", // capture exponential (if any), end of Quantity capturing - @"\s?", // ignore whitespace (allows both "1kg", "1 kg") - @"(?\S+)"); // capture Unit (non-whitespace) input - - var regex = new Regex(regexString); - GroupCollection groups = regex.Match(str.Trim()).Groups; - - var valueString = groups["value"].Value; - var unitString = groups["unit"].Value; - - if (valueString == "" || unitString == "") + var exponentialRegex = @"(?:[eE][-+]?\d+)?)"; + var regexString = string.Format(@"(?:\s*(?[-+]?{0}{1}{2}{3})?{4}{5}", + numRegex, // capture base (integral) Quantity value + exponentialRegex, // capture exponential (if any), end of Quantity capturing + @"\s?", // ignore whitespace (allows both "1kg", "1 kg") + @"(?[^\s\d,]+)", // capture Unit (non-whitespace) input + @"(and)?,?", // allow "and" & "," separators between quantities + @"(?[a-z]*)?"); // capture invalid input + + var quantities = ParseWithRegex(regexString, str, formatProvider); + if (quantities.Count == 0) { - var ex = new ArgumentException( - "Expected valid quantity and unit. Input string needs to be in the format \" or \".", "str"); - ex.Data["input"] = str; - ex.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw ex; + throw new ArgumentException( + "Expected string to have at least one pair of quantity and unit in the format" + + " \"<quantity> <unit>\". Eg. \"5.5 m\" or \"1ft 2in\""); } + return quantities.Aggregate((x, y) => x + y); + } - try - { - ElectricResistanceUnit unit = ParseUnit(unitString, formatProvider); - double value = double.Parse(valueString, formatProvider); + /// + /// Parse a string given a particular regular expression. + /// + /// Error parsing string. + private static List ParseWithRegex(string regexString, string str, IFormatProvider formatProvider = null) + { + var regex = new Regex(regexString); + MatchCollection matches = regex.Matches(str.Trim()); + var converted = new List(); - return From(value, unit); - } - catch (Exception e) + foreach (Match match in matches) { - var newEx = new UnitsNetException("Error parsing string.", e); - newEx.Data["input"] = str; - newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw newEx; + GroupCollection groups = match.Groups; + + var valueString = groups["value"].Value; + var unitString = groups["unit"].Value; + if (groups["invalid"].Value != "") + { + var newEx = new UnitsNetException("Invalid string detected: " + groups["invalid"].Value); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } + if (valueString == "" && unitString == "") continue; + + try + { + ElectricResistanceUnit unit = ParseUnit(unitString, formatProvider); + double value = double.Parse(valueString, formatProvider); + + converted.Add(From(value, unit)); + } + catch (Exception ex) + { + var newEx = new UnitsNetException("Error parsing string.", ex); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } } + return converted; } /// diff --git a/UnitsNet/GeneratedCode/UnitClasses/Energy.g.cs b/UnitsNet/GeneratedCode/UnitClasses/Energy.g.cs index 269a18222e..07563408a0 100644 --- a/UnitsNet/GeneratedCode/UnitClasses/Energy.g.cs +++ b/UnitsNet/GeneratedCode/UnitClasses/Energy.g.cs @@ -20,6 +20,7 @@ // THE SOFTWARE. using System; +using System.Collections.Generic; using System.Globalization; using System.Text.RegularExpressions; using System.Linq; @@ -272,17 +273,16 @@ public double As(EnergyUnit unit) #region Parsing /// - /// Parse a string of the format "<quantity> <unit>". + /// Parse a string with one or two quantities of the format "<quantity> <unit>". /// /// /// Length.Parse("5.5 m", new CultureInfo("en-US")); /// /// The value of 'str' cannot be null. /// - /// Expected 2 words. Input string needs to be in the format "<quantity> <unit - /// >". + /// Expected string to have one or two pairs of quantity and unit in the format + /// "<quantity> <unit>". Eg. "5.5 m" or "1ft 2in" /// - /// Error parsing string. public static Energy Parse(string str, IFormatProvider formatProvider = null) { if (str == null) throw new ArgumentNullException("str"); @@ -294,41 +294,70 @@ public static Energy Parse(string str, IFormatProvider formatProvider = null) var numRegex = string.Format(@"[\d., {0}{1}]*\d", // allows digits, dots, commas, and spaces in the quantity (must end in digit) numFormat.NumberGroupSeparator, // adds provided (or current) culture's group separator numFormat.NumberDecimalSeparator); // adds provided (or current) culture's decimal separator - var regexString = string.Format("(?[-+]?{0}{1}{2}{3}", - numRegex, // capture base (integral) Quantity value - @"(?:[eE][-+]?\d+)?)", // capture exponential (if any), end of Quantity capturing - @"\s?", // ignore whitespace (allows both "1kg", "1 kg") - @"(?\S+)"); // capture Unit (non-whitespace) input - - var regex = new Regex(regexString); - GroupCollection groups = regex.Match(str.Trim()).Groups; - - var valueString = groups["value"].Value; - var unitString = groups["unit"].Value; - - if (valueString == "" || unitString == "") + var exponentialRegex = @"(?:[eE][-+]?\d+)?)"; + var regexString = string.Format(@"(?:\s*(?[-+]?{0}{1}{2}{3})?{4}{5}", + numRegex, // capture base (integral) Quantity value + exponentialRegex, // capture exponential (if any), end of Quantity capturing + @"\s?", // ignore whitespace (allows both "1kg", "1 kg") + @"(?[^\s\d,]+)", // capture Unit (non-whitespace) input + @"(and)?,?", // allow "and" & "," separators between quantities + @"(?[a-z]*)?"); // capture invalid input + + var quantities = ParseWithRegex(regexString, str, formatProvider); + if (quantities.Count == 0) { - var ex = new ArgumentException( - "Expected valid quantity and unit. Input string needs to be in the format \" or \".", "str"); - ex.Data["input"] = str; - ex.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw ex; + throw new ArgumentException( + "Expected string to have at least one pair of quantity and unit in the format" + + " \"<quantity> <unit>\". Eg. \"5.5 m\" or \"1ft 2in\""); } + return quantities.Aggregate((x, y) => x + y); + } - try - { - EnergyUnit unit = ParseUnit(unitString, formatProvider); - double value = double.Parse(valueString, formatProvider); + /// + /// Parse a string given a particular regular expression. + /// + /// Error parsing string. + private static List ParseWithRegex(string regexString, string str, IFormatProvider formatProvider = null) + { + var regex = new Regex(regexString); + MatchCollection matches = regex.Matches(str.Trim()); + var converted = new List(); - return From(value, unit); - } - catch (Exception e) + foreach (Match match in matches) { - var newEx = new UnitsNetException("Error parsing string.", e); - newEx.Data["input"] = str; - newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw newEx; + GroupCollection groups = match.Groups; + + var valueString = groups["value"].Value; + var unitString = groups["unit"].Value; + if (groups["invalid"].Value != "") + { + var newEx = new UnitsNetException("Invalid string detected: " + groups["invalid"].Value); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } + if (valueString == "" && unitString == "") continue; + + try + { + EnergyUnit unit = ParseUnit(unitString, formatProvider); + double value = double.Parse(valueString, formatProvider); + + converted.Add(From(value, unit)); + } + catch (Exception ex) + { + var newEx = new UnitsNetException("Error parsing string.", ex); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } } + return converted; } /// diff --git a/UnitsNet/GeneratedCode/UnitClasses/Flow.g.cs b/UnitsNet/GeneratedCode/UnitClasses/Flow.g.cs index 54b238db62..c1b6d0e565 100644 --- a/UnitsNet/GeneratedCode/UnitClasses/Flow.g.cs +++ b/UnitsNet/GeneratedCode/UnitClasses/Flow.g.cs @@ -20,6 +20,7 @@ // THE SOFTWARE. using System; +using System.Collections.Generic; using System.Globalization; using System.Text.RegularExpressions; using System.Linq; @@ -292,17 +293,16 @@ public double As(FlowUnit unit) #region Parsing /// - /// Parse a string of the format "<quantity> <unit>". + /// Parse a string with one or two quantities of the format "<quantity> <unit>". /// /// /// Length.Parse("5.5 m", new CultureInfo("en-US")); /// /// The value of 'str' cannot be null. /// - /// Expected 2 words. Input string needs to be in the format "<quantity> <unit - /// >". + /// Expected string to have one or two pairs of quantity and unit in the format + /// "<quantity> <unit>". Eg. "5.5 m" or "1ft 2in" /// - /// Error parsing string. public static Flow Parse(string str, IFormatProvider formatProvider = null) { if (str == null) throw new ArgumentNullException("str"); @@ -314,41 +314,70 @@ public static Flow Parse(string str, IFormatProvider formatProvider = null) var numRegex = string.Format(@"[\d., {0}{1}]*\d", // allows digits, dots, commas, and spaces in the quantity (must end in digit) numFormat.NumberGroupSeparator, // adds provided (or current) culture's group separator numFormat.NumberDecimalSeparator); // adds provided (or current) culture's decimal separator - var regexString = string.Format("(?[-+]?{0}{1}{2}{3}", - numRegex, // capture base (integral) Quantity value - @"(?:[eE][-+]?\d+)?)", // capture exponential (if any), end of Quantity capturing - @"\s?", // ignore whitespace (allows both "1kg", "1 kg") - @"(?\S+)"); // capture Unit (non-whitespace) input - - var regex = new Regex(regexString); - GroupCollection groups = regex.Match(str.Trim()).Groups; - - var valueString = groups["value"].Value; - var unitString = groups["unit"].Value; - - if (valueString == "" || unitString == "") + var exponentialRegex = @"(?:[eE][-+]?\d+)?)"; + var regexString = string.Format(@"(?:\s*(?[-+]?{0}{1}{2}{3})?{4}{5}", + numRegex, // capture base (integral) Quantity value + exponentialRegex, // capture exponential (if any), end of Quantity capturing + @"\s?", // ignore whitespace (allows both "1kg", "1 kg") + @"(?[^\s\d,]+)", // capture Unit (non-whitespace) input + @"(and)?,?", // allow "and" & "," separators between quantities + @"(?[a-z]*)?"); // capture invalid input + + var quantities = ParseWithRegex(regexString, str, formatProvider); + if (quantities.Count == 0) { - var ex = new ArgumentException( - "Expected valid quantity and unit. Input string needs to be in the format \" or \".", "str"); - ex.Data["input"] = str; - ex.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw ex; + throw new ArgumentException( + "Expected string to have at least one pair of quantity and unit in the format" + + " \"<quantity> <unit>\". Eg. \"5.5 m\" or \"1ft 2in\""); } + return quantities.Aggregate((x, y) => x + y); + } - try - { - FlowUnit unit = ParseUnit(unitString, formatProvider); - double value = double.Parse(valueString, formatProvider); + /// + /// Parse a string given a particular regular expression. + /// + /// Error parsing string. + private static List ParseWithRegex(string regexString, string str, IFormatProvider formatProvider = null) + { + var regex = new Regex(regexString); + MatchCollection matches = regex.Matches(str.Trim()); + var converted = new List(); - return From(value, unit); - } - catch (Exception e) + foreach (Match match in matches) { - var newEx = new UnitsNetException("Error parsing string.", e); - newEx.Data["input"] = str; - newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw newEx; + GroupCollection groups = match.Groups; + + var valueString = groups["value"].Value; + var unitString = groups["unit"].Value; + if (groups["invalid"].Value != "") + { + var newEx = new UnitsNetException("Invalid string detected: " + groups["invalid"].Value); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } + if (valueString == "" && unitString == "") continue; + + try + { + FlowUnit unit = ParseUnit(unitString, formatProvider); + double value = double.Parse(valueString, formatProvider); + + converted.Add(From(value, unit)); + } + catch (Exception ex) + { + var newEx = new UnitsNetException("Error parsing string.", ex); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } } + return converted; } /// diff --git a/UnitsNet/GeneratedCode/UnitClasses/Force.g.cs b/UnitsNet/GeneratedCode/UnitClasses/Force.g.cs index 8d18db0b1c..16e0e24240 100644 --- a/UnitsNet/GeneratedCode/UnitClasses/Force.g.cs +++ b/UnitsNet/GeneratedCode/UnitClasses/Force.g.cs @@ -20,6 +20,7 @@ // THE SOFTWARE. using System; +using System.Collections.Generic; using System.Globalization; using System.Text.RegularExpressions; using System.Linq; @@ -372,17 +373,16 @@ public double As(ForceUnit unit) #region Parsing /// - /// Parse a string of the format "<quantity> <unit>". + /// Parse a string with one or two quantities of the format "<quantity> <unit>". /// /// /// Length.Parse("5.5 m", new CultureInfo("en-US")); /// /// The value of 'str' cannot be null. /// - /// Expected 2 words. Input string needs to be in the format "<quantity> <unit - /// >". + /// Expected string to have one or two pairs of quantity and unit in the format + /// "<quantity> <unit>". Eg. "5.5 m" or "1ft 2in" /// - /// Error parsing string. public static Force Parse(string str, IFormatProvider formatProvider = null) { if (str == null) throw new ArgumentNullException("str"); @@ -394,41 +394,70 @@ public static Force Parse(string str, IFormatProvider formatProvider = null) var numRegex = string.Format(@"[\d., {0}{1}]*\d", // allows digits, dots, commas, and spaces in the quantity (must end in digit) numFormat.NumberGroupSeparator, // adds provided (or current) culture's group separator numFormat.NumberDecimalSeparator); // adds provided (or current) culture's decimal separator - var regexString = string.Format("(?[-+]?{0}{1}{2}{3}", - numRegex, // capture base (integral) Quantity value - @"(?:[eE][-+]?\d+)?)", // capture exponential (if any), end of Quantity capturing - @"\s?", // ignore whitespace (allows both "1kg", "1 kg") - @"(?\S+)"); // capture Unit (non-whitespace) input - - var regex = new Regex(regexString); - GroupCollection groups = regex.Match(str.Trim()).Groups; - - var valueString = groups["value"].Value; - var unitString = groups["unit"].Value; - - if (valueString == "" || unitString == "") + var exponentialRegex = @"(?:[eE][-+]?\d+)?)"; + var regexString = string.Format(@"(?:\s*(?[-+]?{0}{1}{2}{3})?{4}{5}", + numRegex, // capture base (integral) Quantity value + exponentialRegex, // capture exponential (if any), end of Quantity capturing + @"\s?", // ignore whitespace (allows both "1kg", "1 kg") + @"(?[^\s\d,]+)", // capture Unit (non-whitespace) input + @"(and)?,?", // allow "and" & "," separators between quantities + @"(?[a-z]*)?"); // capture invalid input + + var quantities = ParseWithRegex(regexString, str, formatProvider); + if (quantities.Count == 0) { - var ex = new ArgumentException( - "Expected valid quantity and unit. Input string needs to be in the format \" or \".", "str"); - ex.Data["input"] = str; - ex.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw ex; + throw new ArgumentException( + "Expected string to have at least one pair of quantity and unit in the format" + + " \"<quantity> <unit>\". Eg. \"5.5 m\" or \"1ft 2in\""); } + return quantities.Aggregate((x, y) => x + y); + } - try - { - ForceUnit unit = ParseUnit(unitString, formatProvider); - double value = double.Parse(valueString, formatProvider); + /// + /// Parse a string given a particular regular expression. + /// + /// Error parsing string. + private static List ParseWithRegex(string regexString, string str, IFormatProvider formatProvider = null) + { + var regex = new Regex(regexString); + MatchCollection matches = regex.Matches(str.Trim()); + var converted = new List(); - return From(value, unit); - } - catch (Exception e) + foreach (Match match in matches) { - var newEx = new UnitsNetException("Error parsing string.", e); - newEx.Data["input"] = str; - newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw newEx; + GroupCollection groups = match.Groups; + + var valueString = groups["value"].Value; + var unitString = groups["unit"].Value; + if (groups["invalid"].Value != "") + { + var newEx = new UnitsNetException("Invalid string detected: " + groups["invalid"].Value); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } + if (valueString == "" && unitString == "") continue; + + try + { + ForceUnit unit = ParseUnit(unitString, formatProvider); + double value = double.Parse(valueString, formatProvider); + + converted.Add(From(value, unit)); + } + catch (Exception ex) + { + var newEx = new UnitsNetException("Error parsing string.", ex); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } } + return converted; } /// diff --git a/UnitsNet/GeneratedCode/UnitClasses/Frequency.g.cs b/UnitsNet/GeneratedCode/UnitClasses/Frequency.g.cs index 10dfa22e23..3132aea747 100644 --- a/UnitsNet/GeneratedCode/UnitClasses/Frequency.g.cs +++ b/UnitsNet/GeneratedCode/UnitClasses/Frequency.g.cs @@ -20,6 +20,7 @@ // THE SOFTWARE. using System; +using System.Collections.Generic; using System.Globalization; using System.Text.RegularExpressions; using System.Linq; @@ -312,17 +313,16 @@ public double As(FrequencyUnit unit) #region Parsing /// - /// Parse a string of the format "<quantity> <unit>". + /// Parse a string with one or two quantities of the format "<quantity> <unit>". /// /// /// Length.Parse("5.5 m", new CultureInfo("en-US")); /// /// The value of 'str' cannot be null. /// - /// Expected 2 words. Input string needs to be in the format "<quantity> <unit - /// >". + /// Expected string to have one or two pairs of quantity and unit in the format + /// "<quantity> <unit>". Eg. "5.5 m" or "1ft 2in" /// - /// Error parsing string. public static Frequency Parse(string str, IFormatProvider formatProvider = null) { if (str == null) throw new ArgumentNullException("str"); @@ -334,41 +334,70 @@ public static Frequency Parse(string str, IFormatProvider formatProvider = null) var numRegex = string.Format(@"[\d., {0}{1}]*\d", // allows digits, dots, commas, and spaces in the quantity (must end in digit) numFormat.NumberGroupSeparator, // adds provided (or current) culture's group separator numFormat.NumberDecimalSeparator); // adds provided (or current) culture's decimal separator - var regexString = string.Format("(?[-+]?{0}{1}{2}{3}", - numRegex, // capture base (integral) Quantity value - @"(?:[eE][-+]?\d+)?)", // capture exponential (if any), end of Quantity capturing - @"\s?", // ignore whitespace (allows both "1kg", "1 kg") - @"(?\S+)"); // capture Unit (non-whitespace) input - - var regex = new Regex(regexString); - GroupCollection groups = regex.Match(str.Trim()).Groups; - - var valueString = groups["value"].Value; - var unitString = groups["unit"].Value; - - if (valueString == "" || unitString == "") + var exponentialRegex = @"(?:[eE][-+]?\d+)?)"; + var regexString = string.Format(@"(?:\s*(?[-+]?{0}{1}{2}{3})?{4}{5}", + numRegex, // capture base (integral) Quantity value + exponentialRegex, // capture exponential (if any), end of Quantity capturing + @"\s?", // ignore whitespace (allows both "1kg", "1 kg") + @"(?[^\s\d,]+)", // capture Unit (non-whitespace) input + @"(and)?,?", // allow "and" & "," separators between quantities + @"(?[a-z]*)?"); // capture invalid input + + var quantities = ParseWithRegex(regexString, str, formatProvider); + if (quantities.Count == 0) { - var ex = new ArgumentException( - "Expected valid quantity and unit. Input string needs to be in the format \" or \".", "str"); - ex.Data["input"] = str; - ex.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw ex; + throw new ArgumentException( + "Expected string to have at least one pair of quantity and unit in the format" + + " \"<quantity> <unit>\". Eg. \"5.5 m\" or \"1ft 2in\""); } + return quantities.Aggregate((x, y) => x + y); + } - try - { - FrequencyUnit unit = ParseUnit(unitString, formatProvider); - double value = double.Parse(valueString, formatProvider); + /// + /// Parse a string given a particular regular expression. + /// + /// Error parsing string. + private static List ParseWithRegex(string regexString, string str, IFormatProvider formatProvider = null) + { + var regex = new Regex(regexString); + MatchCollection matches = regex.Matches(str.Trim()); + var converted = new List(); - return From(value, unit); - } - catch (Exception e) + foreach (Match match in matches) { - var newEx = new UnitsNetException("Error parsing string.", e); - newEx.Data["input"] = str; - newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw newEx; + GroupCollection groups = match.Groups; + + var valueString = groups["value"].Value; + var unitString = groups["unit"].Value; + if (groups["invalid"].Value != "") + { + var newEx = new UnitsNetException("Invalid string detected: " + groups["invalid"].Value); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } + if (valueString == "" && unitString == "") continue; + + try + { + FrequencyUnit unit = ParseUnit(unitString, formatProvider); + double value = double.Parse(valueString, formatProvider); + + converted.Add(From(value, unit)); + } + catch (Exception ex) + { + var newEx = new UnitsNetException("Error parsing string.", ex); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } } + return converted; } /// diff --git a/UnitsNet/GeneratedCode/UnitClasses/Information.g.cs b/UnitsNet/GeneratedCode/UnitClasses/Information.g.cs index ea87e1b325..e2daeb800c 100644 --- a/UnitsNet/GeneratedCode/UnitClasses/Information.g.cs +++ b/UnitsNet/GeneratedCode/UnitClasses/Information.g.cs @@ -20,6 +20,7 @@ // THE SOFTWARE. using System; +using System.Collections.Generic; using System.Globalization; using System.Text.RegularExpressions; using System.Linq; @@ -732,17 +733,16 @@ public double As(InformationUnit unit) #region Parsing /// - /// Parse a string of the format "<quantity> <unit>". + /// Parse a string with one or two quantities of the format "<quantity> <unit>". /// /// /// Length.Parse("5.5 m", new CultureInfo("en-US")); /// /// The value of 'str' cannot be null. /// - /// Expected 2 words. Input string needs to be in the format "<quantity> <unit - /// >". + /// Expected string to have one or two pairs of quantity and unit in the format + /// "<quantity> <unit>". Eg. "5.5 m" or "1ft 2in" /// - /// Error parsing string. public static Information Parse(string str, IFormatProvider formatProvider = null) { if (str == null) throw new ArgumentNullException("str"); @@ -754,41 +754,70 @@ public static Information Parse(string str, IFormatProvider formatProvider = nul var numRegex = string.Format(@"[\d., {0}{1}]*\d", // allows digits, dots, commas, and spaces in the quantity (must end in digit) numFormat.NumberGroupSeparator, // adds provided (or current) culture's group separator numFormat.NumberDecimalSeparator); // adds provided (or current) culture's decimal separator - var regexString = string.Format("(?[-+]?{0}{1}{2}{3}", - numRegex, // capture base (integral) Quantity value - @"(?:[eE][-+]?\d+)?)", // capture exponential (if any), end of Quantity capturing - @"\s?", // ignore whitespace (allows both "1kg", "1 kg") - @"(?\S+)"); // capture Unit (non-whitespace) input - - var regex = new Regex(regexString); - GroupCollection groups = regex.Match(str.Trim()).Groups; - - var valueString = groups["value"].Value; - var unitString = groups["unit"].Value; - - if (valueString == "" || unitString == "") + var exponentialRegex = @"(?:[eE][-+]?\d+)?)"; + var regexString = string.Format(@"(?:\s*(?[-+]?{0}{1}{2}{3})?{4}{5}", + numRegex, // capture base (integral) Quantity value + exponentialRegex, // capture exponential (if any), end of Quantity capturing + @"\s?", // ignore whitespace (allows both "1kg", "1 kg") + @"(?[^\s\d,]+)", // capture Unit (non-whitespace) input + @"(and)?,?", // allow "and" & "," separators between quantities + @"(?[a-z]*)?"); // capture invalid input + + var quantities = ParseWithRegex(regexString, str, formatProvider); + if (quantities.Count == 0) { - var ex = new ArgumentException( - "Expected valid quantity and unit. Input string needs to be in the format \" or \".", "str"); - ex.Data["input"] = str; - ex.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw ex; + throw new ArgumentException( + "Expected string to have at least one pair of quantity and unit in the format" + + " \"<quantity> <unit>\". Eg. \"5.5 m\" or \"1ft 2in\""); } + return quantities.Aggregate((x, y) => x + y); + } - try - { - InformationUnit unit = ParseUnit(unitString, formatProvider); - double value = double.Parse(valueString, formatProvider); + /// + /// Parse a string given a particular regular expression. + /// + /// Error parsing string. + private static List ParseWithRegex(string regexString, string str, IFormatProvider formatProvider = null) + { + var regex = new Regex(regexString); + MatchCollection matches = regex.Matches(str.Trim()); + var converted = new List(); - return From(value, unit); - } - catch (Exception e) + foreach (Match match in matches) { - var newEx = new UnitsNetException("Error parsing string.", e); - newEx.Data["input"] = str; - newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw newEx; + GroupCollection groups = match.Groups; + + var valueString = groups["value"].Value; + var unitString = groups["unit"].Value; + if (groups["invalid"].Value != "") + { + var newEx = new UnitsNetException("Invalid string detected: " + groups["invalid"].Value); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } + if (valueString == "" && unitString == "") continue; + + try + { + InformationUnit unit = ParseUnit(unitString, formatProvider); + double value = double.Parse(valueString, formatProvider); + + converted.Add(From(value, unit)); + } + catch (Exception ex) + { + var newEx = new UnitsNetException("Error parsing string.", ex); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } } + return converted; } /// diff --git a/UnitsNet/GeneratedCode/UnitClasses/KinematicViscosity.g.cs b/UnitsNet/GeneratedCode/UnitClasses/KinematicViscosity.g.cs index 2172ced19c..f18649eff5 100644 --- a/UnitsNet/GeneratedCode/UnitClasses/KinematicViscosity.g.cs +++ b/UnitsNet/GeneratedCode/UnitClasses/KinematicViscosity.g.cs @@ -20,6 +20,7 @@ // THE SOFTWARE. using System; +using System.Collections.Generic; using System.Globalization; using System.Text.RegularExpressions; using System.Linq; @@ -372,17 +373,16 @@ public double As(KinematicViscosityUnit unit) #region Parsing /// - /// Parse a string of the format "<quantity> <unit>". + /// Parse a string with one or two quantities of the format "<quantity> <unit>". /// /// /// Length.Parse("5.5 m", new CultureInfo("en-US")); /// /// The value of 'str' cannot be null. /// - /// Expected 2 words. Input string needs to be in the format "<quantity> <unit - /// >". + /// Expected string to have one or two pairs of quantity and unit in the format + /// "<quantity> <unit>". Eg. "5.5 m" or "1ft 2in" /// - /// Error parsing string. public static KinematicViscosity Parse(string str, IFormatProvider formatProvider = null) { if (str == null) throw new ArgumentNullException("str"); @@ -394,41 +394,70 @@ public static KinematicViscosity Parse(string str, IFormatProvider formatProvide var numRegex = string.Format(@"[\d., {0}{1}]*\d", // allows digits, dots, commas, and spaces in the quantity (must end in digit) numFormat.NumberGroupSeparator, // adds provided (or current) culture's group separator numFormat.NumberDecimalSeparator); // adds provided (or current) culture's decimal separator - var regexString = string.Format("(?[-+]?{0}{1}{2}{3}", - numRegex, // capture base (integral) Quantity value - @"(?:[eE][-+]?\d+)?)", // capture exponential (if any), end of Quantity capturing - @"\s?", // ignore whitespace (allows both "1kg", "1 kg") - @"(?\S+)"); // capture Unit (non-whitespace) input - - var regex = new Regex(regexString); - GroupCollection groups = regex.Match(str.Trim()).Groups; - - var valueString = groups["value"].Value; - var unitString = groups["unit"].Value; - - if (valueString == "" || unitString == "") + var exponentialRegex = @"(?:[eE][-+]?\d+)?)"; + var regexString = string.Format(@"(?:\s*(?[-+]?{0}{1}{2}{3})?{4}{5}", + numRegex, // capture base (integral) Quantity value + exponentialRegex, // capture exponential (if any), end of Quantity capturing + @"\s?", // ignore whitespace (allows both "1kg", "1 kg") + @"(?[^\s\d,]+)", // capture Unit (non-whitespace) input + @"(and)?,?", // allow "and" & "," separators between quantities + @"(?[a-z]*)?"); // capture invalid input + + var quantities = ParseWithRegex(regexString, str, formatProvider); + if (quantities.Count == 0) { - var ex = new ArgumentException( - "Expected valid quantity and unit. Input string needs to be in the format \" or \".", "str"); - ex.Data["input"] = str; - ex.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw ex; + throw new ArgumentException( + "Expected string to have at least one pair of quantity and unit in the format" + + " \"<quantity> <unit>\". Eg. \"5.5 m\" or \"1ft 2in\""); } + return quantities.Aggregate((x, y) => x + y); + } - try - { - KinematicViscosityUnit unit = ParseUnit(unitString, formatProvider); - double value = double.Parse(valueString, formatProvider); + /// + /// Parse a string given a particular regular expression. + /// + /// Error parsing string. + private static List ParseWithRegex(string regexString, string str, IFormatProvider formatProvider = null) + { + var regex = new Regex(regexString); + MatchCollection matches = regex.Matches(str.Trim()); + var converted = new List(); - return From(value, unit); - } - catch (Exception e) + foreach (Match match in matches) { - var newEx = new UnitsNetException("Error parsing string.", e); - newEx.Data["input"] = str; - newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw newEx; + GroupCollection groups = match.Groups; + + var valueString = groups["value"].Value; + var unitString = groups["unit"].Value; + if (groups["invalid"].Value != "") + { + var newEx = new UnitsNetException("Invalid string detected: " + groups["invalid"].Value); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } + if (valueString == "" && unitString == "") continue; + + try + { + KinematicViscosityUnit unit = ParseUnit(unitString, formatProvider); + double value = double.Parse(valueString, formatProvider); + + converted.Add(From(value, unit)); + } + catch (Exception ex) + { + var newEx = new UnitsNetException("Error parsing string.", ex); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } } + return converted; } /// diff --git a/UnitsNet/GeneratedCode/UnitClasses/Length.g.cs b/UnitsNet/GeneratedCode/UnitClasses/Length.g.cs index f2256f0fff..cd5a641b73 100644 --- a/UnitsNet/GeneratedCode/UnitClasses/Length.g.cs +++ b/UnitsNet/GeneratedCode/UnitClasses/Length.g.cs @@ -20,6 +20,7 @@ // THE SOFTWARE. using System; +using System.Collections.Generic; using System.Globalization; using System.Text.RegularExpressions; using System.Linq; @@ -472,17 +473,16 @@ public double As(LengthUnit unit) #region Parsing /// - /// Parse a string of the format "<quantity> <unit>". + /// Parse a string with one or two quantities of the format "<quantity> <unit>". /// /// /// Length.Parse("5.5 m", new CultureInfo("en-US")); /// /// The value of 'str' cannot be null. /// - /// Expected 2 words. Input string needs to be in the format "<quantity> <unit - /// >". + /// Expected string to have one or two pairs of quantity and unit in the format + /// "<quantity> <unit>". Eg. "5.5 m" or "1ft 2in" /// - /// Error parsing string. public static Length Parse(string str, IFormatProvider formatProvider = null) { if (str == null) throw new ArgumentNullException("str"); @@ -494,41 +494,70 @@ public static Length Parse(string str, IFormatProvider formatProvider = null) var numRegex = string.Format(@"[\d., {0}{1}]*\d", // allows digits, dots, commas, and spaces in the quantity (must end in digit) numFormat.NumberGroupSeparator, // adds provided (or current) culture's group separator numFormat.NumberDecimalSeparator); // adds provided (or current) culture's decimal separator - var regexString = string.Format("(?[-+]?{0}{1}{2}{3}", - numRegex, // capture base (integral) Quantity value - @"(?:[eE][-+]?\d+)?)", // capture exponential (if any), end of Quantity capturing - @"\s?", // ignore whitespace (allows both "1kg", "1 kg") - @"(?\S+)"); // capture Unit (non-whitespace) input - - var regex = new Regex(regexString); - GroupCollection groups = regex.Match(str.Trim()).Groups; - - var valueString = groups["value"].Value; - var unitString = groups["unit"].Value; - - if (valueString == "" || unitString == "") + var exponentialRegex = @"(?:[eE][-+]?\d+)?)"; + var regexString = string.Format(@"(?:\s*(?[-+]?{0}{1}{2}{3})?{4}{5}", + numRegex, // capture base (integral) Quantity value + exponentialRegex, // capture exponential (if any), end of Quantity capturing + @"\s?", // ignore whitespace (allows both "1kg", "1 kg") + @"(?[^\s\d,]+)", // capture Unit (non-whitespace) input + @"(and)?,?", // allow "and" & "," separators between quantities + @"(?[a-z]*)?"); // capture invalid input + + var quantities = ParseWithRegex(regexString, str, formatProvider); + if (quantities.Count == 0) { - var ex = new ArgumentException( - "Expected valid quantity and unit. Input string needs to be in the format \" or \".", "str"); - ex.Data["input"] = str; - ex.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw ex; + throw new ArgumentException( + "Expected string to have at least one pair of quantity and unit in the format" + + " \"<quantity> <unit>\". Eg. \"5.5 m\" or \"1ft 2in\""); } + return quantities.Aggregate((x, y) => x + y); + } - try - { - LengthUnit unit = ParseUnit(unitString, formatProvider); - double value = double.Parse(valueString, formatProvider); + /// + /// Parse a string given a particular regular expression. + /// + /// Error parsing string. + private static List ParseWithRegex(string regexString, string str, IFormatProvider formatProvider = null) + { + var regex = new Regex(regexString); + MatchCollection matches = regex.Matches(str.Trim()); + var converted = new List(); - return From(value, unit); - } - catch (Exception e) + foreach (Match match in matches) { - var newEx = new UnitsNetException("Error parsing string.", e); - newEx.Data["input"] = str; - newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw newEx; + GroupCollection groups = match.Groups; + + var valueString = groups["value"].Value; + var unitString = groups["unit"].Value; + if (groups["invalid"].Value != "") + { + var newEx = new UnitsNetException("Invalid string detected: " + groups["invalid"].Value); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } + if (valueString == "" && unitString == "") continue; + + try + { + LengthUnit unit = ParseUnit(unitString, formatProvider); + double value = double.Parse(valueString, formatProvider); + + converted.Add(From(value, unit)); + } + catch (Exception ex) + { + var newEx = new UnitsNetException("Error parsing string.", ex); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } } + return converted; } /// diff --git a/UnitsNet/GeneratedCode/UnitClasses/Level.g.cs b/UnitsNet/GeneratedCode/UnitClasses/Level.g.cs index cf5d3a2df1..ab78ee9fc8 100644 --- a/UnitsNet/GeneratedCode/UnitClasses/Level.g.cs +++ b/UnitsNet/GeneratedCode/UnitClasses/Level.g.cs @@ -20,6 +20,7 @@ // THE SOFTWARE. using System; +using System.Collections.Generic; using System.Globalization; using System.Text.RegularExpressions; using System.Linq; @@ -260,17 +261,16 @@ public double As(LevelUnit unit) #region Parsing /// - /// Parse a string of the format "<quantity> <unit>". + /// Parse a string with one or two quantities of the format "<quantity> <unit>". /// /// /// Length.Parse("5.5 m", new CultureInfo("en-US")); /// /// The value of 'str' cannot be null. /// - /// Expected 2 words. Input string needs to be in the format "<quantity> <unit - /// >". + /// Expected string to have one or two pairs of quantity and unit in the format + /// "<quantity> <unit>". Eg. "5.5 m" or "1ft 2in" /// - /// Error parsing string. public static Level Parse(string str, IFormatProvider formatProvider = null) { if (str == null) throw new ArgumentNullException("str"); @@ -282,41 +282,70 @@ public static Level Parse(string str, IFormatProvider formatProvider = null) var numRegex = string.Format(@"[\d., {0}{1}]*\d", // allows digits, dots, commas, and spaces in the quantity (must end in digit) numFormat.NumberGroupSeparator, // adds provided (or current) culture's group separator numFormat.NumberDecimalSeparator); // adds provided (or current) culture's decimal separator - var regexString = string.Format("(?[-+]?{0}{1}{2}{3}", - numRegex, // capture base (integral) Quantity value - @"(?:[eE][-+]?\d+)?)", // capture exponential (if any), end of Quantity capturing - @"\s?", // ignore whitespace (allows both "1kg", "1 kg") - @"(?\S+)"); // capture Unit (non-whitespace) input - - var regex = new Regex(regexString); - GroupCollection groups = regex.Match(str.Trim()).Groups; - - var valueString = groups["value"].Value; - var unitString = groups["unit"].Value; - - if (valueString == "" || unitString == "") + var exponentialRegex = @"(?:[eE][-+]?\d+)?)"; + var regexString = string.Format(@"(?:\s*(?[-+]?{0}{1}{2}{3})?{4}{5}", + numRegex, // capture base (integral) Quantity value + exponentialRegex, // capture exponential (if any), end of Quantity capturing + @"\s?", // ignore whitespace (allows both "1kg", "1 kg") + @"(?[^\s\d,]+)", // capture Unit (non-whitespace) input + @"(and)?,?", // allow "and" & "," separators between quantities + @"(?[a-z]*)?"); // capture invalid input + + var quantities = ParseWithRegex(regexString, str, formatProvider); + if (quantities.Count == 0) { - var ex = new ArgumentException( - "Expected valid quantity and unit. Input string needs to be in the format \" or \".", "str"); - ex.Data["input"] = str; - ex.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw ex; + throw new ArgumentException( + "Expected string to have at least one pair of quantity and unit in the format" + + " \"<quantity> <unit>\". Eg. \"5.5 m\" or \"1ft 2in\""); } + return quantities.Aggregate((x, y) => x + y); + } - try - { - LevelUnit unit = ParseUnit(unitString, formatProvider); - double value = double.Parse(valueString, formatProvider); + /// + /// Parse a string given a particular regular expression. + /// + /// Error parsing string. + private static List ParseWithRegex(string regexString, string str, IFormatProvider formatProvider = null) + { + var regex = new Regex(regexString); + MatchCollection matches = regex.Matches(str.Trim()); + var converted = new List(); - return From(value, unit); - } - catch (Exception e) + foreach (Match match in matches) { - var newEx = new UnitsNetException("Error parsing string.", e); - newEx.Data["input"] = str; - newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw newEx; + GroupCollection groups = match.Groups; + + var valueString = groups["value"].Value; + var unitString = groups["unit"].Value; + if (groups["invalid"].Value != "") + { + var newEx = new UnitsNetException("Invalid string detected: " + groups["invalid"].Value); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } + if (valueString == "" && unitString == "") continue; + + try + { + LevelUnit unit = ParseUnit(unitString, formatProvider); + double value = double.Parse(valueString, formatProvider); + + converted.Add(From(value, unit)); + } + catch (Exception ex) + { + var newEx = new UnitsNetException("Error parsing string.", ex); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } } + return converted; } /// diff --git a/UnitsNet/GeneratedCode/UnitClasses/Mass.g.cs b/UnitsNet/GeneratedCode/UnitClasses/Mass.g.cs index b653b3bcbd..7c52b1591c 100644 --- a/UnitsNet/GeneratedCode/UnitClasses/Mass.g.cs +++ b/UnitsNet/GeneratedCode/UnitClasses/Mass.g.cs @@ -20,6 +20,7 @@ // THE SOFTWARE. using System; +using System.Collections.Generic; using System.Globalization; using System.Text.RegularExpressions; using System.Linq; @@ -552,17 +553,16 @@ public double As(MassUnit unit) #region Parsing /// - /// Parse a string of the format "<quantity> <unit>". + /// Parse a string with one or two quantities of the format "<quantity> <unit>". /// /// /// Length.Parse("5.5 m", new CultureInfo("en-US")); /// /// The value of 'str' cannot be null. /// - /// Expected 2 words. Input string needs to be in the format "<quantity> <unit - /// >". + /// Expected string to have one or two pairs of quantity and unit in the format + /// "<quantity> <unit>". Eg. "5.5 m" or "1ft 2in" /// - /// Error parsing string. public static Mass Parse(string str, IFormatProvider formatProvider = null) { if (str == null) throw new ArgumentNullException("str"); @@ -574,41 +574,70 @@ public static Mass Parse(string str, IFormatProvider formatProvider = null) var numRegex = string.Format(@"[\d., {0}{1}]*\d", // allows digits, dots, commas, and spaces in the quantity (must end in digit) numFormat.NumberGroupSeparator, // adds provided (or current) culture's group separator numFormat.NumberDecimalSeparator); // adds provided (or current) culture's decimal separator - var regexString = string.Format("(?[-+]?{0}{1}{2}{3}", - numRegex, // capture base (integral) Quantity value - @"(?:[eE][-+]?\d+)?)", // capture exponential (if any), end of Quantity capturing - @"\s?", // ignore whitespace (allows both "1kg", "1 kg") - @"(?\S+)"); // capture Unit (non-whitespace) input - - var regex = new Regex(regexString); - GroupCollection groups = regex.Match(str.Trim()).Groups; - - var valueString = groups["value"].Value; - var unitString = groups["unit"].Value; - - if (valueString == "" || unitString == "") + var exponentialRegex = @"(?:[eE][-+]?\d+)?)"; + var regexString = string.Format(@"(?:\s*(?[-+]?{0}{1}{2}{3})?{4}{5}", + numRegex, // capture base (integral) Quantity value + exponentialRegex, // capture exponential (if any), end of Quantity capturing + @"\s?", // ignore whitespace (allows both "1kg", "1 kg") + @"(?[^\s\d,]+)", // capture Unit (non-whitespace) input + @"(and)?,?", // allow "and" & "," separators between quantities + @"(?[a-z]*)?"); // capture invalid input + + var quantities = ParseWithRegex(regexString, str, formatProvider); + if (quantities.Count == 0) { - var ex = new ArgumentException( - "Expected valid quantity and unit. Input string needs to be in the format \" or \".", "str"); - ex.Data["input"] = str; - ex.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw ex; + throw new ArgumentException( + "Expected string to have at least one pair of quantity and unit in the format" + + " \"<quantity> <unit>\". Eg. \"5.5 m\" or \"1ft 2in\""); } + return quantities.Aggregate((x, y) => x + y); + } - try - { - MassUnit unit = ParseUnit(unitString, formatProvider); - double value = double.Parse(valueString, formatProvider); + /// + /// Parse a string given a particular regular expression. + /// + /// Error parsing string. + private static List ParseWithRegex(string regexString, string str, IFormatProvider formatProvider = null) + { + var regex = new Regex(regexString); + MatchCollection matches = regex.Matches(str.Trim()); + var converted = new List(); - return From(value, unit); - } - catch (Exception e) + foreach (Match match in matches) { - var newEx = new UnitsNetException("Error parsing string.", e); - newEx.Data["input"] = str; - newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw newEx; + GroupCollection groups = match.Groups; + + var valueString = groups["value"].Value; + var unitString = groups["unit"].Value; + if (groups["invalid"].Value != "") + { + var newEx = new UnitsNetException("Invalid string detected: " + groups["invalid"].Value); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } + if (valueString == "" && unitString == "") continue; + + try + { + MassUnit unit = ParseUnit(unitString, formatProvider); + double value = double.Parse(valueString, formatProvider); + + converted.Add(From(value, unit)); + } + catch (Exception ex) + { + var newEx = new UnitsNetException("Error parsing string.", ex); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } } + return converted; } /// diff --git a/UnitsNet/GeneratedCode/UnitClasses/Power.g.cs b/UnitsNet/GeneratedCode/UnitClasses/Power.g.cs index 11e88ee66c..d2a21e49ca 100644 --- a/UnitsNet/GeneratedCode/UnitClasses/Power.g.cs +++ b/UnitsNet/GeneratedCode/UnitClasses/Power.g.cs @@ -20,6 +20,7 @@ // THE SOFTWARE. using System; +using System.Collections.Generic; using System.Globalization; using System.Text.RegularExpressions; using System.Linq; @@ -532,17 +533,16 @@ public double As(PowerUnit unit) #region Parsing /// - /// Parse a string of the format "<quantity> <unit>". + /// Parse a string with one or two quantities of the format "<quantity> <unit>". /// /// /// Length.Parse("5.5 m", new CultureInfo("en-US")); /// /// The value of 'str' cannot be null. /// - /// Expected 2 words. Input string needs to be in the format "<quantity> <unit - /// >". + /// Expected string to have one or two pairs of quantity and unit in the format + /// "<quantity> <unit>". Eg. "5.5 m" or "1ft 2in" /// - /// Error parsing string. public static Power Parse(string str, IFormatProvider formatProvider = null) { if (str == null) throw new ArgumentNullException("str"); @@ -554,41 +554,70 @@ public static Power Parse(string str, IFormatProvider formatProvider = null) var numRegex = string.Format(@"[\d., {0}{1}]*\d", // allows digits, dots, commas, and spaces in the quantity (must end in digit) numFormat.NumberGroupSeparator, // adds provided (or current) culture's group separator numFormat.NumberDecimalSeparator); // adds provided (or current) culture's decimal separator - var regexString = string.Format("(?[-+]?{0}{1}{2}{3}", - numRegex, // capture base (integral) Quantity value - @"(?:[eE][-+]?\d+)?)", // capture exponential (if any), end of Quantity capturing - @"\s?", // ignore whitespace (allows both "1kg", "1 kg") - @"(?\S+)"); // capture Unit (non-whitespace) input - - var regex = new Regex(regexString); - GroupCollection groups = regex.Match(str.Trim()).Groups; - - var valueString = groups["value"].Value; - var unitString = groups["unit"].Value; - - if (valueString == "" || unitString == "") + var exponentialRegex = @"(?:[eE][-+]?\d+)?)"; + var regexString = string.Format(@"(?:\s*(?[-+]?{0}{1}{2}{3})?{4}{5}", + numRegex, // capture base (integral) Quantity value + exponentialRegex, // capture exponential (if any), end of Quantity capturing + @"\s?", // ignore whitespace (allows both "1kg", "1 kg") + @"(?[^\s\d,]+)", // capture Unit (non-whitespace) input + @"(and)?,?", // allow "and" & "," separators between quantities + @"(?[a-z]*)?"); // capture invalid input + + var quantities = ParseWithRegex(regexString, str, formatProvider); + if (quantities.Count == 0) { - var ex = new ArgumentException( - "Expected valid quantity and unit. Input string needs to be in the format \" or \".", "str"); - ex.Data["input"] = str; - ex.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw ex; + throw new ArgumentException( + "Expected string to have at least one pair of quantity and unit in the format" + + " \"<quantity> <unit>\". Eg. \"5.5 m\" or \"1ft 2in\""); } + return quantities.Aggregate((x, y) => x + y); + } - try - { - PowerUnit unit = ParseUnit(unitString, formatProvider); - double value = double.Parse(valueString, formatProvider); + /// + /// Parse a string given a particular regular expression. + /// + /// Error parsing string. + private static List ParseWithRegex(string regexString, string str, IFormatProvider formatProvider = null) + { + var regex = new Regex(regexString); + MatchCollection matches = regex.Matches(str.Trim()); + var converted = new List(); - return From(value, unit); - } - catch (Exception e) + foreach (Match match in matches) { - var newEx = new UnitsNetException("Error parsing string.", e); - newEx.Data["input"] = str; - newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw newEx; + GroupCollection groups = match.Groups; + + var valueString = groups["value"].Value; + var unitString = groups["unit"].Value; + if (groups["invalid"].Value != "") + { + var newEx = new UnitsNetException("Invalid string detected: " + groups["invalid"].Value); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } + if (valueString == "" && unitString == "") continue; + + try + { + PowerUnit unit = ParseUnit(unitString, formatProvider); + double value = double.Parse(valueString, formatProvider); + + converted.Add(From(value, unit)); + } + catch (Exception ex) + { + var newEx = new UnitsNetException("Error parsing string.", ex); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } } + return converted; } /// diff --git a/UnitsNet/GeneratedCode/UnitClasses/PowerRatio.g.cs b/UnitsNet/GeneratedCode/UnitClasses/PowerRatio.g.cs index 9381ad71ad..a428bd4e9c 100644 --- a/UnitsNet/GeneratedCode/UnitClasses/PowerRatio.g.cs +++ b/UnitsNet/GeneratedCode/UnitClasses/PowerRatio.g.cs @@ -20,6 +20,7 @@ // THE SOFTWARE. using System; +using System.Collections.Generic; using System.Globalization; using System.Text.RegularExpressions; using System.Linq; @@ -260,17 +261,16 @@ public double As(PowerRatioUnit unit) #region Parsing /// - /// Parse a string of the format "<quantity> <unit>". + /// Parse a string with one or two quantities of the format "<quantity> <unit>". /// /// /// Length.Parse("5.5 m", new CultureInfo("en-US")); /// /// The value of 'str' cannot be null. /// - /// Expected 2 words. Input string needs to be in the format "<quantity> <unit - /// >". + /// Expected string to have one or two pairs of quantity and unit in the format + /// "<quantity> <unit>". Eg. "5.5 m" or "1ft 2in" /// - /// Error parsing string. public static PowerRatio Parse(string str, IFormatProvider formatProvider = null) { if (str == null) throw new ArgumentNullException("str"); @@ -282,41 +282,70 @@ public static PowerRatio Parse(string str, IFormatProvider formatProvider = null var numRegex = string.Format(@"[\d., {0}{1}]*\d", // allows digits, dots, commas, and spaces in the quantity (must end in digit) numFormat.NumberGroupSeparator, // adds provided (or current) culture's group separator numFormat.NumberDecimalSeparator); // adds provided (or current) culture's decimal separator - var regexString = string.Format("(?[-+]?{0}{1}{2}{3}", - numRegex, // capture base (integral) Quantity value - @"(?:[eE][-+]?\d+)?)", // capture exponential (if any), end of Quantity capturing - @"\s?", // ignore whitespace (allows both "1kg", "1 kg") - @"(?\S+)"); // capture Unit (non-whitespace) input - - var regex = new Regex(regexString); - GroupCollection groups = regex.Match(str.Trim()).Groups; - - var valueString = groups["value"].Value; - var unitString = groups["unit"].Value; - - if (valueString == "" || unitString == "") + var exponentialRegex = @"(?:[eE][-+]?\d+)?)"; + var regexString = string.Format(@"(?:\s*(?[-+]?{0}{1}{2}{3})?{4}{5}", + numRegex, // capture base (integral) Quantity value + exponentialRegex, // capture exponential (if any), end of Quantity capturing + @"\s?", // ignore whitespace (allows both "1kg", "1 kg") + @"(?[^\s\d,]+)", // capture Unit (non-whitespace) input + @"(and)?,?", // allow "and" & "," separators between quantities + @"(?[a-z]*)?"); // capture invalid input + + var quantities = ParseWithRegex(regexString, str, formatProvider); + if (quantities.Count == 0) { - var ex = new ArgumentException( - "Expected valid quantity and unit. Input string needs to be in the format \" or \".", "str"); - ex.Data["input"] = str; - ex.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw ex; + throw new ArgumentException( + "Expected string to have at least one pair of quantity and unit in the format" + + " \"<quantity> <unit>\". Eg. \"5.5 m\" or \"1ft 2in\""); } + return quantities.Aggregate((x, y) => x + y); + } - try - { - PowerRatioUnit unit = ParseUnit(unitString, formatProvider); - double value = double.Parse(valueString, formatProvider); + /// + /// Parse a string given a particular regular expression. + /// + /// Error parsing string. + private static List ParseWithRegex(string regexString, string str, IFormatProvider formatProvider = null) + { + var regex = new Regex(regexString); + MatchCollection matches = regex.Matches(str.Trim()); + var converted = new List(); - return From(value, unit); - } - catch (Exception e) + foreach (Match match in matches) { - var newEx = new UnitsNetException("Error parsing string.", e); - newEx.Data["input"] = str; - newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw newEx; + GroupCollection groups = match.Groups; + + var valueString = groups["value"].Value; + var unitString = groups["unit"].Value; + if (groups["invalid"].Value != "") + { + var newEx = new UnitsNetException("Invalid string detected: " + groups["invalid"].Value); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } + if (valueString == "" && unitString == "") continue; + + try + { + PowerRatioUnit unit = ParseUnit(unitString, formatProvider); + double value = double.Parse(valueString, formatProvider); + + converted.Add(From(value, unit)); + } + catch (Exception ex) + { + var newEx = new UnitsNetException("Error parsing string.", ex); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } } + return converted; } /// diff --git a/UnitsNet/GeneratedCode/UnitClasses/Pressure.g.cs b/UnitsNet/GeneratedCode/UnitClasses/Pressure.g.cs index 2c0030051f..0b7b83095d 100644 --- a/UnitsNet/GeneratedCode/UnitClasses/Pressure.g.cs +++ b/UnitsNet/GeneratedCode/UnitClasses/Pressure.g.cs @@ -20,6 +20,7 @@ // THE SOFTWARE. using System; +using System.Collections.Generic; using System.Globalization; using System.Text.RegularExpressions; using System.Linq; @@ -872,17 +873,16 @@ public double As(PressureUnit unit) #region Parsing /// - /// Parse a string of the format "<quantity> <unit>". + /// Parse a string with one or two quantities of the format "<quantity> <unit>". /// /// /// Length.Parse("5.5 m", new CultureInfo("en-US")); /// /// The value of 'str' cannot be null. /// - /// Expected 2 words. Input string needs to be in the format "<quantity> <unit - /// >". + /// Expected string to have one or two pairs of quantity and unit in the format + /// "<quantity> <unit>". Eg. "5.5 m" or "1ft 2in" /// - /// Error parsing string. public static Pressure Parse(string str, IFormatProvider formatProvider = null) { if (str == null) throw new ArgumentNullException("str"); @@ -894,41 +894,70 @@ public static Pressure Parse(string str, IFormatProvider formatProvider = null) var numRegex = string.Format(@"[\d., {0}{1}]*\d", // allows digits, dots, commas, and spaces in the quantity (must end in digit) numFormat.NumberGroupSeparator, // adds provided (or current) culture's group separator numFormat.NumberDecimalSeparator); // adds provided (or current) culture's decimal separator - var regexString = string.Format("(?[-+]?{0}{1}{2}{3}", - numRegex, // capture base (integral) Quantity value - @"(?:[eE][-+]?\d+)?)", // capture exponential (if any), end of Quantity capturing - @"\s?", // ignore whitespace (allows both "1kg", "1 kg") - @"(?\S+)"); // capture Unit (non-whitespace) input - - var regex = new Regex(regexString); - GroupCollection groups = regex.Match(str.Trim()).Groups; - - var valueString = groups["value"].Value; - var unitString = groups["unit"].Value; - - if (valueString == "" || unitString == "") + var exponentialRegex = @"(?:[eE][-+]?\d+)?)"; + var regexString = string.Format(@"(?:\s*(?[-+]?{0}{1}{2}{3})?{4}{5}", + numRegex, // capture base (integral) Quantity value + exponentialRegex, // capture exponential (if any), end of Quantity capturing + @"\s?", // ignore whitespace (allows both "1kg", "1 kg") + @"(?[^\s\d,]+)", // capture Unit (non-whitespace) input + @"(and)?,?", // allow "and" & "," separators between quantities + @"(?[a-z]*)?"); // capture invalid input + + var quantities = ParseWithRegex(regexString, str, formatProvider); + if (quantities.Count == 0) { - var ex = new ArgumentException( - "Expected valid quantity and unit. Input string needs to be in the format \" or \".", "str"); - ex.Data["input"] = str; - ex.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw ex; + throw new ArgumentException( + "Expected string to have at least one pair of quantity and unit in the format" + + " \"<quantity> <unit>\". Eg. \"5.5 m\" or \"1ft 2in\""); } + return quantities.Aggregate((x, y) => x + y); + } - try - { - PressureUnit unit = ParseUnit(unitString, formatProvider); - double value = double.Parse(valueString, formatProvider); + /// + /// Parse a string given a particular regular expression. + /// + /// Error parsing string. + private static List ParseWithRegex(string regexString, string str, IFormatProvider formatProvider = null) + { + var regex = new Regex(regexString); + MatchCollection matches = regex.Matches(str.Trim()); + var converted = new List(); - return From(value, unit); - } - catch (Exception e) + foreach (Match match in matches) { - var newEx = new UnitsNetException("Error parsing string.", e); - newEx.Data["input"] = str; - newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw newEx; + GroupCollection groups = match.Groups; + + var valueString = groups["value"].Value; + var unitString = groups["unit"].Value; + if (groups["invalid"].Value != "") + { + var newEx = new UnitsNetException("Invalid string detected: " + groups["invalid"].Value); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } + if (valueString == "" && unitString == "") continue; + + try + { + PressureUnit unit = ParseUnit(unitString, formatProvider); + double value = double.Parse(valueString, formatProvider); + + converted.Add(From(value, unit)); + } + catch (Exception ex) + { + var newEx = new UnitsNetException("Error parsing string.", ex); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } } + return converted; } /// diff --git a/UnitsNet/GeneratedCode/UnitClasses/Ratio.g.cs b/UnitsNet/GeneratedCode/UnitClasses/Ratio.g.cs index d48d2184ad..77960770fa 100644 --- a/UnitsNet/GeneratedCode/UnitClasses/Ratio.g.cs +++ b/UnitsNet/GeneratedCode/UnitClasses/Ratio.g.cs @@ -20,6 +20,7 @@ // THE SOFTWARE. using System; +using System.Collections.Generic; using System.Globalization; using System.Text.RegularExpressions; using System.Linq; @@ -332,17 +333,16 @@ public double As(RatioUnit unit) #region Parsing /// - /// Parse a string of the format "<quantity> <unit>". + /// Parse a string with one or two quantities of the format "<quantity> <unit>". /// /// /// Length.Parse("5.5 m", new CultureInfo("en-US")); /// /// The value of 'str' cannot be null. /// - /// Expected 2 words. Input string needs to be in the format "<quantity> <unit - /// >". + /// Expected string to have one or two pairs of quantity and unit in the format + /// "<quantity> <unit>". Eg. "5.5 m" or "1ft 2in" /// - /// Error parsing string. public static Ratio Parse(string str, IFormatProvider formatProvider = null) { if (str == null) throw new ArgumentNullException("str"); @@ -354,41 +354,70 @@ public static Ratio Parse(string str, IFormatProvider formatProvider = null) var numRegex = string.Format(@"[\d., {0}{1}]*\d", // allows digits, dots, commas, and spaces in the quantity (must end in digit) numFormat.NumberGroupSeparator, // adds provided (or current) culture's group separator numFormat.NumberDecimalSeparator); // adds provided (or current) culture's decimal separator - var regexString = string.Format("(?[-+]?{0}{1}{2}{3}", - numRegex, // capture base (integral) Quantity value - @"(?:[eE][-+]?\d+)?)", // capture exponential (if any), end of Quantity capturing - @"\s?", // ignore whitespace (allows both "1kg", "1 kg") - @"(?\S+)"); // capture Unit (non-whitespace) input - - var regex = new Regex(regexString); - GroupCollection groups = regex.Match(str.Trim()).Groups; - - var valueString = groups["value"].Value; - var unitString = groups["unit"].Value; - - if (valueString == "" || unitString == "") + var exponentialRegex = @"(?:[eE][-+]?\d+)?)"; + var regexString = string.Format(@"(?:\s*(?[-+]?{0}{1}{2}{3})?{4}{5}", + numRegex, // capture base (integral) Quantity value + exponentialRegex, // capture exponential (if any), end of Quantity capturing + @"\s?", // ignore whitespace (allows both "1kg", "1 kg") + @"(?[^\s\d,]+)", // capture Unit (non-whitespace) input + @"(and)?,?", // allow "and" & "," separators between quantities + @"(?[a-z]*)?"); // capture invalid input + + var quantities = ParseWithRegex(regexString, str, formatProvider); + if (quantities.Count == 0) { - var ex = new ArgumentException( - "Expected valid quantity and unit. Input string needs to be in the format \" or \".", "str"); - ex.Data["input"] = str; - ex.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw ex; + throw new ArgumentException( + "Expected string to have at least one pair of quantity and unit in the format" + + " \"<quantity> <unit>\". Eg. \"5.5 m\" or \"1ft 2in\""); } + return quantities.Aggregate((x, y) => x + y); + } - try - { - RatioUnit unit = ParseUnit(unitString, formatProvider); - double value = double.Parse(valueString, formatProvider); + /// + /// Parse a string given a particular regular expression. + /// + /// Error parsing string. + private static List ParseWithRegex(string regexString, string str, IFormatProvider formatProvider = null) + { + var regex = new Regex(regexString); + MatchCollection matches = regex.Matches(str.Trim()); + var converted = new List(); - return From(value, unit); - } - catch (Exception e) + foreach (Match match in matches) { - var newEx = new UnitsNetException("Error parsing string.", e); - newEx.Data["input"] = str; - newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw newEx; + GroupCollection groups = match.Groups; + + var valueString = groups["value"].Value; + var unitString = groups["unit"].Value; + if (groups["invalid"].Value != "") + { + var newEx = new UnitsNetException("Invalid string detected: " + groups["invalid"].Value); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } + if (valueString == "" && unitString == "") continue; + + try + { + RatioUnit unit = ParseUnit(unitString, formatProvider); + double value = double.Parse(valueString, formatProvider); + + converted.Add(From(value, unit)); + } + catch (Exception ex) + { + var newEx = new UnitsNetException("Error parsing string.", ex); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } } + return converted; } /// diff --git a/UnitsNet/GeneratedCode/UnitClasses/RotationalSpeed.g.cs b/UnitsNet/GeneratedCode/UnitClasses/RotationalSpeed.g.cs index f8e8be0a1d..4123a89591 100644 --- a/UnitsNet/GeneratedCode/UnitClasses/RotationalSpeed.g.cs +++ b/UnitsNet/GeneratedCode/UnitClasses/RotationalSpeed.g.cs @@ -20,6 +20,7 @@ // THE SOFTWARE. using System; +using System.Collections.Generic; using System.Globalization; using System.Text.RegularExpressions; using System.Linq; @@ -252,17 +253,16 @@ public double As(RotationalSpeedUnit unit) #region Parsing /// - /// Parse a string of the format "<quantity> <unit>". + /// Parse a string with one or two quantities of the format "<quantity> <unit>". /// /// /// Length.Parse("5.5 m", new CultureInfo("en-US")); /// /// The value of 'str' cannot be null. /// - /// Expected 2 words. Input string needs to be in the format "<quantity> <unit - /// >". + /// Expected string to have one or two pairs of quantity and unit in the format + /// "<quantity> <unit>". Eg. "5.5 m" or "1ft 2in" /// - /// Error parsing string. public static RotationalSpeed Parse(string str, IFormatProvider formatProvider = null) { if (str == null) throw new ArgumentNullException("str"); @@ -274,41 +274,70 @@ public static RotationalSpeed Parse(string str, IFormatProvider formatProvider = var numRegex = string.Format(@"[\d., {0}{1}]*\d", // allows digits, dots, commas, and spaces in the quantity (must end in digit) numFormat.NumberGroupSeparator, // adds provided (or current) culture's group separator numFormat.NumberDecimalSeparator); // adds provided (or current) culture's decimal separator - var regexString = string.Format("(?[-+]?{0}{1}{2}{3}", - numRegex, // capture base (integral) Quantity value - @"(?:[eE][-+]?\d+)?)", // capture exponential (if any), end of Quantity capturing - @"\s?", // ignore whitespace (allows both "1kg", "1 kg") - @"(?\S+)"); // capture Unit (non-whitespace) input - - var regex = new Regex(regexString); - GroupCollection groups = regex.Match(str.Trim()).Groups; - - var valueString = groups["value"].Value; - var unitString = groups["unit"].Value; - - if (valueString == "" || unitString == "") + var exponentialRegex = @"(?:[eE][-+]?\d+)?)"; + var regexString = string.Format(@"(?:\s*(?[-+]?{0}{1}{2}{3})?{4}{5}", + numRegex, // capture base (integral) Quantity value + exponentialRegex, // capture exponential (if any), end of Quantity capturing + @"\s?", // ignore whitespace (allows both "1kg", "1 kg") + @"(?[^\s\d,]+)", // capture Unit (non-whitespace) input + @"(and)?,?", // allow "and" & "," separators between quantities + @"(?[a-z]*)?"); // capture invalid input + + var quantities = ParseWithRegex(regexString, str, formatProvider); + if (quantities.Count == 0) { - var ex = new ArgumentException( - "Expected valid quantity and unit. Input string needs to be in the format \" or \".", "str"); - ex.Data["input"] = str; - ex.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw ex; + throw new ArgumentException( + "Expected string to have at least one pair of quantity and unit in the format" + + " \"<quantity> <unit>\". Eg. \"5.5 m\" or \"1ft 2in\""); } + return quantities.Aggregate((x, y) => x + y); + } - try - { - RotationalSpeedUnit unit = ParseUnit(unitString, formatProvider); - double value = double.Parse(valueString, formatProvider); + /// + /// Parse a string given a particular regular expression. + /// + /// Error parsing string. + private static List ParseWithRegex(string regexString, string str, IFormatProvider formatProvider = null) + { + var regex = new Regex(regexString); + MatchCollection matches = regex.Matches(str.Trim()); + var converted = new List(); - return From(value, unit); - } - catch (Exception e) + foreach (Match match in matches) { - var newEx = new UnitsNetException("Error parsing string.", e); - newEx.Data["input"] = str; - newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw newEx; + GroupCollection groups = match.Groups; + + var valueString = groups["value"].Value; + var unitString = groups["unit"].Value; + if (groups["invalid"].Value != "") + { + var newEx = new UnitsNetException("Invalid string detected: " + groups["invalid"].Value); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } + if (valueString == "" && unitString == "") continue; + + try + { + RotationalSpeedUnit unit = ParseUnit(unitString, formatProvider); + double value = double.Parse(valueString, formatProvider); + + converted.Add(From(value, unit)); + } + catch (Exception ex) + { + var newEx = new UnitsNetException("Error parsing string.", ex); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } } + return converted; } /// diff --git a/UnitsNet/GeneratedCode/UnitClasses/SpecificWeight.g.cs b/UnitsNet/GeneratedCode/UnitClasses/SpecificWeight.g.cs index 78651275a2..84cb1f23ab 100644 --- a/UnitsNet/GeneratedCode/UnitClasses/SpecificWeight.g.cs +++ b/UnitsNet/GeneratedCode/UnitClasses/SpecificWeight.g.cs @@ -20,6 +20,7 @@ // THE SOFTWARE. using System; +using System.Collections.Generic; using System.Globalization; using System.Text.RegularExpressions; using System.Linq; @@ -532,17 +533,16 @@ public double As(SpecificWeightUnit unit) #region Parsing /// - /// Parse a string of the format "<quantity> <unit>". + /// Parse a string with one or two quantities of the format "<quantity> <unit>". /// /// /// Length.Parse("5.5 m", new CultureInfo("en-US")); /// /// The value of 'str' cannot be null. /// - /// Expected 2 words. Input string needs to be in the format "<quantity> <unit - /// >". + /// Expected string to have one or two pairs of quantity and unit in the format + /// "<quantity> <unit>". Eg. "5.5 m" or "1ft 2in" /// - /// Error parsing string. public static SpecificWeight Parse(string str, IFormatProvider formatProvider = null) { if (str == null) throw new ArgumentNullException("str"); @@ -554,41 +554,70 @@ public static SpecificWeight Parse(string str, IFormatProvider formatProvider = var numRegex = string.Format(@"[\d., {0}{1}]*\d", // allows digits, dots, commas, and spaces in the quantity (must end in digit) numFormat.NumberGroupSeparator, // adds provided (or current) culture's group separator numFormat.NumberDecimalSeparator); // adds provided (or current) culture's decimal separator - var regexString = string.Format("(?[-+]?{0}{1}{2}{3}", - numRegex, // capture base (integral) Quantity value - @"(?:[eE][-+]?\d+)?)", // capture exponential (if any), end of Quantity capturing - @"\s?", // ignore whitespace (allows both "1kg", "1 kg") - @"(?\S+)"); // capture Unit (non-whitespace) input - - var regex = new Regex(regexString); - GroupCollection groups = regex.Match(str.Trim()).Groups; - - var valueString = groups["value"].Value; - var unitString = groups["unit"].Value; - - if (valueString == "" || unitString == "") + var exponentialRegex = @"(?:[eE][-+]?\d+)?)"; + var regexString = string.Format(@"(?:\s*(?[-+]?{0}{1}{2}{3})?{4}{5}", + numRegex, // capture base (integral) Quantity value + exponentialRegex, // capture exponential (if any), end of Quantity capturing + @"\s?", // ignore whitespace (allows both "1kg", "1 kg") + @"(?[^\s\d,]+)", // capture Unit (non-whitespace) input + @"(and)?,?", // allow "and" & "," separators between quantities + @"(?[a-z]*)?"); // capture invalid input + + var quantities = ParseWithRegex(regexString, str, formatProvider); + if (quantities.Count == 0) { - var ex = new ArgumentException( - "Expected valid quantity and unit. Input string needs to be in the format \" or \".", "str"); - ex.Data["input"] = str; - ex.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw ex; + throw new ArgumentException( + "Expected string to have at least one pair of quantity and unit in the format" + + " \"<quantity> <unit>\". Eg. \"5.5 m\" or \"1ft 2in\""); } + return quantities.Aggregate((x, y) => x + y); + } - try - { - SpecificWeightUnit unit = ParseUnit(unitString, formatProvider); - double value = double.Parse(valueString, formatProvider); + /// + /// Parse a string given a particular regular expression. + /// + /// Error parsing string. + private static List ParseWithRegex(string regexString, string str, IFormatProvider formatProvider = null) + { + var regex = new Regex(regexString); + MatchCollection matches = regex.Matches(str.Trim()); + var converted = new List(); - return From(value, unit); - } - catch (Exception e) + foreach (Match match in matches) { - var newEx = new UnitsNetException("Error parsing string.", e); - newEx.Data["input"] = str; - newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw newEx; + GroupCollection groups = match.Groups; + + var valueString = groups["value"].Value; + var unitString = groups["unit"].Value; + if (groups["invalid"].Value != "") + { + var newEx = new UnitsNetException("Invalid string detected: " + groups["invalid"].Value); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } + if (valueString == "" && unitString == "") continue; + + try + { + SpecificWeightUnit unit = ParseUnit(unitString, formatProvider); + double value = double.Parse(valueString, formatProvider); + + converted.Add(From(value, unit)); + } + catch (Exception ex) + { + var newEx = new UnitsNetException("Error parsing string.", ex); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } } + return converted; } /// diff --git a/UnitsNet/GeneratedCode/UnitClasses/Speed.g.cs b/UnitsNet/GeneratedCode/UnitClasses/Speed.g.cs index 4086050605..d764ba9f55 100644 --- a/UnitsNet/GeneratedCode/UnitClasses/Speed.g.cs +++ b/UnitsNet/GeneratedCode/UnitClasses/Speed.g.cs @@ -20,6 +20,7 @@ // THE SOFTWARE. using System; +using System.Collections.Generic; using System.Globalization; using System.Text.RegularExpressions; using System.Linq; @@ -332,17 +333,16 @@ public double As(SpeedUnit unit) #region Parsing /// - /// Parse a string of the format "<quantity> <unit>". + /// Parse a string with one or two quantities of the format "<quantity> <unit>". /// /// /// Length.Parse("5.5 m", new CultureInfo("en-US")); /// /// The value of 'str' cannot be null. /// - /// Expected 2 words. Input string needs to be in the format "<quantity> <unit - /// >". + /// Expected string to have one or two pairs of quantity and unit in the format + /// "<quantity> <unit>". Eg. "5.5 m" or "1ft 2in" /// - /// Error parsing string. public static Speed Parse(string str, IFormatProvider formatProvider = null) { if (str == null) throw new ArgumentNullException("str"); @@ -354,41 +354,70 @@ public static Speed Parse(string str, IFormatProvider formatProvider = null) var numRegex = string.Format(@"[\d., {0}{1}]*\d", // allows digits, dots, commas, and spaces in the quantity (must end in digit) numFormat.NumberGroupSeparator, // adds provided (or current) culture's group separator numFormat.NumberDecimalSeparator); // adds provided (or current) culture's decimal separator - var regexString = string.Format("(?[-+]?{0}{1}{2}{3}", - numRegex, // capture base (integral) Quantity value - @"(?:[eE][-+]?\d+)?)", // capture exponential (if any), end of Quantity capturing - @"\s?", // ignore whitespace (allows both "1kg", "1 kg") - @"(?\S+)"); // capture Unit (non-whitespace) input - - var regex = new Regex(regexString); - GroupCollection groups = regex.Match(str.Trim()).Groups; - - var valueString = groups["value"].Value; - var unitString = groups["unit"].Value; - - if (valueString == "" || unitString == "") + var exponentialRegex = @"(?:[eE][-+]?\d+)?)"; + var regexString = string.Format(@"(?:\s*(?[-+]?{0}{1}{2}{3})?{4}{5}", + numRegex, // capture base (integral) Quantity value + exponentialRegex, // capture exponential (if any), end of Quantity capturing + @"\s?", // ignore whitespace (allows both "1kg", "1 kg") + @"(?[^\s\d,]+)", // capture Unit (non-whitespace) input + @"(and)?,?", // allow "and" & "," separators between quantities + @"(?[a-z]*)?"); // capture invalid input + + var quantities = ParseWithRegex(regexString, str, formatProvider); + if (quantities.Count == 0) { - var ex = new ArgumentException( - "Expected valid quantity and unit. Input string needs to be in the format \" or \".", "str"); - ex.Data["input"] = str; - ex.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw ex; + throw new ArgumentException( + "Expected string to have at least one pair of quantity and unit in the format" + + " \"<quantity> <unit>\". Eg. \"5.5 m\" or \"1ft 2in\""); } + return quantities.Aggregate((x, y) => x + y); + } - try - { - SpeedUnit unit = ParseUnit(unitString, formatProvider); - double value = double.Parse(valueString, formatProvider); + /// + /// Parse a string given a particular regular expression. + /// + /// Error parsing string. + private static List ParseWithRegex(string regexString, string str, IFormatProvider formatProvider = null) + { + var regex = new Regex(regexString); + MatchCollection matches = regex.Matches(str.Trim()); + var converted = new List(); - return From(value, unit); - } - catch (Exception e) + foreach (Match match in matches) { - var newEx = new UnitsNetException("Error parsing string.", e); - newEx.Data["input"] = str; - newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw newEx; + GroupCollection groups = match.Groups; + + var valueString = groups["value"].Value; + var unitString = groups["unit"].Value; + if (groups["invalid"].Value != "") + { + var newEx = new UnitsNetException("Invalid string detected: " + groups["invalid"].Value); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } + if (valueString == "" && unitString == "") continue; + + try + { + SpeedUnit unit = ParseUnit(unitString, formatProvider); + double value = double.Parse(valueString, formatProvider); + + converted.Add(From(value, unit)); + } + catch (Exception ex) + { + var newEx = new UnitsNetException("Error parsing string.", ex); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } } + return converted; } /// diff --git a/UnitsNet/GeneratedCode/UnitClasses/Temperature.g.cs b/UnitsNet/GeneratedCode/UnitClasses/Temperature.g.cs index 0f71d7a087..d244edb327 100644 --- a/UnitsNet/GeneratedCode/UnitClasses/Temperature.g.cs +++ b/UnitsNet/GeneratedCode/UnitClasses/Temperature.g.cs @@ -20,6 +20,7 @@ // THE SOFTWARE. using System; +using System.Collections.Generic; using System.Globalization; using System.Text.RegularExpressions; using System.Linq; @@ -372,17 +373,16 @@ public double As(TemperatureUnit unit) #region Parsing /// - /// Parse a string of the format "<quantity> <unit>". + /// Parse a string with one or two quantities of the format "<quantity> <unit>". /// /// /// Length.Parse("5.5 m", new CultureInfo("en-US")); /// /// The value of 'str' cannot be null. /// - /// Expected 2 words. Input string needs to be in the format "<quantity> <unit - /// >". + /// Expected string to have one or two pairs of quantity and unit in the format + /// "<quantity> <unit>". Eg. "5.5 m" or "1ft 2in" /// - /// Error parsing string. public static Temperature Parse(string str, IFormatProvider formatProvider = null) { if (str == null) throw new ArgumentNullException("str"); @@ -394,41 +394,70 @@ public static Temperature Parse(string str, IFormatProvider formatProvider = nul var numRegex = string.Format(@"[\d., {0}{1}]*\d", // allows digits, dots, commas, and spaces in the quantity (must end in digit) numFormat.NumberGroupSeparator, // adds provided (or current) culture's group separator numFormat.NumberDecimalSeparator); // adds provided (or current) culture's decimal separator - var regexString = string.Format("(?[-+]?{0}{1}{2}{3}", - numRegex, // capture base (integral) Quantity value - @"(?:[eE][-+]?\d+)?)", // capture exponential (if any), end of Quantity capturing - @"\s?", // ignore whitespace (allows both "1kg", "1 kg") - @"(?\S+)"); // capture Unit (non-whitespace) input - - var regex = new Regex(regexString); - GroupCollection groups = regex.Match(str.Trim()).Groups; - - var valueString = groups["value"].Value; - var unitString = groups["unit"].Value; - - if (valueString == "" || unitString == "") + var exponentialRegex = @"(?:[eE][-+]?\d+)?)"; + var regexString = string.Format(@"(?:\s*(?[-+]?{0}{1}{2}{3})?{4}{5}", + numRegex, // capture base (integral) Quantity value + exponentialRegex, // capture exponential (if any), end of Quantity capturing + @"\s?", // ignore whitespace (allows both "1kg", "1 kg") + @"(?[^\s\d,]+)", // capture Unit (non-whitespace) input + @"(and)?,?", // allow "and" & "," separators between quantities + @"(?[a-z]*)?"); // capture invalid input + + var quantities = ParseWithRegex(regexString, str, formatProvider); + if (quantities.Count == 0) { - var ex = new ArgumentException( - "Expected valid quantity and unit. Input string needs to be in the format \" or \".", "str"); - ex.Data["input"] = str; - ex.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw ex; + throw new ArgumentException( + "Expected string to have at least one pair of quantity and unit in the format" + + " \"<quantity> <unit>\". Eg. \"5.5 m\" or \"1ft 2in\""); } + return quantities.Aggregate((x, y) => x + y); + } - try - { - TemperatureUnit unit = ParseUnit(unitString, formatProvider); - double value = double.Parse(valueString, formatProvider); + /// + /// Parse a string given a particular regular expression. + /// + /// Error parsing string. + private static List ParseWithRegex(string regexString, string str, IFormatProvider formatProvider = null) + { + var regex = new Regex(regexString); + MatchCollection matches = regex.Matches(str.Trim()); + var converted = new List(); - return From(value, unit); - } - catch (Exception e) + foreach (Match match in matches) { - var newEx = new UnitsNetException("Error parsing string.", e); - newEx.Data["input"] = str; - newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw newEx; + GroupCollection groups = match.Groups; + + var valueString = groups["value"].Value; + var unitString = groups["unit"].Value; + if (groups["invalid"].Value != "") + { + var newEx = new UnitsNetException("Invalid string detected: " + groups["invalid"].Value); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } + if (valueString == "" && unitString == "") continue; + + try + { + TemperatureUnit unit = ParseUnit(unitString, formatProvider); + double value = double.Parse(valueString, formatProvider); + + converted.Add(From(value, unit)); + } + catch (Exception ex) + { + var newEx = new UnitsNetException("Error parsing string.", ex); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } } + return converted; } /// diff --git a/UnitsNet/GeneratedCode/UnitClasses/Torque.g.cs b/UnitsNet/GeneratedCode/UnitClasses/Torque.g.cs index 9650533e82..ad1819fb35 100644 --- a/UnitsNet/GeneratedCode/UnitClasses/Torque.g.cs +++ b/UnitsNet/GeneratedCode/UnitClasses/Torque.g.cs @@ -20,6 +20,7 @@ // THE SOFTWARE. using System; +using System.Collections.Generic; using System.Globalization; using System.Text.RegularExpressions; using System.Linq; @@ -532,17 +533,16 @@ public double As(TorqueUnit unit) #region Parsing /// - /// Parse a string of the format "<quantity> <unit>". + /// Parse a string with one or two quantities of the format "<quantity> <unit>". /// /// /// Length.Parse("5.5 m", new CultureInfo("en-US")); /// /// The value of 'str' cannot be null. /// - /// Expected 2 words. Input string needs to be in the format "<quantity> <unit - /// >". + /// Expected string to have one or two pairs of quantity and unit in the format + /// "<quantity> <unit>". Eg. "5.5 m" or "1ft 2in" /// - /// Error parsing string. public static Torque Parse(string str, IFormatProvider formatProvider = null) { if (str == null) throw new ArgumentNullException("str"); @@ -554,41 +554,70 @@ public static Torque Parse(string str, IFormatProvider formatProvider = null) var numRegex = string.Format(@"[\d., {0}{1}]*\d", // allows digits, dots, commas, and spaces in the quantity (must end in digit) numFormat.NumberGroupSeparator, // adds provided (or current) culture's group separator numFormat.NumberDecimalSeparator); // adds provided (or current) culture's decimal separator - var regexString = string.Format("(?[-+]?{0}{1}{2}{3}", - numRegex, // capture base (integral) Quantity value - @"(?:[eE][-+]?\d+)?)", // capture exponential (if any), end of Quantity capturing - @"\s?", // ignore whitespace (allows both "1kg", "1 kg") - @"(?\S+)"); // capture Unit (non-whitespace) input - - var regex = new Regex(regexString); - GroupCollection groups = regex.Match(str.Trim()).Groups; - - var valueString = groups["value"].Value; - var unitString = groups["unit"].Value; - - if (valueString == "" || unitString == "") + var exponentialRegex = @"(?:[eE][-+]?\d+)?)"; + var regexString = string.Format(@"(?:\s*(?[-+]?{0}{1}{2}{3})?{4}{5}", + numRegex, // capture base (integral) Quantity value + exponentialRegex, // capture exponential (if any), end of Quantity capturing + @"\s?", // ignore whitespace (allows both "1kg", "1 kg") + @"(?[^\s\d,]+)", // capture Unit (non-whitespace) input + @"(and)?,?", // allow "and" & "," separators between quantities + @"(?[a-z]*)?"); // capture invalid input + + var quantities = ParseWithRegex(regexString, str, formatProvider); + if (quantities.Count == 0) { - var ex = new ArgumentException( - "Expected valid quantity and unit. Input string needs to be in the format \" or \".", "str"); - ex.Data["input"] = str; - ex.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw ex; + throw new ArgumentException( + "Expected string to have at least one pair of quantity and unit in the format" + + " \"<quantity> <unit>\". Eg. \"5.5 m\" or \"1ft 2in\""); } + return quantities.Aggregate((x, y) => x + y); + } - try - { - TorqueUnit unit = ParseUnit(unitString, formatProvider); - double value = double.Parse(valueString, formatProvider); + /// + /// Parse a string given a particular regular expression. + /// + /// Error parsing string. + private static List ParseWithRegex(string regexString, string str, IFormatProvider formatProvider = null) + { + var regex = new Regex(regexString); + MatchCollection matches = regex.Matches(str.Trim()); + var converted = new List(); - return From(value, unit); - } - catch (Exception e) + foreach (Match match in matches) { - var newEx = new UnitsNetException("Error parsing string.", e); - newEx.Data["input"] = str; - newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw newEx; + GroupCollection groups = match.Groups; + + var valueString = groups["value"].Value; + var unitString = groups["unit"].Value; + if (groups["invalid"].Value != "") + { + var newEx = new UnitsNetException("Invalid string detected: " + groups["invalid"].Value); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } + if (valueString == "" && unitString == "") continue; + + try + { + TorqueUnit unit = ParseUnit(unitString, formatProvider); + double value = double.Parse(valueString, formatProvider); + + converted.Add(From(value, unit)); + } + catch (Exception ex) + { + var newEx = new UnitsNetException("Error parsing string.", ex); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } } + return converted; } /// diff --git a/UnitsNet/GeneratedCode/UnitClasses/Volume.g.cs b/UnitsNet/GeneratedCode/UnitClasses/Volume.g.cs index 4827012d0a..593de21697 100644 --- a/UnitsNet/GeneratedCode/UnitClasses/Volume.g.cs +++ b/UnitsNet/GeneratedCode/UnitClasses/Volume.g.cs @@ -20,6 +20,7 @@ // THE SOFTWARE. using System; +using System.Collections.Generic; using System.Globalization; using System.Text.RegularExpressions; using System.Linq; @@ -612,17 +613,16 @@ public double As(VolumeUnit unit) #region Parsing /// - /// Parse a string of the format "<quantity> <unit>". + /// Parse a string with one or two quantities of the format "<quantity> <unit>". /// /// /// Length.Parse("5.5 m", new CultureInfo("en-US")); /// /// The value of 'str' cannot be null. /// - /// Expected 2 words. Input string needs to be in the format "<quantity> <unit - /// >". + /// Expected string to have one or two pairs of quantity and unit in the format + /// "<quantity> <unit>". Eg. "5.5 m" or "1ft 2in" /// - /// Error parsing string. public static Volume Parse(string str, IFormatProvider formatProvider = null) { if (str == null) throw new ArgumentNullException("str"); @@ -634,41 +634,70 @@ public static Volume Parse(string str, IFormatProvider formatProvider = null) var numRegex = string.Format(@"[\d., {0}{1}]*\d", // allows digits, dots, commas, and spaces in the quantity (must end in digit) numFormat.NumberGroupSeparator, // adds provided (or current) culture's group separator numFormat.NumberDecimalSeparator); // adds provided (or current) culture's decimal separator - var regexString = string.Format("(?[-+]?{0}{1}{2}{3}", - numRegex, // capture base (integral) Quantity value - @"(?:[eE][-+]?\d+)?)", // capture exponential (if any), end of Quantity capturing - @"\s?", // ignore whitespace (allows both "1kg", "1 kg") - @"(?\S+)"); // capture Unit (non-whitespace) input - - var regex = new Regex(regexString); - GroupCollection groups = regex.Match(str.Trim()).Groups; - - var valueString = groups["value"].Value; - var unitString = groups["unit"].Value; - - if (valueString == "" || unitString == "") + var exponentialRegex = @"(?:[eE][-+]?\d+)?)"; + var regexString = string.Format(@"(?:\s*(?[-+]?{0}{1}{2}{3})?{4}{5}", + numRegex, // capture base (integral) Quantity value + exponentialRegex, // capture exponential (if any), end of Quantity capturing + @"\s?", // ignore whitespace (allows both "1kg", "1 kg") + @"(?[^\s\d,]+)", // capture Unit (non-whitespace) input + @"(and)?,?", // allow "and" & "," separators between quantities + @"(?[a-z]*)?"); // capture invalid input + + var quantities = ParseWithRegex(regexString, str, formatProvider); + if (quantities.Count == 0) { - var ex = new ArgumentException( - "Expected valid quantity and unit. Input string needs to be in the format \" or \".", "str"); - ex.Data["input"] = str; - ex.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw ex; + throw new ArgumentException( + "Expected string to have at least one pair of quantity and unit in the format" + + " \"<quantity> <unit>\". Eg. \"5.5 m\" or \"1ft 2in\""); } + return quantities.Aggregate((x, y) => x + y); + } - try - { - VolumeUnit unit = ParseUnit(unitString, formatProvider); - double value = double.Parse(valueString, formatProvider); + /// + /// Parse a string given a particular regular expression. + /// + /// Error parsing string. + private static List ParseWithRegex(string regexString, string str, IFormatProvider formatProvider = null) + { + var regex = new Regex(regexString); + MatchCollection matches = regex.Matches(str.Trim()); + var converted = new List(); - return From(value, unit); - } - catch (Exception e) + foreach (Match match in matches) { - var newEx = new UnitsNetException("Error parsing string.", e); - newEx.Data["input"] = str; - newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw newEx; + GroupCollection groups = match.Groups; + + var valueString = groups["value"].Value; + var unitString = groups["unit"].Value; + if (groups["invalid"].Value != "") + { + var newEx = new UnitsNetException("Invalid string detected: " + groups["invalid"].Value); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } + if (valueString == "" && unitString == "") continue; + + try + { + VolumeUnit unit = ParseUnit(unitString, formatProvider); + double value = double.Parse(valueString, formatProvider); + + converted.Add(From(value, unit)); + } + catch (Exception ex) + { + var newEx = new UnitsNetException("Error parsing string.", ex); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } } + return converted; } /// diff --git a/UnitsNet/GeneratedCode/UnitSystem.Default.g.cs b/UnitsNet/GeneratedCode/UnitSystem.Default.g.cs index ab7567c128..63061ebc9d 100644 --- a/UnitsNet/GeneratedCode/UnitSystem.Default.g.cs +++ b/UnitsNet/GeneratedCode/UnitSystem.Default.g.cs @@ -678,13 +678,13 @@ private static readonly ReadOnlyCollection DefaultLocalization new CulturesForEnumValue((int) LengthUnit.Foot, new[] { - new AbbreviationsForCulture("en-US", "ft"), + new AbbreviationsForCulture("en-US", "ft", "\'"), new AbbreviationsForCulture("ru-RU", "фут"), }), new CulturesForEnumValue((int) LengthUnit.Inch, new[] { - new AbbreviationsForCulture("en-US", "in"), + new AbbreviationsForCulture("en-US", "in", "\""), new AbbreviationsForCulture("ru-RU", "дюйм"), }), new CulturesForEnumValue((int) LengthUnit.Kilometer, diff --git a/UnitsNet/Scripts/Include-GenerateUnitClassSourceCode.ps1 b/UnitsNet/Scripts/Include-GenerateUnitClassSourceCode.ps1 index b2346a374a..5417c4ddee 100644 --- a/UnitsNet/Scripts/Include-GenerateUnitClassSourceCode.ps1 +++ b/UnitsNet/Scripts/Include-GenerateUnitClassSourceCode.ps1 @@ -33,6 +33,7 @@ function GenerateUnitClassSourceCode($unitClass) // THE SOFTWARE. using System; +using System.Collections.Generic; using System.Globalization; using System.Text.RegularExpressions; using System.Linq; @@ -265,17 +266,16 @@ namespace UnitsNet #region Parsing /// - /// Parse a string of the format "<quantity> <unit>". + /// Parse a string with one or two quantities of the format "<quantity> <unit>". /// /// /// Length.Parse("5.5 m", new CultureInfo("en-US")); /// /// The value of 'str' cannot be null. /// - /// Expected 2 words. Input string needs to be in the format "<quantity> <unit - /// >". + /// Expected string to have one or two pairs of quantity and unit in the format + /// "<quantity> <unit>". Eg. "5.5 m" or "1ft 2in" /// - /// Error parsing string. public static $className Parse(string str, IFormatProvider formatProvider = null) { if (str == null) throw new ArgumentNullException("str"); @@ -287,41 +287,70 @@ namespace UnitsNet var numRegex = string.Format(@"[\d., {0}{1}]*\d", // allows digits, dots, commas, and spaces in the quantity (must end in digit) numFormat.NumberGroupSeparator, // adds provided (or current) culture's group separator numFormat.NumberDecimalSeparator); // adds provided (or current) culture's decimal separator - var regexString = string.Format("(?[-+]?{0}{1}{2}{3}", - numRegex, // capture base (integral) Quantity value - @"(?:[eE][-+]?\d+)?)", // capture exponential (if any), end of Quantity capturing - @"\s?", // ignore whitespace (allows both "1kg", "1 kg") - @"(?\S+)"); // capture Unit (non-whitespace) input - - var regex = new Regex(regexString); - GroupCollection groups = regex.Match(str.Trim()).Groups; - - var valueString = groups["value"].Value; - var unitString = groups["unit"].Value; - - if (valueString == "" || unitString == "") + var exponentialRegex = @"(?:[eE][-+]?\d+)?)"; + var regexString = string.Format(@"(?:\s*(?[-+]?{0}{1}{2}{3})?{4}{5}", + numRegex, // capture base (integral) Quantity value + exponentialRegex, // capture exponential (if any), end of Quantity capturing + @"\s?", // ignore whitespace (allows both "1kg", "1 kg") + @"(?[^\s\d,]+)", // capture Unit (non-whitespace) input + @"(and)?,?", // allow "and" & "," separators between quantities + @"(?[a-z]*)?"); // capture invalid input + + var quantities = ParseWithRegex(regexString, str, formatProvider); + if (quantities.Count == 0) { - var ex = new ArgumentException( - "Expected valid quantity and unit. Input string needs to be in the format \" or \".", "str"); - ex.Data["input"] = str; - ex.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw ex; + throw new ArgumentException( + "Expected string to have at least one pair of quantity and unit in the format" + + " \"<quantity> <unit>\". Eg. \"5.5 m\" or \"1ft 2in\""); } + return quantities.Aggregate((x, y) => x + y); + } - try - { - $unitEnumName unit = ParseUnit(unitString, formatProvider); - double value = double.Parse(valueString, formatProvider); + /// + /// Parse a string given a particular regular expression. + /// + /// Error parsing string. + private static List<$className> ParseWithRegex(string regexString, string str, IFormatProvider formatProvider = null) + { + var regex = new Regex(regexString); + MatchCollection matches = regex.Matches(str.Trim()); + var converted = new List<$className>(); - return From(value, unit); - } - catch (Exception e) + foreach (Match match in matches) { - var newEx = new UnitsNetException("Error parsing string.", e); - newEx.Data["input"] = str; - newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); - throw newEx; + GroupCollection groups = match.Groups; + + var valueString = groups["value"].Value; + var unitString = groups["unit"].Value; + if (groups["invalid"].Value != "") + { + var newEx = new UnitsNetException("Invalid string detected: " + groups["invalid"].Value); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } + if (valueString == "" && unitString == "") continue; + + try + { + $unitEnumName unit = ParseUnit(unitString, formatProvider); + double value = double.Parse(valueString, formatProvider); + + converted.Add(From(value, unit)); + } + catch (Exception ex) + { + var newEx = new UnitsNetException("Error parsing string.", ex); + newEx.Data["input"] = str; + newEx.Data["matched value"] = valueString; + newEx.Data["matched unit"] = unitString; + newEx.Data["formatprovider"] = formatProvider == null ? null : formatProvider.ToString(); + throw newEx; + } } + return converted; } /// diff --git a/UnitsNet/Scripts/UnitDefinitions/Length.json b/UnitsNet/Scripts/UnitDefinitions/Length.json index 72e66a4111..9ad60c4a0b 100644 --- a/UnitsNet/Scripts/UnitDefinitions/Length.json +++ b/UnitsNet/Scripts/UnitDefinitions/Length.json @@ -61,7 +61,7 @@ "Localization": [ { "Culture": "en-US", - "Abbreviations": ["ft"] + "Abbreviations": ["ft","\\\'"] }, { "Culture": "ru-RU", @@ -77,7 +77,7 @@ "Localization": [ { "Culture": "en-US", - "Abbreviations": ["in"] + "Abbreviations": ["in","\\\""] }, { "Culture": "ru-RU",