将数量字符串解析为数字

gyu*_*isc 2 .net c# ocr parsing

我正在研究一个使用OCR引擎识别纸质文档的系统.这些凭证是包含总金额,增值税和净金额等金额的发票.我需要将这些金额字符串解析为数字,但它们有多种格式和风格,使用不同的十进制符号和每张发票中的数字分隔数千.如果我试图在.NET中使用正常的double.tryparse和double.parse方法,那么它们通常会因某些金额而失败

这些是我收到的一些例子

"3.533,65" =>  3533.65 
"-133.696" => -133696
"-33.017" => -33017
"-166.713" => -166713
"-5088,8" => -5088.8 
"0.423" => 0.423
"9,215,200" => 9215200
"1,443,840.00" => 1443840
Run Code Online (Sandbox Code Playgroud)

我需要一些方法来猜测数字中的小数分隔符和千位分隔符是什么,然后将值提供给用户以确定这是否正确.

我想知道如何以优雅的方式解决这个问题.

Ric*_*ard 9

我不确定你是否能够得到一种优雅的方法来解决这个问题,因为如果你不能告诉它数据的来源,它总会变得暧昧.

例如,数字1.234和1,234都是有效数字,但没有确定符号意味着什么,你将无法分辨哪个是哪个.

就个人而言,我会编写一个函数,试图根据一些规则进行"最佳猜测"......

  • 如果数字包含,BEFORE .,则,必须为数千且.必须为小数
  • 如果数字包含.BEFORE ,,则.必须为数千且,必须为小数
  • 如果有> 1个,符号,则千位分隔符必须为,
  • 如果有> 1个.符号,则千位分隔符必须为.
  • 如果只有1个,跟随它的数字?如果它不是3,那么它必须是小数分隔符(同样的规则.)
  • 如果有3个数字将它分开(例如1,234和1.234),也许您可​​以将这个数字放在一边并解析同一页面上的其他数字以试图找出它们是否使用不同的分隔符,然后回到它?

一旦你计算出小数分开,删除任何千位分隔符(解析数字不需要)并确保小数分隔符.在您正在解析的字符串中.然后你可以把它传递进去Double.TryParse


Ben*_*ter 7

我可能会设置一个按优先顺序指定的规则列表,这样您就可以按优先级插入规则.然后,您可以根据返回正确规则的正则表达式匹配来解析列表.

快速原型很容易设置类似于:

public class FormatRule
{
    public string Pattern { get; set; }
    public CultureInfo Culture { get; set; }

    public FormatRule(string pattern, CultureInfo culture)
    {
        Pattern = pattern;
        Culture = culture;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在是用于按优先顺序存储规则的FormatRule列表:

List<FormatRule> Rules = new List<FormatRule>()
{
    /* Add rules in order of precedence specifying a culture
     * that can handle the pattern, I've chosen en-US and fr-FR
     * for this example, but equally any culture could be swapped
     * in for various formats you may need to use */
    new FormatRule(@"^0.\d+$", CultureInfo.GetCultureInfo("en-US")),
    new FormatRule(@"^0,\d+$", CultureInfo.GetCultureInfo("fr-FR")),
    new FormatRule(@"^[1-9]+.\d{4,}$", CultureInfo.GetCultureInfo("en-US")),
    new FormatRule(@"^[1-9]+,\d{4,}$", CultureInfo.GetCultureInfo("fr-FR")),
    new FormatRule(@"^-?[1-9]{1,3}(,\d{3,})*(\.\d*)?$", CultureInfo.GetCultureInfo("en-US")),
    new FormatRule(@"^-?[1-9]{1,3}(.\d{3,})*(\,\d*)?$", CultureInfo.GetCultureInfo("fr-FR")),

    /* The default rule */
    new FormatRule(string.Empty, CultureInfo.CurrentCulture)
}
Run Code Online (Sandbox Code Playgroud)

然后,您应该能够迭代列表以查找要应用的正确规则:

public CultureInfo FindProvider(string numberString)
{
    foreach(FormatRule rule in Rules)
    {
        if (Regex.IsMatch(numberString, rule.Pattern))
            return rule.Culture;
    }
    return Rules[Rules.Count - 1].Culture;
}
Run Code Online (Sandbox Code Playgroud)

此设置允许您轻松管理规则并设置何时应以某种方式处理某些内容的优先级.它还允许您指定不同的文化,以单向处理一种格式,而另一种格式处理不同的格式.

public float ParseValue(string valueString)
{
    float value = 0;
    NumberStyles style = NumberStyles.Any;
    IFormatProvider provider = FindCulture(valueString).NumberFormat;
    if (float.TryParse(numberString, style, provider, out value))
        return value;
    else
        throw new InvalidCastException(string.Format("Value '{0}' cannot be parsed with any of the providers in the rule set.", valueString));
}
Run Code Online (Sandbox Code Playgroud)

最后,调用您的ParseValue()方法将您拥有的字符串值转换为float:

string numberString = "-123,456.78"; //Or "23.457.234,87"
float value = ParseValue(numberString);
Run Code Online (Sandbox Code Playgroud)

您可以决定使用字典来保存额外的FormatRule类; 概念是一样的...我在示例中使用了一个列表,因为它使查询使用LINQ更容易.此外,如果需要,您可以轻松替换我用于单,双或十进制的浮点类型.