Recently I had to build a check digit parser using the Modulo 43 specification designed for use with Health Industry Barcode Standards (HIBC).

In this post, I’ll share my implementation of a parser, and while there are some areas where I know that it can be improved, I believe that this may be a good v0.0.1 release of the parser.

Tell me what you think!

First, we have a class to represent the barcode that we are scanning:

/// <summary>
/// Represents the barcode components.
/// </summary>
public class Barcode
{
        public string Data { get; set; }
        public string Prefix { get; set; }
        public string Message { get; set; }
        public char CheckDigit { get; set; }
        public char CalculatedDigit { get; set; }
        public int Sum { get; set; }
        public int Modulo { get; set; }
        public bool IsValid { get; set; }
 }

Then we create the parser class which implements IDisposable:

using System;
using System.Collections.Generic;
using System.Linq;

/// <summary>
/// Parses barcode (128 data format) text and validate using HIBC Modulo 43 Check Digit Calculation.
/// Assumes scanned data is prefixed with one (1) leading and one (1) trailing space.
/// </summary>
public class Parser : IDisposable
{
        //Class body will go here!

       #region Implementation of IDisposable

        protected virtual void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                if (disposing)
                {
                    _data.Clear();
                }
                _disposed = true;
            }
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        #endregion
}

Add our private fields below after the class declaration, but before the IDisposable implementation:

#region Fields

        private bool _disposed;
        
        /// <summary>
        /// Dictionary repesentation of the Table of Numerical Value Assignments for Computing
        /// HIBC LIC data format Check Digit. Read only access.
        /// </summary>
        public Dictionary<char, int> Data { get { return _data; } }
        
        /// <summary>
        /// private dictionary which is populated dynamically on instantiation of the class
        /// </summary>
        private readonly Dictionary<char, int> _data = new Dictionary<char, int>();

        /// <summary>
        /// The prefix that will be appended to the message. While I haven't tested all scenarios because it was out of scope this could be ommitted
        /// in some implementations.
        /// </summary>
        private readonly string _prefix = "AC";

        /// <summary>
        /// array of possible characters from the HIBC LIC table of values.
        /// used to populate the dictionary as well as provide character access by index.
        /// </summary>
        private readonly char[] _chars = new[]
                                    {
                                        '0','1','2','3','4','5','6','7','8','9','A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q'
                                        , 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '-', '.', ' ', '$', '/', '+', '%'
                                    };
#endregion

I implemented an overloaded constructor so that a prefix can be passed to override the default.  In addition, the data dictionary is populated upon initiation. These constructors should follow the fields region, but again before the IDisposable implementations in the class.

/// <summary>
/// Default constructor
/// </summary>
 public Parser()
{
            //build data dictionary
            BuildDictionary();
 }
        
 /// <summary>
 /// Overloaded constructor which accepts an alternate prefix as a parameter
 /// </summary>
 /// <param name="prefix">string: Value that is prepended to the barcode data.</param>
 public Parser(string prefix)
{
            //override default prefix
            _prefix = prefix;

            //build data dictionary
            BuildDictionary();
 }
/// <summary>
/// Populates a dictionary collection from the character array. The position of each character in the character array also represents its
/// corresponding value in the HIBC Table of values
 /// </summary>
 private void BuildDictionary()
 {
            for (var i = 0; i < _chars.Length; i++)
            {
                _data.Add(_chars[i], i);
            }
 }

And finally, the method the performs the parsing and matching:

/// <summary>
/// Returns the HIBC LIC Check Digit.
/// </summary>
/// <param name="barcodeData">string: The scanned barcode data in string format.</param>
/// <returns>char: The check digit character that is the result of the computed barcode data.</returns>
public Barcode Parse(string barcodeData)
{
            var barcode = new Barcode();

            //string must not be null or empty.
            if (string.IsNullOrEmpty(barcodeData))
            {
                barcode.IsValid = false;
                return barcode;
            }

            //TODO: Should not assume a leading and trailing space. Also the check digit could be a space.
            //assuming a trailing space, retrieve the check digit value as second to the last
            //value in the string. This is possible because a string is a character array where
            //we can access individual characters by index.
            var checkDigit = barcodeData[barcodeData.Length - 2];
            
            //cast string to character array, eliminating spaces and check digit character
            //already retrieved. Although the string is an array of characters, casting it to the official
            //types makes available array methods that are now available in string format.
            var characters = barcodeData.ToCharArray(1, barcodeData.Length - 3);

            //extract the prefix
            var prefix = barcodeData.Substring(1, 2);

            //if the prefix is not present, or doesn't match the set value, the barcode is invalid. Abort processing.
            if(!prefix.Equals(_prefix))
            {
                barcode.Data = barcodeData;
                barcode.IsValid = false;
                return barcode;
            }

            //extract the message
            var message = new string(characters).Substring(2);

            //calculate the sum of the characters based on the dictionary of values
            //and the characters in the array.
            var total = characters.Sum(character => _data[character]);

            //calculate the modulo based on the total from above and the
            //Modulo 43 constant of 43.
            var modulo = total % 43;

            //retrieve the value of the test digit (expected check digit)
            //from the array of characters above based on the modulo value
            //which should match the index for the corresponding digit.
            var calculatedDigit = _chars[modulo];

            //assign property values to the barcode object and return as the result.
            barcode.Data = barcodeData;
            barcode.Message = message;
            barcode.Prefix = prefix;
            barcode.CheckDigit = checkDigit;
            barcode.Modulo = modulo;
            barcode.Sum = total;
            barcode.CalculatedDigit = calculatedDigit;
            barcode.IsValid = calculatedDigit.Equals(checkDigit);

            return barcode;
}

Quite naturally a test method is in order. This is obviously in a unit testing project:

[TestMethod]
 public void ReturnsCorrectCheckDigit()
{
            //Arrange
            var parser = new Parser();

            //Act
            var barcode = parser.Parse(" AC0800500Z ");

            //Assert
            Assert.IsTrue(barcode.IsValid);
}

And finally the test result:

UnitTest

So if you find yourself needing to parse a barcode based on the HIBC Modulo 43 Standard, hopefully, you will find this one useful.

Also, if you have any ideas on how the code can be improved, please comment, bash, or whatever. Happy coding!

Comments


Comments are closed