C#距离(英里/公里/等)字符串解析库

C#距离(英里/公里/等)字符串解析库,c#,parsing,distance,C#,Parsing,Distance,有没有任何C#库提供与谷歌相同的功能?当你输入一个查询,比如“13英里743码米”,它将返回“21600米”(例如) 我想做的是给一个函数字符串部分13英里743码,它以米为单位吐出一个int/double。它需要能够处理所有单位输入类型(公里/米/弗隆/英里/码/…),但输出必须以米为单位 编写自己的库并不难,但只要有一个经过测试的库就好了。一种方法是向谷歌发出请求,然后解析返回的html 更新:这将非常低效,但他们已经为您解决了繁重的工作。要实现这一点,您必须制作一个英语(例如)语言解析器来

有没有任何C#库提供与谷歌相同的功能?当你输入一个查询,比如“13英里743码米”,它将返回“21600米”(例如)

我想做的是给一个函数字符串部分
13英里743码
,它以米为单位吐出一个int/double。它需要能够处理所有单位输入类型(公里/米/弗隆/英里/码/…),但输出必须以米为单位


编写自己的库并不难,但只要有一个经过测试的库就好了。

一种方法是向谷歌发出请求,然后解析返回的html

更新:这将非常低效,但他们已经为您解决了繁重的工作。要实现这一点,您必须制作一个英语(例如)语言解析器来获取输入,去掉无意义的单词/符号(如and和逗号),找到值(13和743),找到单位(英里、码和米),找到运算符(in或To)。在那之后,你必须确保它在句法上有意义。您还必须保留一个转换表(不难)

这当然是可能的,但这是一系列的工作,我不确定是否已经存在(除了谷歌)。有那么多角落的案子你得担心。建立一个图书馆来做这项工作将是一个有趣的练习,但很难抓住所有的案例


更简单的解决方案是为他们提供离散的控件,将语言解析带出这里是一个单元转换库。没有您想要的所有测量单位(弗隆!?),但看起来有最多:

未找到任何与字符串分析有关的内容。老实说,这似乎是一种容易出错的获取输入的方法。考虑:

  • 13英里和743码(米)
  • 13英里743码到米
  • 13英里743码 仪表
所有这些都意味着同样的事情,即使你给出了痛苦的关于如何写出字符串的具体说明,他们也可能会做对他们有意义的事情


如果你想了解人们想说的话,那么你最好选择谷歌。否则,您可能会尝试使用特定的输入。

我找不到任何答案,因此我构建了自己的:)这里唯一真正的“魔力”是正则表达式,它可以从原始字符串中获取值/单位组。从这里开始,它是简单的分数/数字解析,然后计算出每个单位代表多少米。我根本没有测试过这么多,所以如果您发现改进或bug,请告诉我(下面的代码在无法处理情况时应该抛出异常)

它不会处理愚蠢的用户输入,但如果每个部分的格式是“[数字][单位]”,我认为它应该可以正常工作。如果输入不符合(例如,
12/32/43
1.43.3.2.44
作为一个值),那么您可以假设的情况不多。我认为它也会处理句子中额外的绒毛,例如
1公里和10英里
(将去掉
)。如果你知道一个完整的单位列表,我还没有尽可能地增加每一个单位&我很想知道

这里有几个测试

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 + ")");
}
private static Dictionary\u distance lookup=new Dictionary()
{
{“英里”,1609.344},
{“furlong”,201.168},
{“码”,0.9144},
{“英寸”,0.0254},
{“英尺”,0.3048},
{“英尺”,0.3048},
{“公里”,1000},
{“公里”,1000},
{“米”,1},
{“米”,1},
{“厘米”,0.01},
{“厘米”,0.01},
{“毫米”,0.001},
{“毫米”,0.001},
};
专用静态双小数(字符串小数)
{
双值=0;
if(分数.Contains('/'))
{
//如果这个值包含/,我们需要计算出分数
字符串[]splitVal=fraction.Split('/');
如果(splitVal.Length!=2)
{
拧紧(分数,“拆分值长度”);
}
//把分数变成小数
value=double.Parse(splitVal[0])/double.Parse(splitVal[1]);
}
其他的
{
//否则,这是一个简单的解析
value=double.Parse(分数);
}
返回值;
}
公共静态双提取距离(字符串距离字符串)
{
双距离米=0;
/*每个单元类型都有一个匹配项。
*例如,字符串“1 1/16英里3/4码”将有两个匹配项
*“1 1/16英里”,“3/4码”。每场比赛将有4码
*组总数,第3组为原始值,第4组为原始值
*原始单位
*/
var matches=Regex.matches(distAsString,@”([\d]+[\d\s\,/]*)\s([A-Za-z]+[^\s\d]));
foreach(匹配中的匹配)
{
//如果组!=4出现问题,我们需要重新考虑正则表达式
如果(match.Groups.Count!=4)
{
搞砸了(distAsString,“match.Groups.Count”);
}
字符串valueRaw=match.Groups[2]。值;
字符串unitRaw=match.Groups[3]。值;
//首先得到价值
双值=0;
if(valueRaw.Contains(“”))
{
//如果这个值包含/,我们需要计算出分数
字符串[]splitVal=valueRaw.Split(“”);
如果(splitVal.Length!=2)
{
拧紧(分离管柱,“分离阀长度”);
}
//把分数变成小数
值=ConvertFraction(splitVal[0])+ConvertFraction(splitVal[1]);
}
其他的
{
值=换算分数(valueRaw);
}
//现在根据单位类型进行计算
//清理原始单元字符串
unitRaw=unitRaw.ToLower().Trim().TrimEnd('s');
if(!\u distance lookup.ContainsKey(unitRaw))
{
拧紧(distAsString,“unitRaw”);
}
distanceInMeters+=值*_DistanceLookup[unitRaw];
}
返回距离(米);
}
私人静空隙堵塞