I am trying to convert this PHP validation code to ColdFusion. However, I cannot get my CF version to correctly validate a VIN. I am hoping someone can shed some light on what I'm missing.
<cfscript>
function isVIN(v) {
var i = "";
var d = "";
var checkdigit = "";
var sum = 0;
var weights = [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
};
if (! REFindNoCase("^([\w]{3})[A-Z]{2}\d{2}([A-Z]{1}|\d{1})([\d{1}|X{1})([A-Z]+\d+|\d+[A-Z]+)\d{5}$", ARGUMENTS.v)) {
return false;
}
if (Len(ARGUMENTS.v) != 17) {
return false;
}
for (i = 1; i <= Len(ARGUMENTS.v); i++) {
d = Mid(ARGUMENTS.v, i, 1);
if (! isNumeric(d)) {
sum += transliterations[d] * weights[i];
} else {
sum += d * weights[i];
}
}
checkdigit = sum % 11;
if (checkdigit == 10) {
checkdigit = "x";
}
if (checkdigit == Mid(ARGUMENTS.v,8,1)) {
return true;
}
return false;
}
</cfscript>
(There is a VIN validation function at CFLib.org, but it doesn't work either).
Your function has two issues.
First, the regex is incorrect. Here's a simplified working version:
^[A-Z\d]{3}[A-Z]{2}\d{2}[A-Z\d][\dX](?:[A-Z]+\d+|\d+[A-Z]+)\d{5}$
Note: As per Adam's answer there's a simpler pattern than this.
Second, in your checkdigit comparison the Mid is one out - it seems the 8 should be a 9.
(Presumably this is a language conversion issue due to PHP being 0-indexed whilst CFML is 1-indexed.)
With both of these fixed, the modified function returns true for the VIN WAUBA24B3XN104537
(which is the only sample VIN I could find in a quick search).
Actually the regex is slightly wrong still. I think @PeterBoughton fixed the syntax, but it wasn't actually valid for the task at hand.
Here's the revised section of code, with suitable comments:
var vinRegex = "(?x) ## allow comments
^ ## from the start of the string
## see http://en.wikipedia.org/wiki/Vehicle_Identification_Number for VIN spec
[A-Z\d]{3} ## World Manufacturer Identifier (WMI)
[A-Z\d]{5} ## Vehicle decription section (VDS)
[\dX] ## Check digit
[A-Z\d] ## Model year
[A-Z\d] ## Plant
\d{6} ## Sequence
$ ## to the end of the string
";
if (! REFindNoCase(vinRegex, arguments.v)) {
return false;
}
This could be dramatically simplified to just this:
^[A-Z\d]{8}[\dX][A-Z\d]{2}\d{6}$
Using either of these also removes the requirement for the length check, as the regex will enforce that too.
Test code for this modification:
for (vin in [
"1GNDM19ZXRB170064",
"1FAFP40634F172825"
]){
writeOutput("#vin#: #isVin(vin)#<br />");
}
I'm gonna update CFLib with the verbose version, as it's easier to understand what's going on, and marry-up to the spec.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With