github.com/artisanhe/tools@v1.0.1-0.20210607022958-19a8fef2eb04/vin-code/vin_validator.go (about) 1 package vincode 2 3 import ( 4 "fmt" 5 "strings" 6 ) 7 8 // VIN 约定 9 const ( 10 // VIN 约定长度 11 vinLegalLength = 17 12 ) 13 14 // 字符对应值 用于计算校验和 15 var vinLettersValue = map[byte]int{ 16 '0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9, 17 'A': 1, 'B': 2, 'C': 3, 'D': 4, 'E': 5, 'F': 6, 'G': 7, 'H': 8, 18 'J': 1, 'K': 2, 'L': 3, 'M': 4, 'N': 5, 19 'P': 7, 20 'R': 9, 21 'S': 2, 'T': 3, 'U': 4, 'V': 5, 'W': 6, 'X': 7, 'Y': 8, 'Z': 9, 22 } 23 24 // 位置加权系数 25 var vinIndexValue = map[int]int{ 26 0: 8, 1: 7, 2: 6, 3: 5, 4: 4, 5: 3, 6: 2, 27 7: 10, 28 9: 9, 10: 8, 11: 7, 12: 6, 13: 5, 14: 4, 15: 3, 16: 2, 29 } 30 31 // 统一转换VIN为大写字符 32 func vinToUpper(vin string) string { 33 return strings.ToUpper(vin) 34 } 35 36 // 校验VIN 长度 37 func checkVINLength(vin string) bool { 38 if len(vin) != vinLegalLength { 39 return false 40 } 41 return true 42 } 43 44 // 是否为ASCII大写字母 45 func isASCIIUpper(n rune) bool { 46 return n >= 'A' && n <= 'Z' 47 } 48 49 // 是否为ASCII小写字母 50 func isASCIILower(n rune) bool { 51 return n >= 'a' && n <= 'z' 52 } 53 54 // 是否为ASCII数字 55 func isASCIINumber(n rune) bool { 56 return n >= '0' && n <= '9' 57 } 58 59 // 校验VIN 是否由字母和数字字符构成 60 func checkVINCharacter(vin string) bool { 61 for _, v := range []rune(vin) { 62 if !isASCIIUpper(v) && !isASCIILower(v) && !isASCIINumber(v) { 63 return false 64 } 65 } 66 return true 67 } 68 69 // 校验VIN 校验位 70 func checkVINCheckDigit(vin string) bool { 71 actualNumRune := '-' 72 checkSum := 0 73 for k, v := range []byte(vin) { 74 if k == 8 { 75 actualNumRune = rune(v) 76 } else { 77 lv, ok := vinLettersValue[v] 78 if !ok { 79 fmt.Printf("v:%v not in vinLettersValue:%v", v, vinLettersValue) 80 return false 81 } 82 iv, ok := vinIndexValue[k] 83 if !ok { 84 fmt.Printf("index:%d not in vinIndexValue:%v", k, vinIndexValue) 85 return false 86 } 87 // each character's check num is calc by letter's value multiply index's value 88 checkSum += lv * iv 89 } 90 } 91 92 checkMod := checkSum % 11 93 checkDigit := ' ' 94 if checkMod == 10 { 95 checkDigit = 'X' 96 } else { 97 checkDigit = '0' + rune(checkMod) 98 } 99 //fmt.Println(checkSum, string(checkDigit)) 100 if checkDigit != actualNumRune { 101 fmt.Printf("expected check digit:%c,actual check digit:%c", checkDigit, actualNumRune) 102 return false 103 } 104 105 return true 106 } 107 108 func VINValidator(vin string, checkDigit bool) error { 109 vinStr := vinToUpper(vin) 110 if !checkVINLength(vinStr) { 111 return VINCodeLengthError 112 } 113 if !checkVINCharacter(vinStr) { 114 return VINCodeCharacterError 115 } 116 // for special cars,like volvo has no check digit 117 if checkDigit { 118 if !checkVINCheckDigit(vinStr) { 119 return VINCodeCheckDigitError 120 } 121 } 122 123 return nil 124 }