解析所有可能类型的不同架构维度输入

时间:2014-04-01 18:54:19

标签: c# regex string linq string-parsing

我正在为我们公司的产品编写一个库,它将采用我们的用户已经熟悉的任何类型的架构维度作为从字符串转换为double的函数的输入。以下是我们希望有效的输入类型列表。

Input |意义| Output(英寸由双人代表)


12.5' | 12英尺和6英寸| 150.0

11" | 11英寸| 11.0

3/16" |英寸的3个十六分之一| 0.1875


在英尺和英寸以及英寸和十六分之间可以使用或不使用空格

11' 11" | 11英尺和11英寸| 143.0

11'11" | 11英尺和11英寸| 143.0


在英尺和英寸之间或英寸到十六分之间或两者之间可以使用或不使用破折号

12'-11" | 12英尺和11英寸| 155.0

12' 11 3/16" | 12英尺和11英寸和3个十六分之二| 155.1875

12' 11-1/2" | 12英尺和11英寸和8个十六分之二| 155.5


在英尺和英寸以及英寸和十六分之间可以使用任意数量的空格

12' 11 1/2" | 12英尺和11英寸和8个十六分之二| 155.5


还提供了另一种更简单的格式

121103 | 12英尺和11英寸和3个十六分之二| 155.1875


每种格式都可以使用否定

-121103 | 12英尺和11英寸和3个十六分之二| -155.1875

-11'11" | 11英尺和11英寸| -143.0

我们目前正在使用一组非常复杂的分支逻辑来尝试确定输入尝试模拟的格式......并且它并不适用于所有情况。

我们可以使用LINQ和正则表达式以及巫术的某种可能组合来确定如何解析字符串吗?

另请注意,我们确实希望避免在表单上提供一个简单的组合框来从中选择输入格式类型。

2 个答案:

答案 0 :(得分:3)

此功能适用于您的输入值示例。

public static Double Conv(String inp)
{
    String expr= "((?<feet>\\d+)(?<inch>\\d{2})(?<sixt>\\d{2}))|((?<feet>[\\d.]+)')?[\\s-]*((?<inch>\\d+)?[\\s-]*((?<numer>\\d+)/(?<denom>\\d+))?\")?";
    Match m = new Regex(expr).Match(inp);
    Double feet = m.Groups["feet"].Success ? Convert.ToDouble(m.Groups["feet"].Value) : 0;
    Int32  inch = m.Groups["inch"].Success ? Convert.ToInt32(m.Groups["inch"].Value) : 0;
    Int32  sixt = m.Groups["sixt"].Success ? Convert.ToInt32(m.Groups["sixt"].Value) : 0;
    Int32 numer = m.Groups["numer"].Success ? Convert.ToInt32(m.Groups["numer"].Value) : 0;
    Int32 denom = m.Groups["denom"].Success ? Convert.ToInt32(m.Groups["denom"].Value) : 1;
    return feet*12+inch+sixt/16.0+numer/Convert.ToDouble(denom);
}    

请注意,除了您提供的有效输入外,我还没有做过测试其他输入的任何工作。你可能想要例如检查至少某些捕获组的成功,或者可以将验证作为单独的步骤进行。这段代码是在解析时完成的。

修改

这是一个更强大的版本:

public static Double Conv(String inp)
{
    String expr= "^\\s*(?<minus>-)?\\s*(((?<feet>\\d+)(?<inch>\\d{2})(?<sixt>\\d{2}))|((?<feet>[\\d.]+)')?[\\s-]*((?<inch>\\d+)?[\\s-]*((?<numer>\\d+)/(?<denom>\\d+))?\")?)\\s*$";
    Match m = new Regex(expr).Match(inp);
    if(!m.Success || inp.Trim()=="")
    {
        // maybe throw exception or set/return some failure indicator
        return 0; // here using return value zero as failure indicator
    }
    Int32 sign  = m.Groups["minus"].Success ? -1 : 1;
    Double feet = m.Groups["feet"].Success ? Convert.ToDouble(m.Groups["feet"].Value) : 0;
    Int32  inch = m.Groups["inch"].Success ? Convert.ToInt32(m.Groups["inch"].Value) : 0;
    Int32  sixt = m.Groups["sixt"].Success ? Convert.ToInt32(m.Groups["sixt"].Value) : 0;
    Int32 numer = m.Groups["numer"].Success ? Convert.ToInt32(m.Groups["numer"].Value) : 0;
    Int32 denom = m.Groups["denom"].Success ? Convert.ToInt32(m.Groups["denom"].Value) : 1;
    return sign*(feet*12+inch+sixt/16.0+numer/Convert.ToDouble(denom));
}

对于空字符串,以及除了示例允许的额外字符的字符串,它都会失败。 五个或更多数字被视为更简单的格式。

更改是开始和结束锚点,允许前导和尾随空格,以及if语句中仅用于emtpy / whitespace的字符串的特殊情况检查。

免责声明:显然没有针对所有可能的非法输入进行测试,而且我不是c#程序员: - )

答案 1 :(得分:1)

这可能会将您的复杂性从分支逻辑转移到正则表达式逻辑:

/(?<special>(?<feet>\d+)(?<inch>\d{2})(?<sixt>\d{2}))|((?<feet>[\d.]+)')?[\s-]*((?<inch>\d+)?[\s-]*((?<numer>\d+)\/(?<denom>\d+))?")?/

如果组特殊匹配,则您具有特殊语法,输出为英尺 * 12 + 英寸 + _ sixt_ / 16,使用{关于群组的{1}}。 如果没有,如果输入,您将有一个或多个组英尺英寸数字 denom 已验证。对于其余部分使用ToDecimal,对其余部分使用ToDecimal,并确保在分数中检查除以零。

概念验证演示代码(ruby):

ToDouble

输出:

[
  ["12.5' "," 12 Feet and six inches "," 150.0"],
  ["11\"  "," 11 Inches "," 11.0"],
  ["3/16\" "," 3 sixteenths of an Inch "," 0.1875"],
  ["11' 11\" "," 11 Feet and 11 Inches "," 143.0"],
  ["11'11\" "," 11 Feet and 11 Inches "," 143.0"],
  ["12'-11\" "," 12 Feet and 11 Inches "," 155.0"],
  ["12' 11 3/16\" "," 12 Feet and 11 Inches and 3 sixteenths "," 155.1875"],
  ["12' 11-1/2\" "," 12 Feet and 11 Inches and 8 sixteenths "," 155.5"],
  ["12'   11     1/2\" "," 12 Feet and 11 Inches and 8 sixteenths "," 155.5"],
  ["121103 "," 12 Feet and 11 Inches and 3 sixteenths "," 155.1875"],
  ["", "empty string", "0"],
].each{|i,d,o|
  m = /(?<special>(?<feet>\d+)(?<inch>\d{2})(?<sixt>\d{2}))|((?<feet>[\d.]+)')?[\s-]*((?<inch>\d+)?[\s-]*((?<numer>\d+)\/(?<denom>\d+))?")?/.match(i)
  #puts "#{(1..8).map{|n|"%15s"%m[n].inspect}.join}"
  puts "#{"%20s"%i}   #{"%10s"%o}   #{m[:special] ? m[:feet].to_i*12+m[:inch].to_i+m[:sixt].to_i/16.0 : m[:feet].to_f*12+m[:inch].to_i+(m[:numer].to_i.to_f/(m[:denom]||1).to_i)} "
}

请注意,除了您提供的有效输入外,我还没有做过测试其他输入的任何工作。你可能想要例如检查至少一些捕获组中的非零值,或者可以作为单独的步骤进行验证。这段代码是在解析时完成的。