【问题标题】:Validate Vat number in C#在 C# 中验证增值税号
【发布时间】:2013-08-21 01:33:25
【问题描述】:

增值税号码不是随机或顺序生成的,而是基于一个公式,可以检查该号码是否有效。如果增值税号无效,则企业无法重新申请增值税。

要手动验证英国增值税号,您可以执行以下练习:

排除前 2 个字母,垂直列出数字,然后将每个数字乘以一个以 8 开头并以 2 结尾的值。然后将所有的总和相加并从总和中减去 97,直到答案是否定的。负数应等于增值税号的最后 2 位数字。

例如,BLABLA 的增值税号是 GB 815382334,计算如下:

8 x 8 = 64
1 x 7 = 7 
5 x 6 = 30 
3 x 5 = 15 
8 x 4 = 32 
2 x 3 = 6 
3 x 2 = 6 

以上计算的总和是64 + 7 + 30 + 15 + 32 + 6 + 6 = 160 从中减去97,直到结果为负,结果为160 – 97 - 97= -34,与最后两位相同:因此增值税号有效。

我想编写一个 C# 应用程序,它以英国增值税号作为输入,使用上述公式计算校验和,并指示该号码是有效还是无效。

这对我来说是一个算法练习。我在网上找到了增值税检查器,但我不明白它们是如何工作的,所以我希望有人可以对上述问题给出一些简单的答案并给出很好的解释?

更新

    public static bool isValidVATNumber(string theVATNumber)
    {
        string startChar = "^";
        string endChar = "$";
        bool rtn = false;
        int i = 8;
        string valString;
        int sum = 0;
        // Check that the string matches the requirements
        rtn = Regex.IsMatch(theVATNumber, (startChar + ("(([1-9]d{8})|([1-9]d{11}))" + endChar)), RegexOptions.Multiline);
        if (rtn)
        {
            // Perform the validation
            valString = theVATNumber;
            if (Regex.IsMatch(valString, (startChar + "[A-Z]{2}"), RegexOptions.Multiline))
            {
                valString = valString.Substring(2);
            }
            while ((i >= 2))
            {
                sum = (sum
                            + (i * int.Parse(valString.Substring(0, 1))));
                valString = valString.Substring(1);
                i--;
            }
            while ((sum > 0))
            {
                sum -= 97;
            }
            rtn = ((sum * -1)
                        == int.Parse(valString));
        }
        return rtn;
    }

请注意,上述方法不起作用,对我来说更难理解,我从我自己的方法开始,我发现它更容易使用但尚未完成(请注意,这很尴尬)

    List<int> integerList = new List<int>();
    int b = 8;

    for (int a = 0; a < textBox1.Text.Length; a++)
    {
        integerList.Add(int.Parse(textBox1.Text[a].ToString())); 
    }
    foreach (int item in integerList) 
    {
        listBox1.Items.Add(item * b);
        --b; 
    }

我仍然需要计算列表的总和并进行其余的计算,并希望能够挑选一些人的大脑来了解其他可能的方法(更简单的方法)。

更新我自己的方法并感谢下面的 Pax:

    List<int> integerList = new List<int>();
    List<int> sumList = new List<int>();
    int b = 8; // Will be 8 for the first multiplication.

    for (int a = 0; a <= 6; a++)
    {
        integerList.Add(int.Parse(textBox1.Text[a].ToString())); 
    }
    foreach (int item in integerList) // Loop once per input digit.
    {

        //listBox1.Items.Add(item * b);
        sumList.Add(item * b);
        --b; 
    }
    listBox1.DataSource = sumList;

    int sum = sumList.Sum();

    while (sum > 0)
    {
        sum = sum - 97;
    }
    int myInt = System.Math.Abs(sum);
    label1.Text = Convert.ToString(myInt);

【问题讨论】:

  • 这是一个有趣的问题,但是您尝试过什么,您对 C# 的理解程度如何?如果您有基本的了解,该算法应该相当简单。一个提示是查看获取增值税号的字符串表示,并使用数组运算符来获取它的值,逐个字符...
  • @KirstyWhite 为了变得更好,你不断尝试,让人们看到你所做的事情(即使你认为这是令人尴尬的错误),你接受建议和建设性的批评,你忽略了喷子和怀疑和所有那些告诉你你没用的东西,你永远不会放弃。存在这样的地方的部分原因是为了让人们尝试和改进。
  • @Russ C: 或更多数学解决方案 - 使用 %
  • 您的初始公式不正确有两个原因 - 自 2010 年 HMRC 发布带有 9755 校验和的数字 - 所以 % 97 然后减去 55。数字中的零也应计为 10(所以数字从 104 开始是 1*8 + 10*7 + 4*6)

标签: c# algorithm


【解决方案1】:

好的,让我们一点一点来看看。假设您有代码 815382334 - 您已经删除了前面不相关的字符。

第一步是遍历字符并保持数值乘以索引的总和),在伪代码中:

sum = 0
for pos = 0 to 6 inclusive:
    sum = sum + num_at(pos) * (8 - pos)

对于上述循环的每次迭代,您从字符串中提取正确的数字并将其乘以它的索引,该索引从 8 开始,一直到 2。然后将其添加到 sum 变量中。请记住,num_at() 方法需要为您提供一个从零到九(含)的整数,不是字符代码本身,它可能是 0x300x39

我经常发现初学者坐下来运行程序更容易,使用他们的头作为 CPU 和一点纸作为存储,例如:

pos  num_at(pos)  8-pos  add  sum
---  -----------  -----  ---  ---
                                0
 0        8          8    64   64
 1        1          7     7   71
 2        5          6    30  101
 3        3          5    15  116
 4        8          4    32  148
 5        2          3     6  154
 6        3          2     6  160

第二步,按照规范,减去 97 直到变为负数:

while sum > 0:
    sum = sum - 97

(尽管您可能更有效地使用模运算符)。再一次,在你的脑海中运行它:

sum
---
160
 63
 34-

然后,作为第三步也是最后一步,将最后两位数加回(作为完整的两位数)以确保得到零:

sum = sum + num_at(7) * 10 + num_at(8)
return (sum == 0);

由于位置 7 和 8 的数字分别为 34,因此 num_at(7) * 10 + num_at(8) 将为您提供 34,这是您要加回负调整总和的值。

模数版本允许类似:

sum = 0
for pos = 0 to 6 inclusive:
    sum = sum + num_at(pos) * (8 - pos)
return ((sum % 97) + num_at(7) * 10 + num_at(8) == 97);

这是有效的,因为sum % 97 实际上与循环相同,它给你一个负数,但没有最后减去 97。因此,当你加回最后两位数时,你会得到 97 而不是 0(对于有效的增值税号)。

例如,160 % 97 为您提供6363 + 34 为您提供97


现在,根据您添加的代码片段,您可能需要处理两种类型的增值税号,即 9 位数和 12 位数。发布的代码片段可能比所需的复杂一点,其中包含所有正则表达式检查和子字符串,其中长度检查、简单的字符串索引和字符检查就足够了。

【讨论】:

  • 是的,谢谢 pax,就像我说的已经查过网络并且已经有了这种方法,但我仍然很难理解。
  • 如果您无法将以上内容从伪代码转换为实际代码,那么您可能需要先阅读一些更基础的教程/练习。 (不要试图粗鲁......只是试图帮助而不是为你做这件事,因为为你做这不是最好的学习方式......)
  • @Kirsty,我添加了一些解释性说明,希望对您有所帮助。
  • 不是我不能翻译,而是我看不懂。因此,为什么我来这里寻求一些更简单的帮助。我已经有了上述方法,如果这个该死的更新 3 能够完成,我会在几秒钟内发布它!
  • 感谢 Pax,解释开始听起来好多了!但是这里有点丢失:sum = sum + num_at(7) * 10 + num_at(8) 我还用我尝试的方法更新了我的问题。
【解决方案2】:

我在 C# 中的方法如下......我已经用几个值快速测试了它,但请确保在使用它之前正确测试它。我尝试选择显示意图清晰而不是性能的代码,以便可以优化一些位,但我认为除非您进行数百万次验证,否则不值得付出努力。

public static class VAT
{
    /// <summary>
    /// Validates a GB VAT number
    /// </summary>
    public static bool ValidateGBVatNumber(string vatNumber)
    {
        vatNumber = vatNumber.Replace(" ", "").ToUpperInvariant();
        if (vatNumber.Length == 11)
        {
            if (vatNumber[0] == 'G' && vatNumber[1] == 'B')
            {
                vatNumber = vatNumber.Substring(2);
            }
            else
            {
                // First digits are not GB
                return false;
            }
        }

        if (vatNumber.Length != 9)
        {
            // Wrong length even after removing spaces and getting rid of the first two characters
            return false;
        }

        // Provided number has 9 digits, which is correct. Proceed to calculate checksum
        int runningTotal = 0;
        int[] multipliersByIndex = new int[] {8, 7, 6, 5, 4, 3, 2};
        for (int i = 0; i < 7; i++)
        {
            int currentDigitValue;
            if (!int.TryParse(vatNumber[i].ToString(), out currentDigitValue))
            {
                // Could not parse a digit into an int => wrong character supplied
                return false;
            }
            runningTotal += currentDigitValue * multipliersByIndex[i];
        }

        // Subtract 97 until negative - this could perhaps be better done with the modulus operator
        // but this way might be more 'obvious'
        while (runningTotal >= 0)
        {
            runningTotal -= 97;
        }

        // Convert to a string that will have two digits even if the number only has one
        string checkSum = (runningTotal * -1).ToString("00");

        return (checkSum[0] == vatNumber[7] && checkSum[1] == vatNumber[8]);
    }
}

示例用法:

isValid = VAT.ValidateGBVatNumber("GB 815382334"); // True
isValid = VAT.ValidateGBVatNumber("GB815382334"); // True
isValid = VAT.ValidateGBVatNumber("815382334"); // True
isValid = VAT.ValidateGBVatNumber("GB 815382335"); // False
isValid = VAT.ValidateGBVatNumber("GB 81538233424242"); // False
isValid = VAT.ValidateGBVatNumber("YHUWOCNYEX"); // False

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-25
    • 1970-01-01
    • 2022-01-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多