VIN Validate – Improved

I have been concerned with several aspects of my previously published VIN validation algorithm. I added a section of code below to find invalid production sequences, while also improving on the message of why it was rejected. – This code is still in a testing phase, I will follow up with more details on the success of this new validation process when I have a chance to run it against my data in full.

//Copyright Rusty Davis 2010
    public class VIN
    {
        //Make sure no instance of this class is created... only method is static.
        private VIN() { }

        public enum VinStatus
        {
            Unknown = -1,
            IsNull = 0,
            InvalidLength = 1,
            InvalidCheckDigit = 2,
            InvalidCharForYear = 3,
            InvalidCharForVin = 4,
            InvalidCharForSequence = 5,
            Invalid = 6,
            Valid = 7
        }

        public static VinStatus IsValidVin(string p_strVin)
        {
            VinStatus status = VinStatus.Unknown;

            int intValue = 0;
            int[] intWeights = { 8, 7, 6, 5, 4, 3, 2, 10, 0, 9, 8, 7, 6, 5, 4, 3, 2 };

            if (p_strVin == null)
            {

                return VinStatus.IsNull;
            }
            else if (p_strVin.Length != 17)
            {
                return VinStatus.InvalidLength;
            }

            p_strVin = p_strVin.ToUpper().Trim();
            int intCheckValue = 0;
            char check = p_strVin[8];
            char year = p_strVin[9];
            char third = p_strVin[2];

            if (!char.IsDigit(check) &&check != 'X' )
            {
                return VinStatus.InvalidCheckDigit;
            }
            else
            {
                if (check != 'X')
                {
                    char[] d = new char[] { check };
                    intCheckValue = int.Parse(Encoding.ASCII.GetString(Encoding.ASCII.GetBytes(d)));
                }
                else
                {
                    intCheckValue = 10;
                }
            }

            Hashtable replaceValues = new Hashtable();
            replaceValues.Add('A', 1);
            replaceValues.Add('B', 2);
            replaceValues.Add('C', 3);
            replaceValues.Add('D', 4);
            replaceValues.Add('E', 5);
            replaceValues.Add('F', 6);
            replaceValues.Add('G', 7);
            replaceValues.Add('H', 8);
            replaceValues.Add('J', 1);
            replaceValues.Add('K', 2);
            replaceValues.Add('L', 3);
            replaceValues.Add('M', 4);
            replaceValues.Add('N', 5);
            replaceValues.Add('P', 7);
            replaceValues.Add('R', 9);
            replaceValues.Add('S', 2);
            replaceValues.Add('T', 3);
            replaceValues.Add('U', 4);
            replaceValues.Add('V', 5);
            replaceValues.Add('W', 6);
            replaceValues.Add('X', 7);
            replaceValues.Add('Y', 8);
            replaceValues.Add('Z', 9);
            replaceValues.Add('1', 1);
            replaceValues.Add('2', 2);
            replaceValues.Add('3', 3);
            replaceValues.Add('4', 4);
            replaceValues.Add('5', 5);
            replaceValues.Add('6', 6);
            replaceValues.Add('7', 7);
            replaceValues.Add('8', 8);
            replaceValues.Add('9', 9);
            replaceValues.Add('0', 0);

            //Make sure it is a Valid Year - Created the next 4 lines to correct U, Z & 0 from being in the list
            Hashtable yearValues = (Hashtable)replaceValues.Clone(); //Get a shallow copy of values
            yearValues.Remove('0');
            yearValues.Remove('Z');
            yearValues.Remove('U');
            if (!yearValues.Contains(year))
            {
                return VinStatus.InvalidCharForYear;
            }

            //Make sure characters that are in the VIN are the ones allowed.
            for (int i = 0; i < p_strVin.Length; i++)
            {
                if(!replaceValues.Contains(p_strVin[i]))
                {
                    return VinStatus.InvalidCharForSequence;
                }
                else if (i > 13 &&!char.IsDigit(p_strVin[i]) &&third == '9')
                {
                    return VinStatus.InvalidCharForSequence;
                }
                else if (i > 11 &&!char.IsDigit(p_strVin[i]) &&third != '9')
                {
                    return VinStatus.InvalidCharForSequence;
                }

                intValue += (intWeights[i] * ((int)replaceValues[p_strVin[i]]));
            }

            if ((intValue % 11) == intCheckValue)
            {
                return VinStatus.Valid;
            }

            return VinStatus.Invalid;
        }
    }

Edit: Corrected paste error Dec 6 2011
Edit: Corrected Year validation Dec 12 2011
Edit: Corrected Ford VIN production Sequences contain A-Z in 12 position. They are numeric equivalent replacements of the letter A = 1 etc. Others may exist still that do not fit the pattern

Advertisement

About shapemetrics

A little cohesion every now and again.

Posted on December 5, 2011, in .net Framework, VIN and tagged , , . Bookmark the permalink. Leave a Comment.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.