VIN Validation - Improved
After publishing the orignal VIN validation algorithm, we realized a need for more information about why the VIN failed validation. Below we improved on the orignal boolean (true/false) algorithm to provide various status messages to help you identify the errors within your data.
//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;
}
}