Introduction
In logistics companies, sometimes you have to check if an ISO Containernumber
is valid or not. The validation depends on the last number of the ISO Containernumber
. This tip shows how to accomplish this validation.
Background
If anyone is interested in how the validation has to be done step by step, please visit this page.
Using the Code
The main part of the validation class is the ISO Alphabet Dictionary
. It contains all char
s used in the ISO standard and the int
that contains the char
. Because the class and all parts of it are static
, the getter of the Alphabet dictionary
is checked for null
. If it is null
, it adds all char
s to it. Using it like this, the Dictionary
won't be created and filled every time this class is called. Not a big improvement for the performance, but every millisecond counts. ;)
As mentioned, the Alphabet dictionary
field and properties look like this:
private static Dictionary<char, int> _Alphabet;
private static Dictionary<char, int> Alphabet
{
get {
if (_Alphabet == null)
{
_Alphabet = new Dictionary<char, int>();
_Alphabet.Add('A', 10);
_Alphabet.Add('B', 12);
_Alphabet.Add('C', 13);
_Alphabet.Add('D', 14);
_Alphabet.Add('E', 15);
_Alphabet.Add('F', 16);
_Alphabet.Add('G', 17);
_Alphabet.Add('H', 18);
_Alphabet.Add('I', 19);
_Alphabet.Add('J', 20);
_Alphabet.Add('K', 21);
_Alphabet.Add('L', 23);
_Alphabet.Add('M', 24);
_Alphabet.Add('N', 25);
_Alphabet.Add('O', 26);
_Alphabet.Add('P', 27);
_Alphabet.Add('Q', 28);
_Alphabet.Add('R', 29);
_Alphabet.Add('S', 30);
_Alphabet.Add('T', 31);
_Alphabet.Add('U', 32);
_Alphabet.Add('V', 34);
_Alphabet.Add('W', 35);
_Alphabet.Add('X', 36);
_Alphabet.Add('Y', 37);
_Alphabet.Add('Z', 38);
_Alphabet.Add('0', 0);
_Alphabet.Add('1', 1);
_Alphabet.Add('2', 2);
_Alphabet.Add('3', 3);
_Alphabet.Add('4', 4);
_Alphabet.Add('5', 5);
_Alphabet.Add('6', 6);
_Alphabet.Add('7', 7);
_Alphabet.Add('8', 8);
_Alphabet.Add('9', 9);
}
return _Alphabet;
}
}
The bool that determines if the ISO Containernumber
is valid or not retrieves a string
value that represents the ISO Containernumber
to check. In this code snippet, there are other methods that we will look at one by one.
public static bool Check(string containerNumberToCheck)
{
string containerNumber = CleanConNumberString(containerNumberToCheck);
if (containerNumber == string.Empty) return true;
if (containerNumber.Length != 11) return false;
double summ = GetSumm(containerNumber);
double tempCheckNumber = summ - (Math.Floor(summ / 11) * 11);
if (tempCheckNumber == 10) tempCheckNumber = 0;
if (tempCheckNumber == GetCheckNumber(containerNumber))
return true;
return false;
}
The first is CleanConNumberString
. It is used to remove all Char
s in the Input string
representing the ISO Containernumber
. It loops through all char
s in the Inputstring
and checks if that char
exists in the Alphabet dictionary
. If it doesn't exist, it replaces that char
with an empty string
(short way to remove a char
from a string
;) ).
private static string CleanConNumberString(string inputString)
{
string resultString = inputString.ToUpper();
foreach (char c in inputString)
{
if (!Alphabet.Keys.Contains(c))
resultString=resultString.Replace(c.ToString(), string.Empty); }
return resultString;
}
The next is the GetSumm double
. In it, we get the specified Summ
defined with the ISO standard. As mentioned, you can get more information about that at this page. The summ
is an addition of the Char
number from the Alphabet dictionary
multiplied with 2 that is "powed" with the Char
index in the Inputstring
.
private static double GetSumm(string inputString)
{
double summ = 0;
if (inputString.Length > 1)
{
for (int i = 0; i < inputString.Length - 1; i++)
{
char temChar = inputString[i];
int charNumber=0;
if(Alphabet.Keys.Contains(temChar))
charNumber = Alphabet[temChar];
summ += charNumber * (Math.Pow(2, i));
}
}
return summ;
}
Also, an important int
that we need is the "Check digit". It's just the last Char
of the Inputstring
converted to an int
. It is important that we return 11 if we can't convert the char
to an int
. In that case, we will surely get false
for the ISO Containernumber
validation because the Check digit can't be 11 ;).
private static int GetCheckNumber(string inputString)
{
if (inputString.Length > 1)
{
char checkChar = inputString[inputString.Length - 1];
int CheckNumber = 0;
if (Int32.TryParse(checkChar.ToString(), out CheckNumber))
return CheckNumber;
}
return 11;
}
Let´s take a look again at the method that returns the important bool value going through it step by step.
- We clean the
inputstring
- Return
false
if the string
doesn't have enough char
s or is empty (good if this code is used in DataGridView
) - We get the ISO
Summ
- We calculate the
tempCheckNumber
or the temCheckdigit
- VERY IMPORTANT - if the
temCheck
digit is 10
, we set its value to 0
- FINALLY, if the
tempCheckNumber
is equal to the real CheckNumber
, the Containernumber string
is valid :)
public static bool Check(string containerNumberToCheck)
{
string containerNumber = CleanConNumberString(containerNumberToCheck);
if (containerNumber == string.Empty) return true;
if (containerNumber.Length != 11) return false;
double summ = GetSumm(containerNumber);
double tempCheckNumber = summ - (Math.Floor(summ / 11) * 11);
if (tempCheckNumber == 10) tempCheckNumber = 0;
if (tempCheckNumber == GetCheckNumber(containerNumber))
return true;
return false;
}
Code of the Class
public static class ContainerNumber
{
private static Dictionary<char, int> _Alphabet;
private static Dictionary<char, int> Alphabet
{
get {
if (_Alphabet == null)
{
_Alphabet = new Dictionary<char, int>();
_Alphabet.Add('A', 10);
_Alphabet.Add('B', 12);
_Alphabet.Add('C', 13);
_Alphabet.Add('D', 14);
_Alphabet.Add('E', 15);
_Alphabet.Add('F', 16);
_Alphabet.Add('G', 17);
_Alphabet.Add('H', 18);
_Alphabet.Add('I', 19);
_Alphabet.Add('J', 20);
_Alphabet.Add('K', 21);
_Alphabet.Add('L', 23);
_Alphabet.Add('M', 24);
_Alphabet.Add('N', 25);
_Alphabet.Add('O', 26);
_Alphabet.Add('P', 27);
_Alphabet.Add('Q', 28);
_Alphabet.Add('R', 29);
_Alphabet.Add('S', 30);
_Alphabet.Add('T', 31);
_Alphabet.Add('U', 32);
_Alphabet.Add('V', 34);
_Alphabet.Add('W', 35);
_Alphabet.Add('X', 36);
_Alphabet.Add('Y', 37);
_Alphabet.Add('Z', 38);
_Alphabet.Add('0', 0);
_Alphabet.Add('1', 1);
_Alphabet.Add('2', 2);
_Alphabet.Add('3', 3);
_Alphabet.Add('4', 4);
_Alphabet.Add('5', 5);
_Alphabet.Add('6', 6);
_Alphabet.Add('7', 7);
_Alphabet.Add('8', 8);
_Alphabet.Add('9', 9);
}
return _Alphabet;
}
}
public static bool Check(string containerNumberToCheck)
{
string containerNumber = CleanConNumberString(containerNumberToCheck);
if (containerNumber == string.Empty) return true;
if (containerNumber.Length != 11) return false;
double summ = GetSumm(containerNumber);
double tempCheckNumber = summ - (Math.Floor(summ / 11) * 11);
if (tempCheckNumber == 10) tempCheckNumber = 0;
if (tempCheckNumber == GetCheckNumber(containerNumber))
return true;
return false;
}
private static string CleanConNumberString(string inputString)
{
string resultString = inputString.ToUpper();
foreach (char c in inputString)
{
if (!Alphabet.Keys.Contains(c))
resultString=resultString.Replace(c.ToString(), string.Empty); }
return resultString;
}
private static int GetCheckNumber(string inputString)
{
if (inputString.Length > 1)
{
char checkChar = inputString[inputString.Length - 1];
int CheckNumber = 0;
if (Int32.TryParse(checkChar.ToString(), out CheckNumber))
return CheckNumber;
}
return 11;
}
private static double GetSumm(string inputString)
{
double summ = 0;
if (inputString.Length > 1)
{
for (int i = 0; i < inputString.Length - 1; i++)
{
char temChar = inputString[i];
int charNumber=0;
if(Alphabet.Keys.Contains(temChar))
charNumber = Alphabet[temChar];
summ += charNumber * (Math.Pow(2, i));
}
}
return summ;
}
}
How to Use the Class
Usage in a WinForm Control
Now we will show how to use this Class in a WinForm. Because it is a static
class, it does not need to be initialized. This makes the code snippet of the usage much smaller. In this case, the used control turns Green
if the Containernumber
is alright and Red
if not.
private void btnCheckContainerNumber_Click(object sender, EventArgs e)
{
string containerNumberToCheck = txtContainerNumber.Text;
bool validated= ContainerNumber.Check(containerNumberToCheck);
if (validated)
{
txtContainerNumber.BackColor = Color.Green;
}
else
{
txtContainerNumber.BackColor = Color.Red;
}
}
Usage in a WinForm DataGridView
The class can be used in a DataGridView
with no loss of performance. If we set the usage in a Cell_Formatting
event, the code will be called only if the cell is Shown/Formatting. In that case, the DataGridView
can have thousands of row
s and the performance will be the same. Actually, this is how I use the code in my project. It is in an override void because my DataGridView
s are made by a DataGridView
class but the usage is the same in a normal CellFormatting
event.
Because my DataGridView
is made by a class (and I can't be sure if the class has accomplished its code), I have to check if the DataGridView
contains the Column
in which the Containernumber
is stored.
After that, we just call our class and use the bool value to paint the cell in Red
if the Containernumber
is not valid and leave it as if the Containernumber
is Valid
.
public override void CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
DataGridView dgv = sender as DataGridView;
DataGridViewRow row = dgv.Rows[e.RowIndex];
string conNr = string.Empty;
if (dgv.Columns.Contains("ConNr"))
{
conNr = row.Cells["ConNr"].Value.ToString();
if (!ContainerNumber.Check(conNr))
row.Cells["ConNr"].Style.BackColor = Color.Red;
}
}
Beside that, if we use the code in a DataGridVew
, we would not like to show empty cells as False
/Red
. It would just make too many Red
cells. The users could get adjusted to it and just not notice "real" invalid Containernumber
s. Because of that, I made a small change in the bool method in our class:
if (containerNumber == string.Empty) return true;
if (containerNumber.Length != 11) return false;
This way, ONLY the invalid Containernumber
s will be Red
and all other cells are empty or contain a valid Containernumber
. :)
Points of Interest
This code is the first one I ever used the Math
class with Pow
, Floor
, etc. :D
History
-
Second version of the demo
- 07.05.2014 - Third version (XML documentation added)