避免使用小数。TryParse 将“1.1.1”或“1,1,1”识别为十进制

Posted

技术标签:

【中文标题】避免使用小数。TryParse 将“1.1.1”或“1,1,1”识别为十进制【英文标题】:Avoid decimal.TryParse to recognize "1.1.1" or "1,1,1" as decimal 【发布时间】:2016-03-07 09:27:39 【问题描述】:

我在字符串识别方面遇到问题:我试图只识别正确格式的数字,但不知道如何。

我正在编写一种文化不变的方式,因此我需要识别“,”和“。”作为小数和千位分隔符,反之亦然。

所有这些对我来说都是正确的格式:

12,1
12.1
1.000,12
1,000.12

但是这样的事情是错误的

1.2.3
1,2,3

我试过了:

NumberStyles style;
decimal n;  
object valore;                                  
style = NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign | NumberStyles.AllowThousands;

Console.WriteLine(decimal.TryParse(valore.ToString(), style , CultureInfo.InvariantCulture, out n));

this is the fiddle to test it

但是“1,1,1”被读取为有效数字,而“1.1.1”则不是。

如果我使用我的特定文化(“it-IT”),“1,1,1”会被识别,而“1.1.1”则不会。

如何丢弃所有无效字符串?

【问题讨论】:

据我所知,您没有在代码中的任何地方使用style 这不是小数点分隔符,NumberGroupSeparatorNumberGroupSizes 仅用于输出,小于最大值(默认为 3)是允许的。 所有这些对我来说都是正确的格式。不,他们不是。您不知道该值是 1000.12 还是 100012。相差 100 倍从来都不是很微妙。所以这对于其他字符串也会出错,比如 1,1,1。您认为您可以正确执行此操作的假设是不正确的。 @gt.guybrush 标准 .NET 格式化和解析基础结构并非旨在处理此类情况。但是无论你是让它按照你想要的那样工作,还是制作你自己的解析器,你仍然会遇到一个问题——歧义。您将如何处理字符串“My numbers are 1,000 1,001 1.000 1.001”(英语)和“Meine Zahlen sind 1,000 1,001 1.000 1.001”(德语)?相同的字符串表示将意味着完全不同的事情,因此几乎不可能做到。一些智能文本分析可能会识别语言/文化,但即使这样也不可靠。 允许两种数字格式会引发更大的问题:如果您收到输入字符串“1.024”怎么办?是一千二十四吗?还是一加分数? 【参考方案1】:

我用个人解析器结束了这个,我希望找到一个更好的解决方案,但是读取 cmets 似乎不太可能

public static bool tryValoreNumerico(object valore, out decimal valoreRestituito)
    
        decimal n;          
        string testoNormalizzato;

        valoreRestituito = 0;

        // normalizzazione
        if (valore.ToString().Contains(",") && valore.ToString().Contains("."))
        
            if (valore.ToString().IndexOf(".") < valore.ToString().IndexOf(","))
            
                testoNormalizzato = valore.ToString().Replace(".", "");
            
            else
            
                testoNormalizzato = valore.ToString().Replace(",", "");
            
        
        else
        
            if ((valore.ToString().Length - valore.ToString().Replace(",", "").Length) > 1)
            
                string[] pezzi = valore.ToString().Split(',');
                for (int i = 1; i < pezzi.Length; i++ )
                
                    if (pezzi[i].Length != 3)
                        return false;
                

                testoNormalizzato = valore.ToString().Replace(",", "");
            
            else if ((valore.ToString().Length - valore.ToString().Replace(".", "").Length) > 1)
            
                string[] pezzi = valore.ToString().Split('.');
                for (int i = 1; i < pezzi.Length; i++)
                
                    if (pezzi[i].Length != 3)
                        return false;
                

                testoNormalizzato = valore.ToString().Replace(".", "");
            

            else
                testoNormalizzato = valore.ToString();

        
        testoNormalizzato = testoNormalizzato.Replace(",", ".");

        if (decimal.TryParse(testoNormalizzato, out n) && testoNormalizzato == Convert.ToDecimal(testoNormalizzato, new CultureInfo("en-US")).ToString().Replace(",", "."))
        
            valoreRestituito = Convert.ToDecimal(testoNormalizzato, new CultureInfo("en-US"));
        
        return decimal.TryParse(testoNormalizzato, out n) && testoNormalizzato == Convert.ToDecimal(testoNormalizzato, new CultureInfo("en-US")).ToString().Replace(",", ".");

    

首先我将数字标准化为 EN 格式,然后尝试转换它

最后的测试是看没有与数字“相似”的文本,因为像“001”、“100 01”这样的字符串不是数字。

比率是:每个字符串都必须保持不变:“001”转换为数字变为“1”,与原始值不同,因此必须避免这种转换

this is the fiddle

【讨论】:

【参考方案2】:

我建议您使用Regex 进行验证和这样的自定义解析方法:

public static decimal DecimalParse(string number)

    if (new Regex(@"^\d+$").IsMatch(number))
    
        return decimal.Parse(number, CultureInfo.InvariantCulture);
    
    if (new Regex(@"^(\d0,3(,\d3)*(\.\d+)?)$").IsMatch(number))
    
        return decimal.Parse(number, CultureInfo.InvariantCulture);
    
    return new Regex(@"^(\d0,3(\.\d3)*(,\d+)?)$").IsMatch(number)
        ? decimal.Parse(number.Replace(".", "").Replace(",", "."), CultureInfo.InvariantCulture)
        : 0;

结果将是:

string num;
num = "1,000";      Console.WriteLine("0", DecimalParse(num));     //1000
num = ",01";        Console.WriteLine("0", DecimalParse(num));     //0.01
num = ".02";        Console.WriteLine("0", DecimalParse(num));     //0.02
num = "12,1";       Console.WriteLine("0", DecimalParse(num));     //12.1
num = "12.1";       Console.WriteLine("0", DecimalParse(num));     //12.1
num = "1.000,12";   Console.WriteLine("0", DecimalParse(num));     //1000.12
num = "1.000.000,12"; Console.WriteLine("0", DecimalParse(num));   //1000000.12
num = "1,000.12";   Console.WriteLine("0", DecimalParse(num));     //1000.12
num = "1,000,000.12"; Console.WriteLine("0", DecimalParse(num));   //1000000.12
num = "1000";       Console.WriteLine("0", DecimalParse(num));     //0
num = "110.";       Console.WriteLine("0", DecimalParse(num));     //0
num = "110,";       Console.WriteLine("0", DecimalParse(num));     //0
num = "1.2.3";      Console.WriteLine("0", DecimalParse(num));     //0
num = "1,2,3";      Console.WriteLine("0", DecimalParse(num));     //0

【讨论】:

无法让它在我的具有意大利文化的机器上工作:第一个正则表达式通过但 decimal.parge thow 错误。你能制作一个与文化无关的版本吗? return decimal.Parse(number, CultureInfo.InvariantCulture); 也有错误吗? 所以,我使用CultureInfo.InvariantCulture 更新了我的答案; 1,2 应该返回 1.2 ;)。 其他情况:1234无法识别:千位分隔符不是强制性的:可能是\d0,+而不是\d0,3 好吧,如果您更新答案以管理像 123456 这样的号码,我将接受作为解决方案

以上是关于避免使用小数。TryParse 将“1.1.1”或“1,1,1”识别为十进制的主要内容,如果未能解决你的问题,请参考以下文章

在c#中如何实现判断一个数值是不是为整数(或小数) 谢谢

c#中如何判断文本框中是不是是数字(包括小数)

如何避免使用 innerHTML 显示的数值中的小数

强制 TextBox 接受小数

Double.TryParse 或 Convert.ToDouble - 哪个更快更安全?

如何将 int.TryParse 与可为空的 int 一起使用? [复制]