Search code examples
c#regexbarcodegs1-ai-syntaxgs1-128

GS1-128 and RegEx


I'm busy with GS1-128 and want to match scanned barcodes using RegEx. I currently have the following expression:

^(01)(12345678)(\d{5})\d(11|17)(\d{2}[0-1]\d[0-3]\d)(10|21)(\d{1,20})(30)(\d{1,20})

This succesfully matches the barcode (01)12345678123450(11)130500(21)1234567890(30)42, splitting it up into the following groups:

  1. 01 - GTIN
  2. 12345678 - company code (dummy) - 8 digits
  3. 12345 - partcode (dummy) - 5 digits
  4. 11 or 17 - Production date/expiry date
  5. 130500 - date - 6 digits
  6. 10 or 21 - batch/serial number
  7. 1234567890 - 1 to 20 characters
  8. 30 - count of items (optional)
  9. 42 - 1 to 8 characters (optional)

Now, I sometimes have a barcode that doesn't have the count of items AI; 30. I can't seem to figure out how to work this into my regex at all. Whenever I make group 8 & 9 optional, the content of these groups get thrown into group 7 for all barcodes that do contain AI 30.

How do I go about making AI 30 optional while preventing it from being grouped with AI 21/10?

Test cases:

(01)12345678654320(11)120500(21)1234567890 should give the following matches:

  1. 01
  2. 12345678
  3. 65432
  4. 11
  5. 120500
  6. 21
  7. 1234567890
  8. NO MATCH
  9. NO MATCH

(01)12345678124570(17)130700(10)30567(30)50 should give the following matches:

  1. 01
  2. 12345678
  3. 12457
  4. 17
  5. 130700
  6. 10
  7. 30567
  8. 30
  9. 50

(01)12345678888880(11)140200(21)66503042(30)100 should give the following matches:

  1. 01
  2. 12345678
  3. 88888
  4. 11
  5. 140200
  6. 21
  7. 66503042
  8. 30
  9. 100

Note that the parentheses are only to show where the AI begins, the barcode itself omits these.


Solution

  • Try this:

    ^(?<gtin>\(01\))(?<comp_code>12345678)(?<part_code>\d{5})0?(?<pd_ed>\((?:11|17)\))(?<date>\d{6})(?<bat_no>\((?:21|10)\))(?<data_req>\d{1,20}?)\b(?<count>(?:\(30\))?)(?<data_opt>(?:\d{1,8})?)$
    

    The above expression should match all the following items:

    (01)12345678654320(11)120500(21)1234567890
    (01)12345678124570(17)130700(10)30567(30)50
    (01)12345678888880(11)140200(21)66503042(30)100
    

    Explanation:

    <!--
    ^(?<gtin>\(01\))(?<comp_code>12345678)(?<part_code>\d{5})0?(?<pd_ed>\((?:11|17)\))(?<date>\d{6})(?<bat_no>\((?:21|10)\))(?<data_req>\d{1,20}?)\b(?<count>(?:\(30\))?)(?<data_opt>(?:\d{1,8})?)$
    
    Assert position at the beginning of the string «^»
    Match the regular expression below and capture its match into backreference with name “gtin” «(?<gtin>\(01\))»
       Match the character “(” literally «\(»
       Match the characters “01” literally «01»
       Match the character “)” literally «\)»
    Match the regular expression below and capture its match into backreference with name “comp_code” «(?<comp_code>12345678)»
       Match the characters “12345678” literally «12345678»
    Match the regular expression below and capture its match into backreference with name “part_code” «(?<part_code>\d{5})»
       Match a single digit 0..9 «\d{5}»
          Exactly 5 times «{5}»
    Match the character “0” literally «0?»
       Between zero and one times, as many times as possible, giving back as needed (greedy) «?»
    Match the regular expression below and capture its match into backreference with name “pd_ed” «(?<pd_ed>\((?:11|17)\))»
       Match the character “(” literally «\(»
       Match the regular expression below «(?:11|17)»
          Match either the regular expression below (attempting the next alternative only if this one fails) «11»
             Match the characters “11” literally «11»
          Or match regular expression number 2 below (the entire group fails if this one fails to match) «17»
             Match the characters “17” literally «17»
       Match the character “)” literally «\)»
    Match the regular expression below and capture its match into backreference with name “date” «(?<date>\d{6})»
       Match a single digit 0..9 «\d{6}»
          Exactly 6 times «{6}»
    Match the regular expression below and capture its match into backreference with name “bat_no” «(?<bat_no>\((?:21|10)\))»
       Match the character “(” literally «\(»
       Match the regular expression below «(?:21|10)»
          Match either the regular expression below (attempting the next alternative only if this one fails) «21»
             Match the characters “21” literally «21»
          Or match regular expression number 2 below (the entire group fails if this one fails to match) «10»
             Match the characters “10” literally «10»
       Match the character “)” literally «\)»
    Match the regular expression below and capture its match into backreference with name “data_req” «(?<data_req>\d{1,20}?)»
       Match a single digit 0..9 «\d{1,20}?»
          Between one and 20 times, as few times as possible, expanding as needed (lazy) «{1,20}?»
    Assert position at a word boundary «\b»
    Match the regular expression below and capture its match into backreference with name “count” «(?<count>(?:\(30\))?)»
       Match the regular expression below «(?:\(30\))?»
          Between zero and one times, as many times as possible, giving back as needed (greedy) «?»
          Match the character “(” literally «\(»
          Match the characters “30” literally «30»
          Match the character “)” literally «\)»
    Match the regular expression below and capture its match into backreference with name “data_opt” «(?<data_opt>(?:\d{1,8})?)»
       Match the regular expression below «(?:\d{1,8})?»
          Between zero and one times, as many times as possible, giving back as needed (greedy) «?»
          Match a single digit 0..9 «\d{1,8}»
             Between one and 8 times, as many times as possible, giving back as needed (greedy) «{1,8}»
    Assert position at the end of the string (or before the line break at the end of the string, if any) «$»
    -->
    

    EDIT

    Omitted escaped parens:

    ^(?<gtin>01)(?<comp_code>12345678)(?<part_code>\d{5})0?(?<pd_ed>(?:11|17))(?<date>\d{6})(?<bat_no>(?:21|10))(?<data_req>\d{1,20}?)\b(?<count>(?:30)?)(?<data_opt>(?:\d{1,8})?)$