解析所有可能类型的不同架构尺寸输入

11

我正在为我们公司的产品编写一个库,可以将用户已经熟悉的任何类型的建筑尺寸作为输入,通过一个函数将其从字符串转换为双精度浮点数。以下是我们希望有效的输入类型列表。

输入| 含义 | 输出(以英寸表示的双精度浮点数)


12.5' | 12 英尺 6 英寸 | 150.0

11" | 11 英寸 | 11.0

3/16" | 3/16 英寸 | 0.1875


英尺和英寸之间,英寸和1/16 英寸之间可使用空格或不使用空格

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

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


英尺和英寸之间或英寸和1/16 英寸之间或两者之间均可使用破折号或不使用破折号

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

12' 11 3/16" | 12 英尺 11 英寸 3/16 | 155.1875

12' 11-1/2" | 12 英尺 11 英寸 8/16 | 155.5


英尺和英寸之间,英寸和1/16 英寸之间可使用任意数量的空格

12' 11 1/2" | 12 英尺 11 英寸 8/16 | 155.5


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

121103 | 12英尺11英寸3/16 | 155.1875


每种格式中也可能包含负数

-121103 | 12英尺11英寸3/16 | -155.1875

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

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

是否有一些LINQ和正则表达式以及巫术的可能性,可以用来确定如何解析字符串?

还请注意,我们真的想避免在表单上提供一个简单的组合框以选择输入格式类型。


你是只使用英制单位还是也使用公制单位工作? - Kyte
@Kyte,显然他是一名工程师。 - tenub
这可以用正则表达式进行验证。然而,由于有太多可选的格式,用户很难记住输入规则。 - user557597
就是这样,我不希望他们记住任何规则,除了他们已经日常使用的(Autocad、其他行业应用程序)。我希望我的函数能够优雅地处理他们的输入,我认为这是可能的,因为我已经将其缩小到以上仅有的几种可能性。 - jth41
2
我认为你可以用三个输入框做得更好。一个是英尺,一个是英寸,还有一个是英寸分数的组合框。将它们一起使用以确定一个数字。 - user557597
显示剩余3条评论
2个回答

5
这个函数适用于你提供的输入值示例。
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语句中检查空/仅包含空格的字符串的特殊情况。

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


除了我处理无意义字符的方式之外,一切似乎都很顺利。您认为最好的处理方法是什么?我该怎么做才能确保它是此操作的有效字符串? - jth41
非常感谢您的精彩回答!除了当我输入 " 时会出现问题,其他都完美无缺。 我该如何添加这个简单的检查?它当前会像我期望的那样在只有 ' 的输入字符串上引发错误。 - jth41
我猜最简单的方法是在if语句中添加更多检查:if(!m.Success || inp.Trim()=="" || inp.Trim()=="\"")。(Trim可能应该移到函数顶部。这也可以消除正则表达式中前导和尾随空格的需要。) - Trygve Flathen
你有没有想过是否可能让用户输入任意精度,而不仅仅是允许"3/16"这样的输入,比如用户可以输入"3/32"? - jth41
1
不要使用 inp.Trim()=="",而是使用 String.IsNullOrWhiteSpace(inp) - Basic
显示剩余3条评论

1
这可能会将您的复杂性从分支逻辑转移到正则表达式逻辑中:
/(?<special>(?<feet>\d+)(?<inch>\d{2})(?<sixt>\d{2}))|((?<feet>[\d.]+)')?[\s-]*((?<inch>\d+)?[\s-]*((?<numer>\d+)\/(?<denom>\d+))?")?/

如果组special匹配,则使用特殊语法,输出为feet*12+inch+_sixt_/16,对组使用ToDecimal。 如果不匹配,则如果输入有效,则会有一个或多个组feetinchnumerdenom。对于feet使用ToDouble,其余使用ToDecimal,并确保检查分数的除零情况。
概念验证演示代码(ruby):
[
  ["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)} "
}

输出:

              12.5'         150.0   150.0 
               11"           11.0   11.0 
              3/16"        0.1875   0.1875 
            11' 11"         143.0   143.0 
             11'11"         143.0   143.0 
            12'-11"         155.0   155.0 
       12' 11 3/16"      155.1875   155.1875 
        12' 11-1/2"         155.5   155.5 
  12'   11     1/2"         155.5   155.5 
             121103      155.1875   155.1875 
                                0   0.0 

请注意,除了您提供的有效输入之外,我没有进行任何其他输入测试。您可能需要检查至少一些捕获组中的非空值,或者将验证作为单独的步骤进行。此代码是针对解析而编写的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接