Skip to content

Feature/improve parsing #81

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Jun 26, 2015
20 changes: 17 additions & 3 deletions UnitsNet.Tests/CustomCode/ParseTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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)
{
Expand All @@ -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;
}

/// <exception cref="UnitsNetException">Error parsing string.</exception>
[TestCase("5.5 m", Result = 5.5)]
[TestCase("500 005 m", Result = 500005)]
Expand Down
95 changes: 62 additions & 33 deletions UnitsNet/GeneratedCode/UnitClasses/Acceleration.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
// THE SOFTWARE.

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text.RegularExpressions;
using System.Linq;
Expand Down Expand Up @@ -232,17 +233,16 @@ public double As(AccelerationUnit unit)
#region Parsing

/// <summary>
/// Parse a string of the format "&lt;quantity&gt; &lt;unit&gt;".
/// Parse a string with one or two quantities of the format "&lt;quantity&gt; &lt;unit&gt;".
/// </summary>
/// <example>
/// Length.Parse("5.5 m", new CultureInfo("en-US"));
/// </example>
/// <exception cref="ArgumentNullException">The value of 'str' cannot be null. </exception>
/// <exception cref="ArgumentException">
/// Expected 2 words. Input string needs to be in the format "&lt;quantity&gt; &lt;unit
/// &gt;".
/// Expected string to have one or two pairs of quantity and unit in the format
/// "&lt;quantity&gt; &lt;unit&gt;". Eg. "5.5 m" or "1ft 2in"
/// </exception>
/// <exception cref="UnitsNetException">Error parsing string.</exception>
public static Acceleration Parse(string str, IFormatProvider formatProvider = null)
{
if (str == null) throw new ArgumentNullException("str");
Expand All @@ -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("(?<value>[-+]?{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")
@"(?<unit>\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*(?<value>[-+]?{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")
@"(?<unit>[^\s\d,]+)", // capture Unit (non-whitespace) input
@"(and)?,?", // allow "and" & "," separators between quantities
@"(?<invalid>[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 \"<quantity><unit> or <quantity> <unit>\".", "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"
+ " \"&lt;quantity&gt; &lt;unit&gt;\". 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);
/// <summary>
/// Parse a string given a particular regular expression.
/// </summary>
/// <exception cref="UnitsNetException">Error parsing string.</exception>
private static List<Acceleration> ParseWithRegex(string regexString, string str, IFormatProvider formatProvider = null)
{
var regex = new Regex(regexString);
MatchCollection matches = regex.Matches(str.Trim());
var converted = new List<Acceleration>();

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;
}

/// <summary>
Expand Down
95 changes: 62 additions & 33 deletions UnitsNet/GeneratedCode/UnitClasses/AmplitudeRatio.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
// THE SOFTWARE.

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text.RegularExpressions;
using System.Linq;
Expand Down Expand Up @@ -280,17 +281,16 @@ public double As(AmplitudeRatioUnit unit)
#region Parsing

/// <summary>
/// Parse a string of the format "&lt;quantity&gt; &lt;unit&gt;".
/// Parse a string with one or two quantities of the format "&lt;quantity&gt; &lt;unit&gt;".
/// </summary>
/// <example>
/// Length.Parse("5.5 m", new CultureInfo("en-US"));
/// </example>
/// <exception cref="ArgumentNullException">The value of 'str' cannot be null. </exception>
/// <exception cref="ArgumentException">
/// Expected 2 words. Input string needs to be in the format "&lt;quantity&gt; &lt;unit
/// &gt;".
/// Expected string to have one or two pairs of quantity and unit in the format
/// "&lt;quantity&gt; &lt;unit&gt;". Eg. "5.5 m" or "1ft 2in"
/// </exception>
/// <exception cref="UnitsNetException">Error parsing string.</exception>
public static AmplitudeRatio Parse(string str, IFormatProvider formatProvider = null)
{
if (str == null) throw new ArgumentNullException("str");
Expand All @@ -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("(?<value>[-+]?{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")
@"(?<unit>\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*(?<value>[-+]?{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")
@"(?<unit>[^\s\d,]+)", // capture Unit (non-whitespace) input
@"(and)?,?", // allow "and" & "," separators between quantities
@"(?<invalid>[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 \"<quantity><unit> or <quantity> <unit>\".", "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"
+ " \"&lt;quantity&gt; &lt;unit&gt;\". 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);
/// <summary>
/// Parse a string given a particular regular expression.
/// </summary>
/// <exception cref="UnitsNetException">Error parsing string.</exception>
private static List<AmplitudeRatio> ParseWithRegex(string regexString, string str, IFormatProvider formatProvider = null)
{
var regex = new Regex(regexString);
MatchCollection matches = regex.Matches(str.Trim());
var converted = new List<AmplitudeRatio>();

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;
}

/// <summary>
Expand Down
Loading