Skip to content

Commit 093074e

Browse files
committed
QuantityParser: Make parsing more strict
- Only parse the format "{value} {unit abbreviation}" where the spacing is optional - No longer support parsing multiple quantities like "1 ft 2 in", use Length.ParseFeetInches() for that - Move Trim() to inner-most level
1 parent accef1c commit 093074e

File tree

8 files changed

+143
-184
lines changed

8 files changed

+143
-184
lines changed

UnitsNet.Tests/CustomCode/ParseTests.cs

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ public class ParseTests
3838
{
3939
[Theory]
4040
[InlineData("1km", 1000)]
41+
[InlineData(" 1km ", 1000)] // Check that it also trims string
4142
[InlineData("1 km", 1000)]
4243
[InlineData("1e-3 km", 1)]
4344
[InlineData("5.5 m", 5.5)]
@@ -51,11 +52,11 @@ public void ParseLengthToMetersUsEnglish(string s, double expected)
5152

5253
[Theory]
5354
[InlineData(null, typeof(ArgumentNullException))] // Can't parse null.
54-
[InlineData("1", typeof(ArgumentException))] // No unit abbreviation.
55-
[InlineData("km", typeof(UnitsNetException))] // No value, wrong measurement type.
56-
[InlineData("1 kg", typeof(UnitsNetException))] // Wrong measurement type.
57-
[InlineData("1ft monkey 1in", typeof(UnitsNetException))] // Invalid separator between two valid measurements.
58-
[InlineData("1ft 1invalid", typeof(UnitsNetException))] // Valid
55+
[InlineData("1", typeof(FormatException))] // No unit abbreviation.
56+
[InlineData("km", typeof(FormatException))] // No value, wrong measurement type.
57+
[InlineData("1 kg", typeof(FormatException))] // Wrong measurement type.
58+
[InlineData("1ft monkey 1in", typeof(FormatException))] // Invalid separator between two valid measurements.
59+
[InlineData("1ft 1invalid", typeof(FormatException))] // Valid
5960
public void ParseLength_InvalidString_USEnglish_ThrowsException(string s, Type expectedExceptionType)
6061
{
6162
var usEnglish = new CultureInfo("en-US");
@@ -71,20 +72,24 @@ public void ParseWithCultureUsingSpaceAsThousandSeparators(string s, double expe
7172
{
7273
var numberFormat = (NumberFormatInfo) CultureInfo.InvariantCulture.NumberFormat.Clone();
7374
numberFormat.NumberGroupSeparator = " ";
75+
numberFormat.CurrencyGroupSeparator = " ";
7476
numberFormat.NumberDecimalSeparator = ".";
77+
numberFormat.CurrencyDecimalSeparator = ".";
7578

7679
double actual = Length.Parse(s, numberFormat).Meters;
7780
Assert.Equal(expected, actual);
7881
}
7982

8083
[Theory]
81-
[InlineData("500.005.050,001 m", typeof(UnitsNetException))]
84+
[InlineData("500.005.050,001 m", typeof(FormatException))]
8285
// quantity doesn't match number format
8386
public void ParseWithCultureUsingSpaceAsThousandSeparators_ThrowsExceptionOnInvalidString(string s, Type expectedExceptionType)
8487
{
8588
var numberFormat = (NumberFormatInfo) CultureInfo.InvariantCulture.NumberFormat.Clone();
8689
numberFormat.NumberGroupSeparator = " ";
90+
numberFormat.CurrencyGroupSeparator = " ";
8791
numberFormat.NumberDecimalSeparator = ".";
92+
numberFormat.CurrencyDecimalSeparator = ".";
8893

8994
Assert.Throws(expectedExceptionType, () => Length.Parse(s, numberFormat));
9095
}
@@ -98,7 +103,9 @@ public void ParseWithCultureUsingDotAsThousandSeparators(string s, double expect
98103
{
99104
var numberFormat = (NumberFormatInfo) CultureInfo.InvariantCulture.NumberFormat.Clone();
100105
numberFormat.NumberGroupSeparator = ".";
106+
numberFormat.CurrencyGroupSeparator = ".";
101107
numberFormat.NumberDecimalSeparator = ",";
108+
numberFormat.CurrencyDecimalSeparator = ",";
102109

103110
double actual = Length.Parse(s, numberFormat).Meters;
104111
Assert.Equal(expected, actual);
@@ -112,12 +119,14 @@ public void ParseMultiWordAbbreviations()
112119
}
113120

114121
[Theory]
115-
[InlineData("500 005 m", typeof(UnitsNetException))] // Quantity doesn't match number format.
122+
[InlineData("500 005 m", typeof(FormatException))] // Quantity doesn't match number format.
116123
public void ParseWithCultureUsingDotAsThousandSeparators_ThrowsExceptionOnInvalidString(string s, Type expectedExceptionType)
117124
{
118125
var numberFormat = (NumberFormatInfo) CultureInfo.InvariantCulture.NumberFormat.Clone();
119126
numberFormat.NumberGroupSeparator = ".";
127+
numberFormat.CurrencyGroupSeparator = ".";
120128
numberFormat.NumberDecimalSeparator = ",";
129+
numberFormat.CurrencyDecimalSeparator = ",";
121130

122131
Assert.Throws(expectedExceptionType, () => Length.Parse(s, numberFormat));
123132
}
@@ -142,7 +151,7 @@ public void ParseLengthUnitUsEnglish_ThrowsExceptionOnInvalidString(string s, Ty
142151

143152
[Theory]
144153
[InlineData("1 m", true)]
145-
[InlineData("1 m 50 cm", true)]
154+
[InlineData("1 m 50 cm", false)]
146155
[InlineData("2 kg", false)]
147156
[InlineData(null, false)]
148157
[InlineData("foo", false)]

UnitsNet.Tests/UnitParserTests.cs

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public void Parse_UnknownAbbreviationThrowsUnitNotFoundException()
4848
}
4949

5050
[Fact]
51-
public void ShouldUseCorrectMicroSign()
51+
public void ParseUnit_ShouldUseCorrectMicroSign()
5252
{
5353
// "\u00b5" = Micro sign
5454
Assert.Equal(AccelerationUnit.MicrometerPerSecondSquared, Acceleration.ParseUnit("\u00b5m/s²"));
@@ -118,13 +118,5 @@ public void Parse_AmbiguousUnitsThrowsException()
118118
Assert.Equal("Cannot parse \"pt\" since it could be either of these: DtpPoint, PrinterPoint", exception1.Message);
119119
Assert.Equal("Cannot parse \"pt\" since it could be either of these: DtpPoint, PrinterPoint", exception2.Message);
120120
}
121-
122-
[Fact]
123-
public void Parse_UnambiguousUnitsDoesNotThrow()
124-
{
125-
var unit = Volume.Parse("1 l");
126-
127-
Assert.Equal(Volume.FromLiters(1), unit);
128-
}
129121
}
130122
}

UnitsNet/CustomCode/Quantities/Length.extra.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,9 @@ public static bool TryParseFeetInches(string str, out Length result, IFormatProv
107107
if (TryParseInternal(str, formatProvider, out result))
108108
return true;
109109

110-
string footRegex = QuantityParser.CreateRegexPatternForUnit(LengthUnit.Foot, formatProvider, matchEntireString: false);
111-
string inchRegex = QuantityParser.CreateRegexPatternForUnit(LengthUnit.Inch, formatProvider, matchEntireString: false);
110+
var quantityParser = QuantityParser.Default;
111+
string footRegex = quantityParser.CreateRegexPatternForUnit(LengthUnit.Foot, formatProvider, matchEntireString: false);
112+
string inchRegex = quantityParser.CreateRegexPatternForUnit(LengthUnit.Inch, formatProvider, matchEntireString: false);
112113

113114
// Match entire string exactly
114115
string pattern = $@"^(?<feet>{footRegex})\s?(?<inches>{inchRegex})$";

0 commit comments

Comments
 (0)