【问题标题】:PHP validate German Tax Id (Steueridentifikationsnummer)PHP 验证德国税号 (Steueridentifikationsnummer)
【发布时间】:2014-06-18 10:09:55
【问题描述】:

德国税号 (Steueridentifikationsnummer) 具有以下属性:

  1. 有 11 位数字
  2. 第一位不能为0
  3. 在前十位数字中:一个数字恰好出现两次或三次,一位或两位数字出现零次,其他数字恰好出现一次
  4. 最后一位是校验和Example Code for Checksum

第三个要点对我来说有点难以优雅地解决。我已经有了其他三个要点的代码,但很想得到最后一个要点的输入,以便其他人可以参考一下。

# validate tax number
        $taxNumber = $_POST['taxNumber'];
        echo preg_replace("/[^0-9]/", "", $taxNumber);

        if (strlen($taxNumber != 11)) {
            # 11 digits
            $taxNumberValid = false;
        } else if ($taxNumber[0] == "0") {
            # first digit != 0
            $taxNumberValid = false; 
        } else {
            # one digit two times, one digit zero times


            # checksum
            $numbers = str_split($taxNumber);
            $sum = 0;
            $product = 10;
            for ($i = 0; $i <= 9; $i++) {
                $sum = ($numbers[$i] + $product) % 10;
                 if ($sum == 0) {
                     $sum = 10;
                 }
                 $product = ($sum * 2) % 11;
            }
            $checksum = 11 - $product;
            if ($checksum == 10) {
                $checksum = 0;
            }

            if ($taxNumber[10] != $checksum) {
                $taxNumberValid = false;
            }
        }

【问题讨论】:

  • SO etiquette 会建议您在问题中显示您当前的验证码,如果有人帮助您,不要将其作为奖品提供。
  • @feeela 请看一下你自己的链接,gregexp 只是验证字符串是否由 11 位数字组成,中间用两个破折号分隔,所以在这里根本没有帮助。
  • @user574632 你说得对,我刚刚用我的代码更新了帖子
  • 如何将所有 10 位数字添加到数组中,然后使用 array_count_values()?在此之后,只有一个键可能有 2 作为值。
  • @SaschaP 感谢您的输入,非常优雅地解决了它

标签: php validation


【解决方案1】:

这段代码解决了问题:

// remove whitespaces, slashes & other unnecessary characters
$taxNumber = preg_replace("/[^0-9]/", "", $taxNumber);

// by default the taxnumber is correct
$taxNumberValid = true;

// taxnumber has to have exactly 11 digits
if (strlen($taxNumber) != 11) {
    $taxNumberValid = false;                
}

// first digit cannot be 0
if ($taxNumber[0] == "0") {
    $taxNumberValid = false; 
} 

/* 
 make sure that within the first ten digits:
     1.) one digit appears exactly twice or thrice
     2.) one or two digits appear zero times
     3.) and oll other digits appear exactly once once
*/
$digits = str_split($taxNumber);
$first10Digits = $digits;
array_pop($first10Digits);
$countDigits = array_count_values ($first10Digits);
if (count($countDigits) != 9 && count($countDigits) != 8) {
    $taxNumberValid = false;
}

// last check: 11th digit has to be the correct checkums
// see http://de.wikipedia.org/wiki/Steueridentifikationsnummer#Aufbau_der_Identifikationsnummer
$sum = 0;
$product = 10;
for($i = 0; $i <= 9; $i++) {
    $sum = ($digits[$i] + $product) % 10;
     if ($sum == 0) {
         $sum = 10;
     }
     $product = ($sum * 2) % 11;
}
$checksum = 11 - $product;
if ($checksum == 10) {
    $checksum = 0;
}
if ($taxNumber[10] != $checksum) {
    $taxNumberValid = false;
}

2017 年更新

直到 2016 年,规则是,在前十位数字内,一个数字必须恰好出现两次。

从 2017 年开始,规则是,在前十位数字内,一个数字必须恰好出现两次或三次。

【讨论】:

  • 有一个错误,可以使用 if (count($countDigits) !== 8) { $taxNumberValid = false; }
  • 此实现中存在一个错误,即“42341677904”被视为有效的 TIN,但事实并非如此。只有一个数字可以出现多次,这里我们有两个数字 4 和两个数字 7。
【解决方案2】:

根据@Pascal Klein 的回答,这是你用 JS 编写的方法:

function countValues(arr) {
  return arr.reduce((obj, item) => {
    obj[item] = obj[item] ? ++obj[item] : 1;
    return obj;
  }, {});
}

function validateTIN(tin) {
  const tinLength = 11;
  
  // Taxnumber has to have exactly 11 digits.
  if (tin.length !== tinLength) {
    return false;
  }
  
  // First digit cannot be 0.
  if (tin[0] === '0') {
    return false;
  }
  
  /* 
   make sure that within the first ten digits:
     1.) one digit appears exactly twice or thrice
     2.) one or two digits appear zero times
     3.) and all other digits appear exactly once
  */
  const tinArray = tin.split('').slice(0, -1);
  const valueCount = countValues(tinArray);
  const valueCountLength = Object.keys(valueCount).length;

  if (valueCountLength !== 8 && valueCountLength !== 9) {
    return false;
  }
  
  // 11th digit has to match the checkum.
  let sum = 0;
  let product = 10;
  
  for(let i = 0; i < tinLength - 1; i++) {
    sum = (+tinArray[i] + product) % 10;
     if (sum === 0) {
         sum = 10;
     }
    product = (sum * 2) % 11;
  }

  let checksum = 11 - product;
  
  if (checksum === 10) {
    checksum = 0;
  }

  if (+tin[tinLength - 1] !== checksum) {
    return false;
  }
  
  return true;
}

const tin1 = 'gbg';
const tin2 = '42344677908';
const tin3 = '12005078909';
const tin4 = '36574261809'; // valid
const tin5 = '10863924976'; // valid

console.log(tin1, validateTIN(tin1));
console.log(tin2, validateTIN(tin2));
console.log(tin3, validateTIN(tin3));
console.log(tin4, validateTIN(tin4));
console.log(tin5, validateTIN(tin5));

【讨论】:

  • 这个实现中有一个错误。 validateTIN('42341677904') 返回 true,但它实际上是无效的 TIN,因为有两个 4 和两个 7。而且按照规则只能重复一个数字。
【解决方案3】:

这是一个考虑所有规则的解决方案(在 Javascript 中)。

function validateTin(tin) {
  // Allow space and slash (/) as number separators
  tin = tin.replace(/ |\//g, "");

  // 11 digits, the first is not allowed to be 0
  if (!/^[1-9][0-9]{10}$/.test(tin)) {
    return false;
  }

  const firstTen = tin.slice(0, 10);

  // Count the number of occurrences of each digit
  const occurrences = firstTen
    .split("")
    .reduce((acc, d) => acc.set(d, (acc.get(d) || 0) + 1), new Map());

  const keys = [...occurrences.keys()];
  const values = [...occurrences.values()];

  // If one digit occurs twice, the length of keys will be 9
  // If one digit occurs three times OR if two different digits occurs twice
  // each, the length will be 8
  if (keys.length !== 9 && keys.length !== 8) {
    return false;
  }

  if (keys.length === 8) {
    // Check how many times the digit that occurred the most times occurred
    const maxOccurrences = values.reduce((max, num) => Math.max(max, num));

    // If maxOccurrences is 2, we know two different numbers occurred twice
    // each. This is not a valid tin.
    // A final restriction is that a number can not occur three times in a row
    if (maxOccurrences === 2 || /(\d)\1\1/.test(firstTen)) {
      return false;
    }
  }

  // Calculate the checksum digit
  let m11 = 10;
  let m10 = 0;

  for (let i = 0; i < 10; i++) {
    m10 = (parseInt(tin[i], 10) + m11) % 10;
    if (m10 === 0) {
      m10 = 10;
    }

    m11 = (2 * m10) % 11;
  }

  let digit = 11 - m11;
  if (digit === 10) {
    digit = 0;
  }

  return digit === parseInt(tin[10], 10);
}

【讨论】:

    猜你喜欢
    • 2021-02-06
    • 1970-01-01
    • 1970-01-01
    • 2011-12-27
    • 1970-01-01
    • 1970-01-01
    • 2022-01-14
    • 1970-01-01
    • 2021-03-17
    相关资源
    最近更新 更多