The other day I was stuck in some traffic while out on a trip with my brother and dad. The freeway was moving at about 5 MPH due to an accident, and we were behind a semi-truck towing a shipping container. Naturally, with nothing better to do, we looked up shipping container information online to find out how much they cost, size, weight, etc. and ran across the ISO 6346 specification for the serial numbers found on shipping containers.
The serial number found on shipping containers follow a strict format. The first 3 characters are the owner code, and are only letters. The 4th digit is the category identifier, which at this moment the only category is “U” for freight container. The remaining 6 characters are the serial number which will only be comprised of digits. After the 10 character serial number, there is a check digit, usually shown as a number in a box to the right.
The check digit is calculated by taking each character of the serial number, and assigning a value to the character and multiplying that value by 2 to the power of its location. Digits retain their face value, 0-9, while letters are given an incrementing value starting at 10, with the exception of multiples of 11 which are skipped (a=10, b=12, c=13, d=14… z=38). All of these values are then added together, divided by 11, truncated to an integer, multiplied by 11, and then subtracted from the original total. This is basically a fancy way of saying take the modulus 11 of the total.
We passed the remainder of our time in the traffic by verifying by hand that all the shipping container check numbers were calculated correctly. When I got home, I wrote this Python program to calculate it for me the next time I may ever need to generate or check a shipping container check digit. It asks for input, then uses a regular expression to validate the format. It then calculates and returns the check digit.
#!/usr/bin/python
import re
ShippingNumber = raw_input("Enter Container Code (eg. PSSU210948): ")
if not re.compile('\D{4}\d{6}').match(ShippingNumber):
print "Invalid, please ensure code follows ISO 6346 specifications."
quit()
Total=0
for i in range(len(ShippingNumber)):
Value=ShippingNumber[i]
if not Value.isdigit():
Value=ord(Value.lower())-ord('a')+10
Value+=Value/11
Total+=int(Value)*2**i
print "Check Digit:", str(Total%11%10)
Thanks for this. What an elegant way to do it.
Thanks! I’ve made it into a javascript version. The divide by 11 wasn’t working for me so i changed to a lookup array.
function isValidContainerNumber(str){
if (str.length!=11){
return false
}
if (!str.match(/^[A-Z]{3}[UJZ]{1}[0-9]{7}$/)){
return false
}
var checkDigit = parseInt(str.substr(10,1));
var alphabetNumerical = {
‘A’ : 10, ‘B’ : 12, ‘C’ : 13, ‘D’ : 14, ‘E’ : 15, ‘F’ : 16, ‘G’ : 17, ‘H’ : 18, ‘I’ : 19,
‘J’ : 20, ‘K’ : 21, ‘L’ : 23, ‘M’ : 24, ‘N’ : 25, ‘O’ : 26, ‘P’ : 27, ‘Q’ : 28, ‘R’ : 29,
‘S’ : 30, ‘T’ : 31, ‘U’ : 32, ‘V’ : 34, ‘W’ : 35, ‘X’ : 36, ‘Y’ : 37, ‘Z’ : 38
};
var total=0
for(var i=0;i<10;i++){
var c = str[i];
var value;
if (i<4){
// a letter
value = alphabetNumerical[c];
}else{
// a digit
value = parseInt(c);
}
total+= value * (2**i);
}
var check = total%11%10;
var isValid = checkDigit==check;
return isValid;
}
function testContainerValidation(){
var valids = "MEDU2795573,CMAU0233795,FCIU6216506,HASU4336962,TCNU1825038,GESU5465561,PONU0196460,GLDU7228251,OOLU8615694,WBPU2017820,MEDU8513316,CAIU8530673,PCIU8187752,OOLU0894360,MRKU5916134,HASU1037016,MRKU9734550,CCLU8583800,CCLU8583800,TGHU0751760,SUDU8242062".split(",");
var invalids = "PETER/MCKENNA,61858379930,17692089255,15747676171,08181093913,16070816373,61859250111,29770705541,08619539951,08151764930,08144572474,P164864,MEDU2795571,CMAU0233790,FCIU6216500,HASU4336969,TCNU1825031".split(",")
for(var i in valids){
if (isValidContainerNumber(valids[i])){
console.log("valid! "+valids[i]);
}else{
console.error("invalid! "+valids[i]);
}
}
for(var i in invalids){
if (!isValidContainerNumber(invalids[i])){
//console.log("correct! "+invalids[i]);
}else{
console.error("incorrect! "+invalids[i]);
}
}
}
Thanks for sharing! I’m glad other people can enjoy the simple things in life like calculating checksums 🙂