【问题标题】:C# distance (mile/km/etc.) string parsing libraryC#距离(英里/公里/等)字符串解析库
【发布时间】:2010-11-10 00:08:52
【问题描述】:

是否有任何 C# 库提供与谷歌相同的功能,当您输入“13 英里 743 码以米为单位”之类的查询时,它将返回“21 600 米”(例如)。

我想要做的是给一个函数字符串部分13 miles 743 yards,然后它会返回一个以米为单位的给定距离的int/double。它需要能够处理所有单位输入类型(公里/米/弗隆/英里/码/...),但输出只能以米为单位。

编写自己的库并不难,但如果准备好一个经过测试的库就好了。

【问题讨论】:

    标签: c# parsing distance


    【解决方案1】:

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

    它不会处理愚蠢的用户输入,但如果每个部分的格式是“[number] [unit]”,我认为它应该可以正常工作。无论如何,如果输入不符合要求(例如,12/32/431.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 样式格式

    【讨论】:

    • 请注意,我在过去一个小时内才刚刚完成编码。在接下来的几天里,我将对其进行更彻底的测试,但已经将它放在这里,以防其他人发现我没有发现的错误。如果发现任何错误,我将更新此答案。
    【解决方案2】:

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

    更新:这确实效率低下,但他们已经为您完成了艰苦的工作。要完成这项工作,您必须制作一个英语(例如)语言解析器来获取输入,去除无意义的单词/符号(如 and 和逗号),找到值(13和 743),查找单位(英里、码和米),查找运算符(in 或 to)。之后,您必须确保它具有语法意义。您还必须保留转换表(不难)。

    这绝对是可能的,但这是一堆工作,我不确定是否已经存在(除了谷歌)。您需要担心很多极端情况。建立一个图书馆来完成这项工作将是一个有趣的练习,但很难捕捉到所有情况。

    更简单的解决方案是为他们提供离散控件以进行语言解析

    【讨论】:

    • 我不会说每次我想解析距离时都调用在线服务是“聪明的”。特别是如果你想离线解析距离,这是完全合理的。
    • 好吧,这不是很“聪明”,但它确实有效。他们已经处理好了语言解析
    • 它仍然非常低效,更不用说对谷歌/网络/延迟的依赖了。这就像每次您想知道一天有多少小时时查询在线服务一样。完全矫枉过正。
    • 根据我的评论@Abe,我认为您使解决方案过于复杂。我应该说,但输入只有英文。数字格式将始终具有 , = 千位分隔符(如果存在). = 小数点(如果存在)。没有操作员,我只需要输出到仪表。甚至谷歌也无法解析错误的用户输入,这只是生活中的事实:)
    【解决方案3】:

    这是一个单位转换库。没有您想要的所有测量单位(弗隆!?),但看起来最多:

    http://www.codeproject.com/KB/library/Measurement_Conversion.aspx

    没有发现任何与字符串解析有关的东西。老实说,这似乎是一种容易出错的获取输入的方式。考虑:

    • 13 英里 743 码(米)
    • 13 英里 743 码
    • 13 英里 743 码

    所有的意思都是一样的,即使你给出了关于如何写出他们的字符串的痛苦的具体说明,他们可能会做对他们有意义的事情......

    如果您想深入了解人们想说的话,那么使用 Google 可能会更好。否则,您可以尝试输入特定的输入。

    【讨论】:

    • 我认为您使我的问题过于复杂。输出只需为米,因此字符串的to metersin meters 部分不存在。剥离and 绒毛也是自然而然的事情。对于输入我别无选择,我只需要将这些半格式良好的字符串解析为距离。如果我有选择,我不会问这个问题:) 解决问题的每一种方法都会有错误,你能做的只有这么多。如果字符串格式不正确,那只是运气不好,解析会失败。
    猜你喜欢
    • 2011-09-10
    • 2021-12-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-12
    相关资源
    最近更新 更多