4

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).

Community
  • 1
  • 1
RHPT
  • 2,560
  • 5
  • 31
  • 43
  • 1
    can you please define 'doesn't work' error? wrong results? – genericHCU Dec 20 '12 at 18:36
  • At the very least he'll be getting an error for the regex - `([\d{1}|X{1})` is invalid syntax - should probably just be `[\dX]`, – Peter Boughton Dec 20 '12 at 18:42
  • @Travis The function doesn't return true for a correct VIN. – RHPT Dec 20 '12 at 18:53
  • @Peter That Regex works: http://gskinner.com/RegExr/?336u3 – RHPT Dec 20 '12 at 18:55
  • No it doesn't. I have no idea what that link is indicating (I don't have Flash in this browser), but the `[` needs to be escaped in CF regex. If you don't escape it, you're starting a character class that consumes until the next `]` (or throws an error if there is no subsequent closing bracket). – Peter Boughton Dec 20 '12 at 19:00
  • OK, humour people who aren't in the United States and have no idea what a VIN looks like, and provide your test data pls. Yes, I'll google one up, but it would be good to see a free-standing reproduction case including input, function & output demonstrating the problem. Am working on a solution in the mean time ;-) – Adam Cameron Dec 20 '12 at 19:15
  • Are you just wanting to validate against the US version (http://en.wikipedia.org/wiki/FMVSS)? According to the wikipedia page for VINs (http://en.wikipedia.org/wiki/Vehicle_identification_number#VIN:_classification), there are a few standards? – Adam Cameron Dec 20 '12 at 19:17
  • @AdamCameron Yes, just US VINs. 1GNDM19ZXRB170064 is a valid VIN. – RHPT Dec 20 '12 at 19:18
  • Returning 1 doesn't mean the regex is correct, it just means it matched at position 1 (the start of the string) - it also returns 1 for `1GNDM19Z|||170064` which is obviously not correct. – Peter Boughton Dec 20 '12 at 19:26
  • All I want for christmas is Peter's grasp of regex. – genericHCU Dec 20 '12 at 19:32
  • @RHPT, can I pls use your revised function on CFLib to fix the currently not working one? I'll credit you (and Peter ;-). Lemme know. – Adam Cameron Dec 20 '12 at 19:38
  • @AdamCameron Yeah. I was just about to msg you to say the same thing:) – RHPT Dec 20 '12 at 19:39
  • Doing it now. Cheers dude. – Adam Cameron Dec 20 '12 at 19:44
  • Travis: five days to go; that's plenty of time. :) – Peter Boughton Dec 20 '12 at 23:23

2 Answers2

8

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).

Community
  • 1
  • 1
Peter Boughton
  • 110,170
  • 32
  • 120
  • 176
3

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.

Adam Cameron
  • 29,677
  • 4
  • 37
  • 78