我找不到任何答案,所以我建立了自己的 :) 这里唯一真正的“魔法”是正则表达式,用于从原始字符串中获取值/单位组。从那里开始进行简单的分数/数字解析,然后计算出每个单位代表多少米。我根本没有测试过这么多,所以如果你发现改进或错误,请告诉我(下面的代码在无法处理情况时应该抛出异常)。
它不会处理愚蠢的用户输入,但如果每个部分的格式是“[number] [unit]”,我认为它应该可以正常工作。无论如何,如果输入不符合要求(例如,12/32/43 或 1.43.3.2.44 作为值),您就无法假设太多。我认为它也会处理句子中的多余绒毛,例如1 kilometer and 10 miles(将去掉and)。我还没有添加所有可能的单位,如果您知道完整的单位列表以及米等价物,我很想知道。
这里有几个测试,
var a = ExtractDistance("1 1/16 Miles 3/4 yards");
var b = ExtractDistance("02234890234.853 meters");
var c = ExtractDistance("1.8 miles 3.2 furlong");
var d = ExtractDistance("1 kilometer");
var e = ExtractDistance("1/16 Miles");
这是我的代码:
private static Dictionary<string, double> _DistanceLookup = new Dictionary<string, double>()
{
{"mile", 1609.344},
{"furlong", 201.168},
{"yard", 0.9144},
{"inch", 0.0254},
{"foot", 0.3048},
{"feet", 0.3048},
{"kilometer", 1000},
{"kilometre", 1000},
{"metre", 1},
{"meter", 1},
{"centimeter", 0.01},
{"centimetre", 0.01},
{"millimeter", 0.001},
{"millimetre", 0.001},
};
private static double ConvertFraction(string fraction)
{
double value = 0;
if (fraction.Contains('/'))
{
// If the value contains /, we need to work out the fraction
string[] splitVal = fraction.Split('/');
if (splitVal.Length != 2)
{
ScrewUp(fraction, "splitVal.Length");
}
// Turn the fraction into decimal
value = double.Parse(splitVal[0]) / double.Parse(splitVal[1]);
}
else
{
// Otherwise it's a simple parse
value = double.Parse(fraction);
}
return value;
}
public static double ExtractDistance(string distAsString)
{
double distanceInMeters = 0;
/* This will have a match per unit type.
* e.g., the string "1 1/16 Miles 3/4 Yards" would have 2 matches
* being "1 1/16 Miles", "3/4 Yards". Each match will then have 4
* groups in total, with group 3 being the raw value and 4 being the
* raw unit
*/
var matches = Regex.Matches(distAsString, @"(([\d]+[\d\s\.,/]*)\s([A-Za-z]+[^\s\d]))");
foreach (Match match in matches)
{
// If groups != 4 something went wrong, we need to rethink our regex
if (match.Groups.Count != 4)
{
ScrewUp(distAsString, "match.Groups.Count");
}
string valueRaw = match.Groups[2].Value;
string unitRaw = match.Groups[3].Value;
// Firstly get the value
double value = 0;
if (valueRaw.Contains(' '))
{
// If the value contains /, we need to work out the fraction
string[] splitVal = valueRaw.Split(' ');
if (splitVal.Length != 2)
{
ScrewUp(distAsString, "splitVal.Length");
}
// Turn the fraction into decimal
value = ConvertFraction(splitVal[0]) + ConvertFraction(splitVal[1]);
}
else
{
value = ConvertFraction(valueRaw);
}
// Now work out based on the unit type
// Clean up the raw unit string
unitRaw = unitRaw.ToLower().Trim().TrimEnd('s');
if (!_DistanceLookup.ContainsKey(unitRaw))
{
ScrewUp(distAsString, "unitRaw");
}
distanceInMeters += value * _DistanceLookup[unitRaw];
}
return distanceInMeters;
}
private static void ScrewUp(string val, string prop)
{
throw new ArgumentException("Extract distance screwed up on string [" + val + "] (bad " + prop + ")");
}
享受吧!我希望有人发现这很有用。请留下cmets/建议。
编辑:将, 添加到正则表达式字符串以处理1,300 meters 样式格式