php 如何验证VIN号码?

hwamh0ep  于 2023-05-05  发布在  PHP
关注(0)|答案(8)|浏览(158)

如何使用PHP验证Vehicle Identification Number?我只需要检查输入的VIN号码是否正确。

yhived7q

yhived7q1#

下面是我用维基百科文章中的例子快速写出来的东西。
不能保证完美或无bug或超级高效,但应该为您提供一个坚实的起点:

注意:我在下面包含了Confluence提供的编辑,使程序稍微更简洁。

function validate_vin($vin) {

    $vin = strtolower($vin);
    if (!preg_match('/^[^\Wioq]{17}$/', $vin)) { 
        return false; 
    }

    $weights = array(8, 7, 6, 5, 4, 3, 2, 10, 0, 9, 8, 7, 6, 5, 4, 3, 2);

    $transliterations = array(
        "a" => 1, "b" => 2, "c" => 3, "d" => 4,
        "e" => 5, "f" => 6, "g" => 7, "h" => 8,
        "j" => 1, "k" => 2, "l" => 3, "m" => 4,
        "n" => 5, "p" => 7, "r" => 9, "s" => 2,
        "t" => 3, "u" => 4, "v" => 5, "w" => 6,
        "x" => 7, "y" => 8, "z" => 9
    );

    $sum = 0;

    for($i = 0 ; $i < strlen($vin) ; $i++ ) { // loop through characters of VIN
        // add transliterations * weight of their positions to get the sum
        if(!is_numeric($vin{$i})) {
            $sum += $transliterations[$vin{$i}] * $weights[$i];
        } else {
            $sum += $vin{$i} * $weights[$i];
        }
    }

    // find checkdigit by taking the mod of the sum

    $checkdigit = $sum % 11;

    if($checkdigit == 10) { // checkdigit of 10 is represented by "X"
        $checkdigit = "x";
    }

    return ($checkdigit == $vin{8});
}

注意:由于校验和的性质,验证VIN时会有很小的百分比错误:

匹配并不能证明VIN是正确的,因为任何两个不同的VIN仍然有1/11的机会具有匹配的校验位。

另请注意11111111111111111将根据上述过程进行验证。是否要检查,取决于您:

直的1(17个连续的1)将满足校验位。这是因为值1乘以89(权重之和)仍然是89。而89%11是1,校验位。这是测试VIN检查算法的简单方法。
参考:http://en.wikipedia.org/wiki/Vehicle_identification_number#Check_digit_calculation

gdx19jrr

gdx19jrr2#

下面是一个由jordan移植到Javascript的代码版本,希望对某人有帮助。

function validate_vin(vin)
{
  function isnumeric(mixed_var) {
    return (typeof(mixed_var) === 'number' || typeof(mixed_var) === 'string') && mixed_var !== '' && !isNaN(mixed_var);
  }
  var pattern = /^[^\Wioq]{17}$/;
  var weights = Array(8, 7, 6, 5, 4, 3, 2, 10, 0, 9, 8, 7, 6, 5, 4, 3, 2);
  var transliterations = {
    "a" : 1, "b" : 2, "c" : 3, "d" : 4,
    "e" : 5, "f" : 6, "g" : 7, "h" : 8,
    "j" : 1, "k" : 2, "l" : 3, "m" : 4,
    "n" : 5, "p" : 7, "r" : 9, "s" : 2,
    "t" : 3, "u" : 4, "v" : 5, "w" : 6,
    "x" : 7, "y" : 8, "z" : 9
  };

  vin = vin.toLowerCase();
  if(!vin.match(pattern)) { return false; }

  var sum = 0;
  for(var i=0; i<vin.length; i++) {
    if(!isnumeric(vin.charAt(i))) {
      sum += transliterations[vin.charAt(i)] * weights[i];
    } else {
      sum += parseInt(vin.charAt(i)) * weights[i];
    }  
  }

  var checkdigit = sum % 11;
  if(checkdigit == 10) { // check digit of 10 represented by X
    checkdigit = 'x';
  }

  return (checkdigit == vin.charAt(8));
}
cedebl8k

cedebl8k3#

是“VIN”,“VIN Number”=“Vehicle Identification Number Number Number”,这没道理。
您可以在此处查看VIN结构的定义:
http://en.wikipedia.org/wiki/Vehicle_identification_number
你可以从那里开始工作,或者你可以在这里获取这个脚本:
http://www.geekpedia.com/code29_Check-if-VIN-number-is-valid.html
下面是jordan发布的函数的改进版本:

$vin = "1M8GDM9AXKP042788";

function validate_vin($vin) {

    $vin = strtolower($vin);
    if (!preg_match('/^[^\Wioq]{17}$/', $vin)) { 
        return false; 
    }

    $weights = array(8, 7, 6, 5, 4, 3, 2, 10, 0, 9, 8, 7, 6, 5, 4, 3, 2);

    $transliterations = array(
        "a" => 1, "b" => 2, "c" => 3, "d" => 4,
        "e" => 5, "f" => 6, "g" => 7, "h" => 8,
        "j" => 1, "k" => 2, "l" => 3, "m" => 4,
        "n" => 5, "p" => 7, "r" => 9, "s" => 2,
        "t" => 3, "u" => 4, "v" => 5, "w" => 6,
        "x" => 7, "y" => 8, "z" => 9
    );

    $sum = 0;

    for($i = 0 ; $i < strlen($vin) ; $i++ ) { // loop through characters of VIN
        // add transliterations * weight of their positions to get the sum
        if(!is_numeric($vin{$i})) {
            $sum += $transliterations[$vin{$i}] * $weights[$i];
        } else {
            $sum += $vin{$i} * $weights[$i];
        }
    }

    // find checkdigit by taking the mod of the sum

    $checkdigit = $sum % 11;

    if($checkdigit == 10) { // checkdigit of 10 is represented by "X"
        $checkdigit = "x";
    }

    return ($checkdigit == $vin{8});
}
mnemlml8

mnemlml84#

我最近不得不用PHP编写一个VIN验证类。我发布了我的类供大家使用:http://dev.strategystar.net/2012/05/validate-vin-checksum-with-php/

class VIN
{
    public static $transliteration = array(
        'A'=>1, 'B'=>2, 'C'=>3, 'D'=>4, 'E'=>5, 'F'=>6, 'G'=>7, 'H'=>8, 
        'J'=>1, 'K'=>2, 'L'=>3, 'M'=>4, 'N'=>5, 'P'=>7, 'R'=>9,
        'S'=>2, 'T'=>3, 'U'=>4, 'V'=>5, 'W'=>6, 'X'=>7, 'Y'=>8, 'Z'=>9,
    );

    public static $weights = array(8,7,6,5,4,3,2,10,0,9,8,7,6,5,4,3,2);

    /***
     * The checksum method is used to validate whether or not a VIN is valid
     * It will return an array with two keys: status and message
     * The "status" will either be boolean TRUE or FALSE
     * The "message" will be a string describing the status
     */
    public static function checksum($vin)
    {
        $vin = strtoupper($vin);
        $length = strlen($vin);
        $sum = 0;

        if($length != 17)
        {
            return array('status'=>false, 'message'=>'VIN is not the right length');
        }

        for($x=0; $x<$length; $x++)
        {
            $char = substr($vin, $x, 1);

            if(is_numeric($char))
            {
                $sum += $char * self::$weights[$x];
            }
            else
            {
                if(!isset(self::$transliteration[$char]))
                {
                    return array('status'=>false, 'message'=>'VIN contains an invalid character.');
                }

                $sum += self::$transliteration[$char] * self::$weights[$x];
            }
        }

        $remainder = $sum % 11;
        $checkdigit = $remainder == 10 ? 'X' : $remainder;

        if(substr($vin, 8, 1) != $checkdigit)
        {
            return array('status'=>false, 'message'=>'The VIN is not valid.');
        }

        return array('status'=>true, 'message'=>'The VIN is valid.');
    }
}
rjjhvcjd

rjjhvcjd5#

请注意,wiki说:“在美国和加拿大销售的所有道路车辆都使用校验位验证。”
因此,如果你与其他国家合作,你可能想放松校验位验证
https://www.olschimke.eu/2012/08/02/dealing-with-vehicle-identification-numbers-vin-data-quality/有一些很好的提示。

o8x7eapl

o8x7eapl6#

感谢所有的算法等等。我在维基百科上看到的。这是我根据上面的评论整理的版本。注意,上面的版本有问题,例如00000000000354888对vin返回OK。我采取了上面的内容,并添加了一个选项,如果〈1980,则首先检查年份,假设它不是一个17位的真实的vin(必须),并且如果有单个字符的序列,则假设无效。这对于我的需要来说已经足够好了,因为我正在与长度为0(如果不是17)的值进行比较。我也知道年份的值,所以如果我检查,我可以加快代码跳过VIN检查(是的,我可以把它在函数之前)哈哈再见!.

<?php
  /*
  =======================================================================================
  PURPOSE: VIN Validation (Check-digit validation is compulsory for all road vehicles sold in North America.)
  DETAILS: Validates 17 digit VINs by checking their formatting
  USAGE:  returns boolean or returns an array with a detailed message
  COMMENTS: This could be made more robust by checking the country codes etc..
  MORE INFO: https://en.wikipedia.org/wiki/Vehicle_identification_number
  =======================================================================================
  */

  class vinValidation {

public static $transliteration = array(
    'A'=>1, 'B'=>2, 'C'=>3, 'D'=>4, 'E'=>5, 'F'=>6, 'G'=>7, 'H'=>8,
    'J'=>1, 'K'=>2, 'L'=>3, 'M'=>4, 'N'=>5, 'P'=>7, 'R'=>9,
    'S'=>2, 'T'=>3, 'U'=>4, 'V'=>5, 'W'=>6, 'X'=>7, 'Y'=>8, 'Z'=>9,
);

public static $weights = array(8,7,6,5,4,3,2,10,0,9,8,7,6,5,4,3,2);

public function validateVIN($vin, $ret_array_status = false, $year = null) {

    //validates US/NA 1980>= VINs, if before 1980, no vin standards, this returns false
    if (!empty($year) && preg_match("/^[0-9]{4}/", $year)) {
        if ($year < 1980) return ($ret_array_status ? array('status' => false, 'message' => 'Unable to check VIN, pre-dates 1980.') : false);
    }

    $vin_length = 17; // US vin requirements >= 1980
    $vin = strtoupper(trim($vin));
    $sum = 0;

     //if (!preg_match('/^[^\Wioq]{17}$/', $vin))
    //return ($ret_array_status ? array('status'=>false, 'message'=>'VIN is not valid, not the right length.') : false);

    if (!preg_match('/^[A-HJ-NPR-Z0-9]{17}$/', $vin))
    return ($ret_array_status ? array('status'=>false, 'message'=>'VIN is not valid, VIN formatting is incorrect.') : false);

    if (preg_match('/(\w)\1{5,}/', $vin))
    return ($ret_array_status ? array('status'=>false, 'message'=>'VIN contains invalid repeating character sequence.') : false);

    for($x=0; $x < $vin_length; $x++) {
        $char = substr($vin, $x, 1);
        if(is_numeric($char)) {
            $sum += $char * self::$weights[$x];
        } else {
            if(!isset(self::$transliteration[$char]))
            return ($ret_array_status ? array('status'=>false, 'message'=>'VIN contains an invalid character.') : false);
            $sum += self::$transliteration[$char] * self::$weights[$x];
        }
    }
    $remainder = $sum % 11;
    $checkdigit = $remainder == 10 ? 'X' : $remainder;

    //echo " sum:".$sum." remain:".$remainder." check dig:".$checkdigit."\n";

    if(substr($vin, 8, 1) != $checkdigit)
    return ($ret_array_status ? array('status'=>false, 'message'=>'The VIN is not valid, failed checksum.') : false);

    // all is good return true or a value and status.
    return ($ret_array_status ? array('status'=>true, 'message'=>'The VIN is valid, passed checksum.') : true);
}

  }

测试:

$vinClass = new vinValidation();

  // not long enough not val
  var_dump($vinClass->validateVIN('1I345678123456789', false, 2000));
  var_dump($vinClass->validateVIN('1I345678123456789'));

  echo "-----------------------------------------------------------\n";
  // not valid
  var_dump($vinClass->validateVIN('00000000012870842', true));
  var_dump($vinClass->validateVIN('00000000012870842', 1968)); //assumes faulty by year
  var_dump($vinClass->validateVIN('00000000012870842'));

  echo "-----------------------------------------------------------\n";
  // not valid
  var_dump($vinClass->validateVIN('00000000000354888', true));
  var_dump($vinClass->validateVIN('00000000000354888'));

  echo "-----------------------------------------------------------\n";
  // Fails Checksum test
  var_dump($vinClass->validateVIN('368TU79MXH4763452',false,2000));
  var_dump($vinClass->validateVIN('368TU79MXH4763452'));

  echo "-----------------------------------------------------------\n";
  // yachtzee, (returns true or array) !
  var_dump($vinClass->validateVIN('WP1AF2A56GLB91679',true));
  var_dump($vinClass->validateVIN('WP1AF2A56GLB91679'));
zlhcx6iw

zlhcx6iw7#

以下是Mike Q发布的JavaScript类版本:

class VIN {
    static transliteration = {
        'A':1, 'B':2, 'C':3, 'D':4, 'E':5, 'F':6, 'G':7, 'H':8, 'J':1, 'K':2, 'L':3, 'M':4, 'N':5, 'P':7, 'R':9, 'S':2, 'T':3, 'U':4, 'V':5, 'W':6, 'X':7, 'Y':8, 'Z':9
    }

    static weights = [8,7,6,5,4,3,2,10,0,9,8,7,6,5,4,3,2];

    validateVIN(vin, retArrayStatus = false, year = null) {

        if (year != null && year.match(/^[0-9]{4}/)) {
            if (year < 1980)
                return retArrayStatus ? {'status': false, 'message': 'Unable to check VIN, pre-dates 1980.'} : false;
        }

        let vinLength = 17;
        vin = vin.trim();
        let sum = 0;

        if (!vin.match(/^[A-HJ-NPR-Z0-9]{17}$/))
            return retArrayStatus ? {'status': false, 'message': 'VIN is not valid, VIN formatting is incorrect [i, o, q].'} : false;

        //if (!vin.match(/(\w)\1{5,}/))
        //    return retArrayStatus ? {'status': false, 'message': 'VIN contains invalid repeating character sequence.'} : false;

        for (let x = 0; x < vinLength; x++) {
            let char = vin.substr(x, 1);
            if (!isNaN(char)) {
                sum += char * VIN.weights[x];
            }
            else {
                if (VIN.transliteration[char] == '')
                    return retArrayStatus ? {'status': false, 'message': 'VIN contains an invalid character.'} : false;
                sum += VIN.transliteration[char] * VIN.weights[x];
            }
        }
        let reminder = sum % 11;
        let checkdigit = reminder == 10 ? 'X' : reminder;

        if (vin.substr(8, 1) != checkdigit)
            return retArrayStatus ? {'status': false, 'message': 'The VIN is not valid, failed checksum.'} : false;

        return retArrayStatus ? {'status': true, 'message': 'The VIN is valid, passed checksum.'} : true;
    }
}
j2qf4p5b

j2qf4p5b8#

真的很喜欢bimbom22代码,但在php 8.1.18更新后抛出此错误:PHP致命错误:不再支持带大括号的数组和字符串偏移量访问语法
以下是更新后的语法,以解决php 8.1.18中的致命错误

function validate_vin($vin) {
    $vin = strtolower($vin);
    if (!preg_match('/^[^\Wioq]{17}$/', $vin)) { 
        return false; 
    }
    $weights = array(8, 7, 6, 5, 4, 3, 2, 10, 0, 9, 8, 7, 6, 5, 4, 3, 2);
    $transliterations = array(
        "a" => 1, "b" => 2, "c" => 3, "d" => 4,
        "e" => 5, "f" => 6, "g" => 7, "h" => 8,
        "j" => 1, "k" => 2, "l" => 3, "m" => 4,
        "n" => 5, "p" => 7, "r" => 9, "s" => 2,
        "t" => 3, "u" => 4, "v" => 5, "w" => 6,
        "x" => 7, "y" => 8, "z" => 9
    );
    $sum = 0;
    for($i = 0 ; $i < strlen($vin) ; $i++ ) { // loop through characters of VIN
        // add transliterations * weight of their positions to get the sum
        $check_char = substr($vin, $i, 1);
        if(!is_numeric($check_char)) {
            $sum += $transliterations[$check_char] * $weights[$i];
        } else {
            $sum += $check_char * $weights[$i];
        }
    }

    // find checkdigit by taking the mod of the sum
    $checkdigit = $sum % 11;
    if($checkdigit == 10) { // checkdigit of 10 is represented by "X"
        $checkdigit = "x";
    }
    $actual_checkdigit = substr($vin, 8, 1);
    return ($checkdigit == $actual_checkdigit);
}

相关问题