UPC

Revision as of 19:30, 27 September 2020 by rosettacode>Gerard Schildberger (added ;Goal:, ;Task;, ;Sample input:, ;Verification; section headers, split the UPC-A bar code structure into separate bullet points, added whitespace.)

Convert UPC bar codes to decimal.

UPC is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.
Goal


Specifically:

The UPC standard is actually a collection of standards -- physical standards, data format standards, product reference standards...

Here,   in this task,   we will focus on some of the data format standards,   with an imaginary physical+electrical implementation which converts physical UPC bar codes to ASCII   (with spaces and   #   characters representing the presence or absence of ink).


Sample input

Below, we have a representation of ten different UPC-A bar codes read by our imaginary bar code reader:

         # #   # ##  #  ## #   ## ### ## ### ## #### # # # ## ##  #   #  ##  ## ###  # ##  ## ### #  # #       
        # # #   ##   ## # #### #   # ## #   ## #   ## # # # ###  # ###  ##  ## ###  # #  ### ###  # # #         
         # #    # # #  ###  #   #    # #  #   #    # # # # ## #   ## #   ## #   ##   # # #### ### ## # #         
       # # ##  ## ##  ##   #  #   #  # ###  # ##  ## # # #   ## ##  #  ### ## ## #   # #### ## #   # #        
         # # ### ## #   ## ## ###  ##  # ##   #   # ## # # ### #  ## ##  #    # ### #  ## ##  #      # #          
          # #  #   # ##  ##  #   #   #  # ##  ##  #   # # # # #### #  ##  # #### #### # #  ##  # #### # #         
         # #  #  ##  ##  # #   ## ##   # ### ## ##   # # # #  #   #   #  #  ### # #    ###  # #  #   # #        
        # # #    # ##  ##   #  # ##  ##  ### #   #  # # # ### ## ## ### ## ### ### ## #  ##  ### ## # #         
         # # ### ##   ## # # #### #   ## # #### # #### # # #   #  # ###  #    # ###  # #    # ###  # # #       
        # # # #### ##   # #### # #   ## ## ### #### # # # #  ### # ###  ###  # # ###  #    # #  ### # #         

Some of these were entered upside down,   and one entry has a timing error.


Task

Implement code to find the corresponding decimal representation of each, rejecting the error.

Extra credit for handling the rows entered upside down   (the other option is to reject them).


Notes

Each digit is represented by 7 bits:

    0:  0 0 0 1 1 0 1
    1:  0 0 1 1 0 0 1
    2:  0 0 1 0 0 1 1
    3:  0 1 1 1 1 0 1
    4:  0 1 0 0 0 1 1
    5:  0 1 1 0 0 0 1
    6:  0 1 0 1 1 1 1
    7:  0 1 1 1 0 1 1
    8:  0 1 1 0 1 1 1
    9:  0 0 0 1 0 1 1

On the left hand side of the bar code a space represents a 0 and a # represents a 1.
On the right hand side of the bar code, a # represents a 0 and a space represents a 1
Alternatively (for the above):   spaces always represent zeros and # characters always represent ones, but the representation is logically negated -- 1s and 0s are flipped -- on the right hand side of the bar code.


The UPC-A bar code structure
  •   It begins with at least 9 spaces   (which our imaginary bar code reader unfortunately doesn't always reproduce properly),
  •   then has a     # #     sequence marking the start of the sequence,
  •   then has the six "left hand" digits,
  •   then has a   # #   sequence in the middle,
  •   then has the six "right hand digits",
  •   then has another   # #   (end sequence),   and finally,
  •   then ends with nine trailing spaces   (which might be eaten by wiki edits and in any event were not quite captured correctly by our imaginary bar code reader).


Finally, the last digit is a checksum digit which may be used to help detect errors.


Verification

Multiply each digit in the represented 12 digit sequence by the corresponding number in   (3,1,3,1,3,1,3,1,3,1,3,1)   and add the products.

The sum (mod 10) must be 0   (must have a zero as its last digit)   if the UPC number has been read correctly.

AWK

<lang AWK>

  1. syntax: GAWK -f UPC.AWK

BEGIN {

   ls_arr["   ## #"] = 0
   ls_arr["  ##  #"] = 1
   ls_arr["  #  ##"] = 2
   ls_arr[" #### #"] = 3
   ls_arr[" #   ##"] = 4
   ls_arr[" ##   #"] = 5
   ls_arr[" # ####"] = 6
   ls_arr[" ### ##"] = 7
   ls_arr[" ## ###"] = 8
   ls_arr["   # ##"] = 9
   for (i in ls_arr) {
     tmp = i
     gsub(/#/,"x",tmp)
     gsub(/ /,"#",tmp)
     gsub(/x/," ",tmp)
     rs_arr[tmp] = ls_arr[i]
   }
   split("3,1,3,1,3,1,3,1,3,1,3,1",weight_arr,",")
   bc_arr[++n] = "         # #   # ##  #  ## #   ## ### ## ### ## #### # # # ## ##  #   #  ##  ## ###  # ##  ## ### #  # #       "
   bc_arr[++n] = "        # # #   ##   ## # #### #   # ## #   ## #   ## # # # ###  # ###  ##  ## ###  # #  ### ###  # # #         "
   bc_arr[++n] = "         # #    # # #  ###  #   #    # #  #   #    # # # # ## #   ## #   ## #   ##   # # #### ### ## # #         "
   bc_arr[++n] = "       # # ##  ## ##  ##   #  #   #  # ###  # ##  ## # # #   ## ##  #  ### ## ## #   # #### ## #   # #        "
   bc_arr[++n] = "         # # ### ## #   ## ## ###  ##  # ##   #   # ## # # ### #  ## ##  #    # ### #  ## ##  #      # #          "
   bc_arr[++n] = "          # #  #   # ##  ##  #   #   #  # ##  ##  #   # # # # #### #  ##  # #### #### # #  ##  # #### # #         "
   bc_arr[++n] = "         # #  #  ##  ##  # #   ## ##   # ### ## ##   # # # #  #   #   #  #  ### # #    ###  # #  #   # #        "
   bc_arr[++n] = "        # # #    # ##  ##   #  # ##  ##  ### #   #  # # # ### ## ## ### ## ### ### ## #  ##  ### ## # #         "
   bc_arr[++n] = "         # # ### ##   ## # # #### #   ## # #### # #### # # #   #  # ###  #    # ###  # #    # ###  # # #       "
   bc_arr[++n] = "        # # # #### ##   # #### # #   ## ## ### #### # # # #  ### # ###  ###  # # ###  #    # #  ### # #         "
   bc_arr[++n] = "        # # # #### ##   # #### # #   ## ## ### #### # # # #  ### # ###  ###  # # ###  #    # #  ### #" # NG
   tmp = "123456712345671234567123456712345671234567"
   printf("%2s: %-18s---%s-----%s---\n","N","UPC-A",tmp,tmp) # heading
   for (i=1; i<=n; i++) {
  1. accept any number of spaces at beginning or end; I.E. minimum of 9 is not enforced
     sub(/^ +/,"",bc_arr[i])
     sub(/ +$/,"",bc_arr[i])
     bc = bc_arr[i]
     if (length(bc) == 95 && substr(bc,1,3) == "# #" && substr(bc,46,5) == " # # " && substr(bc,93,3) == "# #") {
       upc = ""
       sum = upc_build(ls_arr,1,substr(bc,4,42))
       sum += upc_build(rs_arr,7,substr(bc,51,42))
       if (upc ~ /^x+$/) { msg = "reversed" }
       else if (upc ~ /x/) { msg = "invalid digit(s)" }
       else if (sum % 10 != 0) { msg = "bad check digit" }
       else { msg = upc }
     }
     else {
       msg = "invalid format"
     }
     printf("%2d: %-18s%s\n",i,msg,bc)
   }
   exit(0)

} function upc_build(arr,pos,bc, i,s,sum) {

   pos--
   for (i=1; i<=42; i+=7) {
     s = substr(bc,i,7)
     pos++
     if (s in arr) {
       upc = upc arr[s]
       sum += arr[s] * weight_arr[pos]
     }
     else {
       upc = upc "x"
     }
   }
   return(sum)

} </lang>

Output:
 N: UPC-A             ---123456712345671234567123456712345671234567-----123456712345671234567123456712345671234567---
 1: 924773271019      # #   # ##  #  ## #   ## ### ## ### ## #### # # # ## ##  #   #  ##  ## ###  # ##  ## ### #  # #
 2: 403944441050      # # #   ##   ## # #### #   # ## #   ## #   ## # # # ###  # ###  ##  ## ###  # #  ### ###  # # #
 3: reversed          # #    # # #  ###  #   #    # #  #   #    # # # # ## #   ## #   ## #   ##   # # #### ### ## # #
 4: reversed          # # ##  ## ##  ##   #  #   #  # ###  # ##  ## # # #   ## ##  #  ### ## ## #   # #### ## #   # #
 5: invalid digit(s)  # # ### ## #   ## ## ###  ##  # ##   #   # ## # # ### #  ## ##  #    # ### #  ## ##  #      # #
 6: reversed          # #  #   # ##  ##  #   #   #  # ##  ##  #   # # # # #### #  ##  # #### #### # #  ##  # #### # #
 7: 214575875608      # #  #  ##  ##  # #   ## ##   # ### ## ##   # # # #  #   #   #  #  ### # #    ###  # #  #   # #
 8: reversed          # # #    # ##  ##   #  # ##  ##  ### #   #  # # # ### ## ## ### ## ### ### ## #  ##  ### ## # #
 9: 706466743030      # # ### ##   ## # # #### #   ## # #### # #### # # #   #  # ###  #    # ###  # #    # ###  # # #
10: 653483540435      # # # #### ##   # #### # #   ## ## ### #### # # # #  ### # ###  ###  # # ###  #    # #  ### # #
11: invalid format    # # # #### ##   # #### # #   ## ## ### #### # # # #  ### # ###  ###  # # ###  #    # #  ### #

C

<lang c>#include <stdbool.h>

  1. include <stdio.h>
  2. include <stdlib.h>
  3. include <string.h>

typedef char const *const string;

bool consume_sentinal(bool middle, string s, size_t *pos) {

   if (middle) {
       if (s[*pos] == ' ' && s[*pos + 1] == '#' && s[*pos + 2] == ' ' && s[*pos + 3] == '#' && s[*pos + 4] == ' ') {
           *pos += 5;
           return true;
       }
   } else {
       if (s[*pos] == '#' && s[*pos + 1] == ' ' && s[*pos + 2] == '#') {
           *pos += 3;
           return true;
       }
   }
   return false;

}

int consume_digit(bool right, string s, size_t *pos) {

   const char zero = right ? '#' : ' ';
   const char one = right ? ' ' : '#';
   size_t i = *pos;
   int result = -1;
   if (s[i] == zero) {
       if (s[i + 1] == zero) {
           if (s[i + 2] == zero) {
               if (s[i + 3] == one) {
                   if (s[i + 4] == zero) {
                       if (s[i + 5] == one && s[i + 6] == one) {
                           result = 9;
                       }
                   } else if (s[i + 4] == one) {
                       if (s[i + 5] == zero && s[i + 6] == one) {
                           result = 0;
                       }
                   }
               }
           } else if (s[i + 2] == one) {
               if (s[i + 3] == zero) {
                   if (s[i + 4] == zero && s[i + 5] == one && s[i + 6] == one) {
                       result = 2;
                   }
               } else if (s[i + 3] == one) {
                   if (s[i + 4] == zero && s[i + 5] == zero && s[i + 6] == one) {
                       result = 1;
                   }
               }
           }
       } else if (s[i + 1] == one) {
           if (s[i + 2] == zero) {
               if (s[i + 3] == zero) {
                   if (s[i + 4] == zero && s[i + 5] == one && s[i + 6] == one) {
                       result = 4;
                   }
               } else if (s[i + 3] == one) {
                   if (s[i + 4] == one && s[i + 5] == one && s[i + 6] == one) {
                       result = 6;
                   }
               }
           } else if (s[i + 2] == one) {
               if (s[i + 3] == zero) {
                   if (s[i + 4] == zero) {
                       if (s[i + 5] == zero && s[i + 6] == one) {
                           result = 5;
                       }
                   } else if (s[i + 4] == one) {
                       if (s[i + 5] == one && s[i + 6] == one) {
                           result = 8;
                       }
                   }
               } else if (s[i + 3] == one) {
                   if (s[i + 4] == zero) {
                       if (s[i + 5] == one && s[i + 6] == one) {
                           result = 7;
                       }
                   } else if (s[i + 4] == one) {
                       if (s[i + 5] == zero && s[i + 6] == one) {
                           result = 3;
                       }
                   }
               }
           }
       }
   }
   if (result >= 0) {
       *pos += 7;
   }
   return result;

}

bool decode_upc(string src, char *buffer) {

   const int one = 1;
   const int three = 3;
   size_t pos = 0;
   int sum = 0;
   int digit;
   //1) 9 spaces (unreliable)
   while (src[pos] != '#') {
       if (src[pos] == 0) {
           return false;
       }
       pos++;
   }
   //2) Start "# #"
   if (!consume_sentinal(false, src, &pos)) {
       return false;
   }
   //3) 6 left-hand digits (space is zero and hash is one)
   digit = consume_digit(false, src, &pos);
   if (digit < 0) {
       return false;
   }
   sum += three * digit;
   *buffer++ = digit + '0';
   *buffer++ = ' ';
   digit = consume_digit(false, src, &pos);
   if (digit < 0) {
       return false;
   }
   sum += one * digit;
   *buffer++ = digit + '0';
   *buffer++ = ' ';
   digit = consume_digit(false, src, &pos);
   if (digit < 0) {
       return false;
   }
   sum += three * digit;
   *buffer++ = digit + '0';
   *buffer++ = ' ';
   digit = consume_digit(false, src, &pos);
   if (digit < 0) {
       return false;
   }
   sum += one * digit;
   *buffer++ = digit + '0';
   *buffer++ = ' ';
   digit = consume_digit(false, src, &pos);
   if (digit < 0) {
       return false;
   }
   sum += three * digit;
   *buffer++ = digit + '0';
   *buffer++ = ' ';
   digit = consume_digit(false, src, &pos);
   if (digit < 0) {
       return false;
   }
   sum += one * digit;
   *buffer++ = digit + '0';
   *buffer++ = ' ';
   //4) Middle "# #"
   if (!consume_sentinal(true, src, &pos)) {
       return false;
   }
   //5) 6 right-hand digits (hash is zero and space is one)
   digit = consume_digit(true, src, &pos);
   if (digit < 0) {
       return false;
   }
   sum += three * digit;
   *buffer++ = digit + '0';
   *buffer++ = ' ';
   digit = consume_digit(true, src, &pos);
   if (digit < 0) {
       return false;
   }
   sum += one * digit;
   *buffer++ = digit + '0';
   *buffer++ = ' ';
   digit = consume_digit(true, src, &pos);
   if (digit < 0) {
       return false;
   }
   sum += three * digit;
   *buffer++ = digit + '0';
   *buffer++ = ' ';
   digit = consume_digit(true, src, &pos);
   if (digit < 0) {
       return false;
   }
   sum += one * digit;
   *buffer++ = digit + '0';
   *buffer++ = ' ';
   digit = consume_digit(true, src, &pos);
   if (digit < 0) {
       return false;
   }
   sum += three * digit;
   *buffer++ = digit + '0';
   *buffer++ = ' ';
   digit = consume_digit(true, src, &pos);
   if (digit < 0) {
       return false;
   }
   sum += one * digit;
   *buffer++ = digit + '0';
   *buffer++ = ' ';
   //6) Final "# #"
   if (!consume_sentinal(false, src, &pos)) {
       return false;
   }
   //7) 9 spaces (unreliable)
   // skip
   //8) the dot product of the number and (3, 1)+ sequence mod 10 must be zero
   return sum % 10 == 0;

}

void test(string src) {

   char buffer[24];
   if (decode_upc(src, buffer)) {
       buffer[22] = 0;
       printf("%sValid\n", buffer);
   } else {
       size_t len = strlen(src);
       char *rev = malloc(len + 1);
       size_t i;
       if (rev == NULL) {
           exit(1);
       }
       for (i = 0; i < len; i++) {
           rev[i] = src[len - i - 1];
       }
  1. pragma warning(push)
  2. pragma warning(disable : 6386)
       // if len + 1 bytes are allocated, and len bytes are writable, there is no buffer overrun
       rev[len] = 0;
  1. pragma warning(pop)
       if (decode_upc(rev, buffer)) {
           buffer[22] = 0;
           printf("%sValid (upside down)\n", buffer);
       } else {
           printf("Invalid digit(s)\n");
       }
       free(rev);
   }

}

int main() {

   int num = 0;
   printf("%2d: ", ++num);
   test("         # #   # ##  #  ## #   ## ### ## ### ## #### # # # ## ##  #   #  ##  ## ###  # ##  ## ### #  # #       ");
   printf("%2d: ", ++num);
   test("        # # #   ##   ## # #### #   # ## #   ## #   ## # # # ###  # ###  ##  ## ###  # #  ### ###  # # #         ");
   printf("%2d: ", ++num);
   test("         # #    # # #  ###  #   #    # #  #   #    # # # # ## #   ## #   ## #   ##   # # #### ### ## # #         ");
   printf("%2d: ", ++num);
   test("       # # ##  ## ##  ##   #  #   #  # ###  # ##  ## # # #   ## ##  #  ### ## ## #   # #### ## #   # #        ");
   printf("%2d: ", ++num);
   test("         # # ### ## #   ## ## ###  ##  # ##   #   # ## # # ### #  ## ##  #    # ### #  ## ##  #      # #          ");
   printf("%2d: ", ++num);
   test("          # #  #   # ##  ##  #   #   #  # ##  ##  #   # # # # #### #  ##  # #### #### # #  ##  # #### # #         ");
   printf("%2d: ", ++num);
   test("         # #  #  ##  ##  # #   ## ##   # ### ## ##   # # # #  #   #   #  #  ### # #    ###  # #  #   # #        ");
   printf("%2d: ", ++num);
   test("        # # #    # ##  ##   #  # ##  ##  ### #   #  # # # ### ## ## ### ## ### ### ## #  ##  ### ## # #         ");
   printf("%2d: ", ++num);
   test("         # # ### ##   ## # # #### #   ## # #### # #### # # #   #  # ###  #    # ###  # #    # ###  # # #       ");
   printf("%2d: ", ++num);
   test("        # # # #### ##   # #### # #   ## ## ### #### # # # #  ### # ###  ###  # # ###  #    # #  ### # #         ");
   return 0;

}</lang>

Output:
 1: 9 2 4 7 7 3 2 7 1 0 1 Valid
 2: 4 0 3 9 4 4 4 4 1 0 5 Valid
 3: 8 3 4 9 9 9 6 7 6 7 0 Valid (upside down)
 4: 9 3 9 8 2 5 1 5 8 8 1 Valid (upside down)
 5: Invalid digit(s)
 6: 3 1 6 3 1 3 7 1 8 7 1 Valid (upside down)
 7: 2 1 4 5 7 5 8 7 5 6 0 Valid
 8: 8 1 8 7 7 8 8 4 1 8 1 Valid (upside down)
 9: 7 0 6 4 6 6 7 4 3 0 3 Valid
10: 6 5 3 4 8 3 5 4 0 4 3 Valid

Factor

Works with: Factor version 0.99 2019-10-06

<lang factor>USING: combinators combinators.short-circuit formatting grouping kernel locals math math.vectors sequences sequences.repeating unicode ;

CONSTANT: numbers {

   "   ## #"
   "  ##  #"
   "  #  ##"
   " #### #"
   " #   ##"
   " ##   #"
   " # ####"
   " ### ##"
   " ## ###"
   "   # ##"

}

upc>dec ( str -- seq )
   [ blank? ] trim 3 tail 3 head* 42 cut 5 tail
   [ 35 = 32 35 ? ] map append 7 group [ numbers index ] map ;
valid-digits? ( seq -- ? ) [ f = ] none? ;
valid-checksum? ( seq -- ? )
   { 3 1 } 12 cycle v* sum 10 mod zero? ;
valid-upc? ( seq -- ? )
   { [ valid-digits? ] [ valid-checksum? ] } 1&& ;
process-upc ( upc -- obj upside-down? )
   upc upc>dec :> d
   {
       { [ d valid-upc? ] [ d f ] }
       { [ upc reverse upc>dec dup valid-upc? ] [ t ] }
       { [ drop d valid-digits? ] [ "Invalid checksum" f ] }
       [ "Invalid digit(s)" f ]
   } cond ;

{

"         # #   # ##  #  ## #   ## ### ## ### ## #### # # # ## ##  #   #  ##  ## ###  # ##  ## ### #  # #       "
 "        # # #   ##   ## # #### #   # ## #   ## #   ## # # # ###  # ###  ##  ## ###  # #  ### ###  # # #      "
"         # #    # # #  ###  #   #    # #  #   #    # # # # ## #   ## #   ## #   ##   # # #### ### ## # #       "
  "       # # ##  ## ##  ##   #  #   #  # ###  # ##  ## # # #   ## ##  #  ### ## ## #   # #### ## #   # #        "
"         # # ### ## #   ## ## ###  ##  # ##   #   # ## # # ### #  ## ##  #    # ### #  ## ##  #      # #       "

" # # # # ## ## # # # # ## ## # # # # # #### # ## # #### #### # # ## # #### # # "

"         # #  #  ##  ##  # #   ## ##   # ### ## ##   # # # #  #   #   #  #  ### # #    ###  # #  #   # #     "
 "        # # #    # ##  ##   #  # ##  ##  ### #   #  # # # ### ## ## ### ## ### ### ## #  ##  ### ## # #      "
"         # # ### ##   ## # # #### #   ## # #### # #### # # #   #  # ###  #    # ###  # #    # ###  # # #       "
 "        # # # #### ##   # #### # #   ## ## ### #### # # # #  ### # ###  ###  # # ###  #    # #  ### # #      "

} [ process-upc "(upside down)" "" ? "%u %s\n" printf ] each</lang>

Output:
{ 9 2 4 7 7 3 2 7 1 0 1 9 }
{ 4 0 3 9 4 4 4 4 1 0 5 0 }
{ 8 3 4 9 9 9 6 7 6 7 0 6 } (upside down)
{ 9 3 9 8 2 5 1 5 8 8 1 1 } (upside down)
"Invalid digit(s)" 
{ 3 1 6 3 1 3 7 1 8 7 1 7 } (upside down)
{ 2 1 4 5 7 5 8 7 5 6 0 8 }
{ 8 1 8 7 7 8 8 4 1 8 1 3 } (upside down)
{ 7 0 6 4 6 6 7 4 3 0 3 0 }
{ 6 5 3 4 8 3 5 4 0 4 3 5 }

Go

<lang go>package main

import (

   "fmt"
   "regexp"

)

var bits = []string{

   "0 0 0 1 1 0 1 ",
   "0 0 1 1 0 0 1 ",
   "0 0 1 0 0 1 1 ",
   "0 1 1 1 1 0 1 ",
   "0 1 0 0 0 1 1 ",
   "0 1 1 0 0 0 1 ",
   "0 1 0 1 1 1 1 ",
   "0 1 1 1 0 1 1 ",
   "0 1 1 0 1 1 1 ",
   "0 0 0 1 0 1 1 ",

}

var (

   lhs = make(map[string]int)
   rhs = make(map[string]int)

)

var weights = []int{3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1}

const (

   s = "# #"
   m = " # # "
   e = "# #"
   d = "(?:#| ){7}"

)

func init() {

   for i := 0; i <= 9; i++ {
       lt := make([]byte, 7)
       rt := make([]byte, 7)
       for j := 0; j < 14; j += 2 {
           if bits[i][j] == '1' {
               lt[j/2] = '#'
               rt[j/2] = ' '
           } else {
               lt[j/2] = ' '
               rt[j/2] = '#'
           }
       }
       lhs[string(lt)] = i
       rhs[string(rt)] = i
   }

}

func reverse(s string) string {

   b := []byte(s)
   for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 {
       b[i], b[j] = b[j], b[i]
   }
   return string(b)

}

func main() {

   barcodes := []string{
       "         # #   # ##  #  ## #   ## ### ## ### ## #### # # # ## ##  #   #  ##  ## ###  # ##  ## ### #  # #       ",
       "        # # #   ##   ## # #### #   # ## #   ## #   ## # # # ###  # ###  ##  ## ###  # #  ### ###  # # #         ",
       "         # #    # # #  ###  #   #    # #  #   #    # # # # ## #   ## #   ## #   ##   # # #### ### ## # #         ",
       "       # # ##  ## ##  ##   #  #   #  # ###  # ##  ## # # #   ## ##  #  ### ## ## #   # #### ## #   # #        ",
       "         # # ### ## #   ## ## ###  ##  # ##   #   # ## # # ### #  ## ##  #    # ### #  ## ##  #      # #          ",
       "          # #  #   # ##  ##  #   #   #  # ##  ##  #   # # # # #### #  ##  # #### #### # #  ##  # #### # #         ",
       "         # #  #  ##  ##  # #   ## ##   # ### ## ##   # # # #  #   #   #  #  ### # #    ###  # #  #   # #        ",
       "        # # #    # ##  ##   #  # ##  ##  ### #   #  # # # ### ## ## ### ## ### ### ## #  ##  ### ## # #         ",
       "         # # ### ##   ## # # #### #   ## # #### # #### # # #   #  # ###  #    # ###  # #    # ###  # # #       ",
       "        # # # #### ##   # #### # #   ## ## ### #### # # # #  ### # ###  ###  # # ###  #    # #  ### # #         ",
   }
   // Regular expression to check validity of a barcode and extract digits. However we accept any number
   // of spaces at the beginning or end i.e. we don't enforce a minimum of 9.
   expr := fmt.Sprintf(`^\s*%s(%s)(%s)(%s)(%s)(%s)(%s)%s(%s)(%s)(%s)(%s)(%s)(%s)%s\s*$`,
       s, d, d, d, d, d, d, m, d, d, d, d, d, d, e)
   rx := regexp.MustCompile(expr)
   fmt.Println("UPC-A barcodes:")
   for i, bc := range barcodes {
       for j := 0; j <= 1; j++ {
           if !rx.MatchString(bc) {
               fmt.Printf("%2d: Invalid format\n", i+1)
               break
           }
           codes := rx.FindStringSubmatch(bc)
           digits := make([]int, 12)
           var invalid, ok bool // False by default.
           for i := 1; i <= 6; i++ {
               digits[i-1], ok = lhs[codes[i]]
               if !ok {
                   invalid = true
               }
               digits[i+5], ok = rhs[codes[i+6]]
               if !ok {
                   invalid = true
               }
           }
           if invalid { // Contains at least one invalid digit.
               if j == 0 { // Try reversing.
                   bc = reverse(bc)
                   continue
               } else {
                   fmt.Printf("%2d: Invalid digit(s)\n", i+1)
                   break
               }
           }
           sum := 0
           for i, d := range digits {
               sum += weights[i] * d
           }
           if sum%10 != 0 {
               fmt.Printf("%2d: Checksum error\n", i+1)
               break
           } else {
               ud := ""
               if j == 1 {
                   ud = "(upside down)"
               }
               fmt.Printf("%2d: %v %s\n", i+1, digits, ud)
               break
           }
       }
   }

}</lang>

Output:
UPC-A barcodes:
 1: [9 2 4 7 7 3 2 7 1 0 1 9] 
 2: [4 0 3 9 4 4 4 4 1 0 5 0] 
 3: [8 3 4 9 9 9 6 7 6 7 0 6] (upside down)
 4: [9 3 9 8 2 5 1 5 8 8 1 1] (upside down)
 5: Invalid digit(s)
 6: [3 1 6 3 1 3 7 1 8 7 1 7] (upside down)
 7: [2 1 4 5 7 5 8 7 5 6 0 8] 
 8: [8 1 8 7 7 8 8 4 1 8 1 3] (upside down)
 9: [7 0 6 4 6 6 7 4 3 0 3 0] 
10: [6 5 3 4 8 3 5 4 0 4 3 5] 

J

Implementation:

<lang J>upcdigit=:".;._2]0 :0

 0 0 0 1 1 0 1 NB. 0
 0 0 1 1 0 0 1 NB. 1
 0 0 1 0 0 1 1 NB. 2
 0 1 1 1 1 0 1 NB. 3
 0 1 0 0 0 1 1 NB. 4
 0 1 1 0 0 0 1 NB. 5
 0 1 0 1 1 1 1 NB. 6
 0 1 1 1 0 1 1 NB. 7
 0 1 1 0 1 1 1 NB. 8
 0 0 0 1 0 1 1 NB. 9

)

upc2dec=:3 :0

 if. 95~: #code=. '#'=dtb dlb y do._ return.end.
 if. (11$1 0) ~: 0 1 2 45 46 47 48 49 92 93 94{ code do._ return. end.
 digits=. <./([:,upcdigit i.0 1~:(3 50+/i.6 7) {  ])"1 code,:|.code
 if. 10 e.digits do._ return.end.
 if.0 ~:10|digits+/ .* 12$3 1 do._ return.end.

)</lang>

Here, we perform some basic integrity checks and use a table lookup to identify the decimal digits.

Task example:

<lang J>barcodes=:0 :0

        # #   # ##  #  ## #   ## ### ## ### ## #### # # # ## ##  #   #  ##  ## ###  # ##  ## ### #  # #       
       # # #   ##   ## # #### #   # ## #   ## #   ## # # # ###  # ###  ##  ## ###  # #  ### ###  # # #         
        # #    # # #  ###  #   #    # #  #   #    # # # # ## #   ## #   ## #   ##   # # #### ### ## # #         
      # # ##  ## ##  ##   #  #   #  # ###  # ##  ## # # #   ## ##  #  ### ## ## #   # #### ## #   # #        
        # # ### ## #   ## ## ###  ##  # ##   #   # ## # # ### #  ## ##  #    # ### #  ## ##  #      # #          
         # #  #   # ##  ##  #   #   #  # ##  ##  #   # # # # #### #  ##  # #### #### # #  ##  # #### # #         
        # #  #  ##  ##  # #   ## ##   # ### ## ##   # # # #  #   #   #  #  ### # #    ###  # #  #   # #        
       # # #    # ##  ##   #  # ##  ##  ### #   #  # # # ### ## ## ### ## ### ### ## #  ##  ### ## # #         
        # # ### ##   ## # # #### #   ## # #### # #### # # #   #  # ###  #    # ###  # #    # ###  # # #       
       # # # #### ##   # #### # #   ## ## ### #### # # # #  ### # ###  ###  # # ###  #    # #  ### # #         

)

  upc2dec;._2 barcodes

9 2 4 7 7 3 2 7 1 0 1 9 4 0 3 9 4 4 4 4 1 0 5 0 8 3 4 9 9 9 6 7 6 7 0 6 9 3 9 8 2 5 1 5 8 8 1 1 _ 0 0 0 0 0 0 0 0 0 0 0 3 1 6 3 1 3 7 1 8 7 1 7 2 1 4 5 7 5 8 7 5 6 0 8 8 1 8 7 7 8 8 4 1 8 1 3 7 0 6 4 6 6 7 4 3 0 3 0 6 5 3 4 8 3 5 4 0 4 3 5 </lang>

The row which begins with _ is the damaged row. (If rescanning did not fix that problem, the operator would have to enter the code manually.)

It may be desirable to format the result differently, but that's currently not a part of the task definition.

Julia

Translation of: Raku

<lang julia>const pat1 = (" ## #", " ## #", " # ##", " #### #", " # ##",

             " ##   #", " # ####", " ### ##", " ## ###", "   # ##")

const pat2 = [replace(x, r"[# ]" => (x) -> x == " " ? "#" : " ") for x in pat1] const ptod1 = Dict((b => a - 1) for (a, b) in enumerate(pat1)) const ptod2 = Dict((b => a - 1) for (a, b) in enumerate(pat2)) const reg = Regex("^\\s*# #\\s*((?:" * join(pat1, "|") *

                 "){6})\\s*# #\\s*((?:" * join(pat2, "|") * "){6})\\s*# #\\s*")

const lines = [

"         # #   # ##  #  ## #   ## ### ## ### ## #### # # # ## ##  #   #  ##  ## ###  # ##  ## ### #  # #       ",
 "        # # #   ##   ## # #### #   # ## #   ## #   ## # # # ###  # ###  ##  ## ###  # #  ### ###  # # #      ",
"         # #    # # #  ###  #   #    # #  #   #    # # # # ## #   ## #   ## #   ##   # # #### ### ## # #       ",
  "       # # ##  ## ##  ##   #  #   #  # ###  # ##  ## # # #   ## ##  #  ### ## ## #   # #### ## #   # #        ",
"         # # ### ## #   ## ## ###  ##  # ##   #   # ## # # ### #  ## ##  #    # ### #  ## ##  #      # #       ",

" # # # # ## ## # # # # ## ## # # # # # #### # ## # #### #### # # ## # #### # # ",

"         # #  #  ##  ##  # #   ## ##   # ### ## ##   # # # #  #   #   #  #  ### # #    ###  # #  #   # #     ",
 "        # # #    # ##  ##   #  # ##  ##  ### #   #  # # # ### ## ## ### ## ### ### ## #  ##  ### ## # #      ",
"         # # ### ##   ## # # #### #   ## # #### # #### # # #   #  # ###  #    # ###  # #    # ###  # # #       ",
 "        # # # #### ##   # #### # #   ## ## ### #### # # # #  ### # ###  ###  # # ###  #    # #  ### # #      ",

]

function decodeUPC(line)

   if (m = match(reg, line)) != nothing
       mats, dig = filter(!isempty, m.captures), Int[]
       for mat in mats
           append!(dig, [ptod1[x.match] for x in eachmatch(r"(.......)", mat)
               if haskey(ptod1, x.match)])
           append!(dig, [ptod2[x.match] for x in eachmatch(r"(.......)", mat)
               if haskey(ptod2, x.match)])
       end
       dsum = sum([(isodd(i) ? 3 : 1) * n for (i, n) in enumerate(dig)])
       (dsum % 10 == 0) && return prod(string.(dig))
   end
   return ""

end

for line in lines

   println((s = decodeUPC(line)) != "" ? s :
           (s = decodeUPC(reverse(line))) != "" ? s : "Invalid")

end

</lang>

Output:
924773271019
403944441050
834999676706
939825158811
Invalid
316313718717
214575875608
818778841813
706466743030
653483540435

Perl

Translation of: Raku

<lang perl>use strict; use warnings; use feature 'say';

sub decode_UPC {

   my($line) = @_;
   my(%pattern_to_digit_1,%pattern_to_digit_2,@patterns1,@patterns2,@digits,$sum);
   for my $p ('   ## #', '  ##  #', '  #  ##', ' #### #', ' #   ##', ' ##   #', ' # ####', ' ### ##', ' ## ###', '   # ##') {
       push @patterns1, $p;
       push @patterns2, $p =~ tr/# / #/r;
   }
   $pattern_to_digit_1{$patterns1[$_]} = $_ for 0..$#patterns1;
   $pattern_to_digit_2{$patterns2[$_]} = $_ for 0..$#patterns2;
    my $re = '\s*# #\s*' .
             "(?<match1>(?:@{[join '|', @patterns1]}){6})" .
             '\s*# #\s*' .
             "(?<match2>(?:@{[join '|', @patterns2]}){6})" .
             '\s*# #\s*';
    $line =~ /^$re$/g || return;
   my($match1,$match2) = ($+{match1}, $+{match2});
   push @digits, $pattern_to_digit_1{$_} for $match1 =~ /(.{7})/g;
   push @digits, $pattern_to_digit_2{$_} for $match2 =~ /(.{7})/g;
   $sum += (3,1)[$_%2] * $digits[$_] for 0..11;
   $sum % 10 ?  : join , @digits;

}

my @lines = (

   '         # #   # ##  #  ## #   ## ### ## ### ## #### # # # ## ##  #   #  ##  ## ###  # ##  ## ### #  # #       ',
    '        # # #   ##   ## # #### #   # ## #   ## #   ## # # # ###  # ###  ##  ## ###  # #  ### ###  # # #      ',
   '         # #    # # #  ###  #   #    # #  #   #    # # # # ## #   ## #   ## #   ##   # # #### ### ## # #       ',
     '       # # ##  ## ##  ##   #  #   #  # ###  # ##  ## # # #   ## ##  #  ### ## ## #   # #### ## #   # #        ',
   '         # # ### ## #   ## ## ###  ##  # ##   #   # ## # # ### #  ## ##  #    # ### #  ## ##  #      # #       ',
  '          # #  #   # ##  ##  #   #   #  # ##  ##  #   # # # # #### #  ##  # #### #### # #  ##  # #### # #    ',
   '         # #  #  ##  ##  # #   ## ##   # ### ## ##   # # # #  #   #   #  #  ### # #    ###  # #  #   # #     ',
    '        # # #    # ##  ##   #  # ##  ##  ### #   #  # # # ### ## ## ### ## ### ### ## #  ##  ### ## # #      ',
   '         # # ### ##   ## # # #### #   ## # #### # #### # # #   #  # ###  #    # ###  # #    # ###  # # #       ',
    '        # # # #### ##   # #### # #   ## ## ### #### # # # #  ### # ###  ###  # # ###  #    # #  ### # #      ',

);

for my $line (@lines) {

   say decode_UPC($line)
    // decode_UPC(join , reverse split , $line)
    // 'Invalid';

} </lang>

Output:
924773271019
403944441050
834999676706
939825158811
Invalid
316313718717
214575875608
818778841813
706466743030
653483540435

Phix

<lang Phix>constant numbers = {" ## #", -- 0

                   "  ##  #", -- 1
                   "  #  ##", -- 2
                   " #### #", -- 3
                   " #   ##", -- 4
                   " ##   #", -- 5
                   " # ####", -- 6
                   " ### ##", -- 7
                   " ## ###", -- 8
                   "   # ##"} -- 9

procedure decode(string bar_code)

   bar_code = trim(bar_code)
   if length(bar_code)=95
   and bar_code[1..3]="# #"
   and bar_code[46..50]=" # # "
   and bar_code[93..95]="# #" then
       for reversed=false to true do
           sequence r = {}
           for i=1 to 12 do
               integer st = iff(i<=6?i*7-3:i*7+2)
               string number = bar_code[st..st+6]
               if i>6 then number = substitute_all(number," #X","X #") end if
               r &= find(number,numbers)-1
           end for
           if not find(-1,r) then
               if remainder(sum(sq_mul(r,{3,1,3,1,3,1,3,1,3,1,3,1})),10) then
                   printf(1,"invalid checksum\n")
               else
                   printf(1,"%v%s\n",{r,iff(reversed?" (upside down)","")})
               end if
               return
           end if
           bar_code = reverse(bar_code)
       end for
   end if
   printf(1,"invalid\n")

end procedure

constant bar_codes = split("""

        # #   # ##  #  ## #   ## ### ## ### ## #### # # # ## ##  #   #  ##  ## ###  # ##  ## ### #  # #       
       # # #   ##   ## # #### #   # ## #   ## #   ## # # # ###  # ###  ##  ## ###  # #  ### ###  # # #         
        # #    # # #  ###  #   #    # #  #   #    # # # # ## #   ## #   ## #   ##   # # #### ### ## # #         
      # # ##  ## ##  ##   #  #   #  # ###  # ##  ## # # #   ## ##  #  ### ## ## #   # #### ## #   # #        
        # # ### ## #   ## ## ###  ##  # ##   #   # ## # # ### #  ## ##  #    # ### #  ## ##  #      # #          
         # #  #   # ##  ##  #   #   #  # ##  ##  #   # # # # #### #  ##  # #### #### # #  ##  # #### # #         
        # #  #  ##  ##  # #   ## ##   # ### ## ##   # # # #  #   #   #  #  ### # #    ###  # #  #   # #        
       # # #    # ##  ##   #  # ##  ##  ### #   #  # # # ### ## ## ### ## ### ### ## #  ##  ### ## # #         
        # # ### ##   ## # # #### #   ## # #### # #### # # #   #  # ###  #    # ###  # #    # ###  # # #       
       # # # #### ##   # #### # #   ## ## ### #### # # # #  ### # ###  ###  # # ###  #    # #  ### # #         

""","\n",true) for i=1 to length(bar_codes) do

   decode(bar_codes[i])

end for</lang>

Output:
{9,2,4,7,7,3,2,7,1,0,1,9}
{4,0,3,9,4,4,4,4,1,0,5,0}
{8,3,4,9,9,9,6,7,6,7,0,6} (upside down)
{9,3,9,8,2,5,1,5,8,8,1,1} (upside down)
invalid
{3,1,6,3,1,3,7,1,8,7,1,7} (upside down)
{2,1,4,5,7,5,8,7,5,6,0,8}
{8,1,8,7,7,8,8,4,1,8,1,3} (upside down)
{7,0,6,4,6,6,7,4,3,0,3,0}
{6,5,3,4,8,3,5,4,0,4,3,5}

PicoLisp

<lang PicoLisp>(de l2n (Lst)

  (case Lst
     ((0 0 0 1 1 0 1) 0)
     ((0 0 1 1 0 0 1) 1)
     ((0 0 1 0 0 1 1) 2)
     ((0 1 1 1 1 0 1) 3)
     ((0 1 0 0 0 1 1) 4)
     ((0 1 1 0 0 0 1) 5)
     ((0 1 0 1 1 1 1) 6)
     ((0 1 1 1 0 1 1) 7)
     ((0 1 1 0 1 1 1) 8)
     ((0 0 0 1 0 1 1) 9) ) )

(de convs (Lst Flg)

  (make
     (for L Lst
        (link
           (if2 (= "#" L) Flg 0 1 1 0) ) ) ) )

(de getL (Lst)

  (make
     (cut 3 'Lst)
     (do 6
        (link (convs (cut 7 'Lst))) )
     (cut 5 'Lst)
     (do 6
        (link (convs (cut 7 'Lst) T)) ) ) )

(de parse (Str)

  (let Lst
     (make
        (link (clip (chop Str)))
        (link (reverse (car (made)))) )
     (find
        '((N) (fully num? N))
        (mapcar '((L) (mapcar l2n (getL L))) Lst) ) ) )

(de upc (Str)

  (let Lst (parse Str)
     (cons
        Lst
        (%
           (apply + (mapcar * Lst (circ 3 1)))
           10 ) ) ) )

(setq *U

  (quote
     "         # #   # ##  #  ## #   ## ### ## ### ## #### # # # ## ##  #   #  ##  ## ###  # ##  ## ### #  # #       "
     "        # # #   ##   ## # #### #   # ## #   ## #   ## # # # ###  # ###  ##  ## ###  # #  ### ###  # # #         "
     "         # #    # # #  ###  #   #    # #  #   #    # # # # ## #   ## #   ## #   ##   # # #### ### ## # #         "
     "       # # ##  ## ##  ##   #  #   #  # ###  # ##  ## # # #   ## ##  #  ### ## ## #   # #### ## #   # #        "
     "         # # ### ## #   ## ## ###  ##  # ##   #   # ## # # ### #  ## ##  #    # ### #  ## ##  #      # #          "
     "          # #  #   # ##  ##  #   #   #  # ##  ##  #   # # # # #### #  ##  # #### #### # #  ##  # #### # #         "
     "         # #  #  ##  ##  # #   ## ##   # ### ## ##   # # # #  #   #   #  #  ### # #    ###  # #  #   # #        "
     "        # # #    # ##  ##   #  # ##  ##  ### #   #  # # # ### ## ## ### ## ### ### ## #  ##  ### ## # #         "
     "         # # ### ##   ## # # #### #   ## # #### # #### # # #   #  # ###  #    # ###  # #    # ###  # # #       "
     "        # # # #### ##   # #### # #   ## ## ### #### # # # #  ### # ###  ###  # # ###  #    # #  ### # #         " ) )

(for L (mapcar upc *U)

  (println (if (car L) @ 'invalid)) )</lang>
Output:
(9 2 4 7 7 3 2 7 1 0 1 9)
(4 0 3 9 4 4 4 4 1 0 5 0)
(8 3 4 9 9 9 6 7 6 7 0 6)
(9 3 9 8 2 5 1 5 8 8 1 1)
invalid
(3 1 6 3 1 3 7 1 8 7 1 7)
(2 1 4 5 7 5 8 7 5 6 0 8)
(8 1 8 7 7 8 8 4 1 8 1 3)
(7 0 6 4 6 6 7 4 3 0 3 0)
(6 5 3 4 8 3 5 4 0 4 3 5)

Raku

(formerly Perl 6) <lang perl6>sub decode_UPC ( Str $line ) {

   constant @patterns1 = '   ## #', '  ##  #', '  #  ##', ' #### #', ' #   ##',
                         ' ##   #', ' # ####', ' ### ##', ' ## ###', '   # ##';
   constant @patterns2 = @patterns1».trans( '#' => ' ', ' ' => '#' );
   constant %pattern_to_digit_1 = @patterns1.antipairs;
   constant %pattern_to_digit_2 = @patterns2.antipairs;
   constant $re = / ^  '# #'  (@patterns1) ** 6
                      ' # # ' (@patterns2) ** 6
                       '# #'                     $ /;
   $line.trim ~~ $re
       orelse return;
   my @digits = flat %pattern_to_digit_1{ $0».Str },
                     %pattern_to_digit_2{ $1».Str };
   return unless ( @digits Z* ( |(3,1) xx * ) ).sum %% 10;
   return @digits.join;

}

my @lines =

   '         # #   # ##  #  ## #   ## ### ## ### ## #### # # # ## ##  #   #  ##  ## ###  # ##  ## ### #  # #       ',
    '        # # #   ##   ## # #### #   # ## #   ## #   ## # # # ###  # ###  ##  ## ###  # #  ### ###  # # #      ',
   '         # #    # # #  ###  #   #    # #  #   #    # # # # ## #   ## #   ## #   ##   # # #### ### ## # #       ',
     '       # # ##  ## ##  ##   #  #   #  # ###  # ##  ## # # #   ## ##  #  ### ## ## #   # #### ## #   # #        ',
   '         # # ### ## #   ## ## ###  ##  # ##   #   # ## # # ### #  ## ##  #    # ### #  ## ##  #      # #       ',
  '          # #  #   # ##  ##  #   #   #  # ##  ##  #   # # # # #### #  ##  # #### #### # #  ##  # #### # #    ',
   '         # #  #  ##  ##  # #   ## ##   # ### ## ##   # # # #  #   #   #  #  ### # #    ###  # #  #   # #     ',
    '        # # #    # ##  ##   #  # ##  ##  ### #   #  # # # ### ## ## ### ## ### ### ## #  ##  ### ## # #      ',
   '         # # ### ##   ## # # #### #   ## # #### # #### # # #   #  # ###  #    # ###  # #    # ###  # # #       ',
    '        # # # #### ##   # #### # #   ## ## ### #### # # # #  ### # ###  ###  # # ###  #    # #  ### # #      ',

for @lines -> $line {

   say decode_UPC($line)
    // decode_UPC($line.flip)
    // 'Invalid';

}</lang>

Output:
924773271019
403944441050
834999676706
939825158811
Invalid
316313718717
214575875608
818778841813
706466743030
653483540435

REXX

<lang rexx>/*REXX program to read/interpret UPC symbols and translate them to a numberic string.*/

          #.0= '   ## #'
          #.1= '  ##  #'
          #.2= '  #  ##'
          #.3= ' #### #'
          #.4= ' #   ##'
          #.5= ' ##   #'
          #.6= ' # ####'
          #.7= ' ### ##'
          #.8= ' ## ###'                                /* [↓]  right─sided UPC digits.*/
          #.9= '   # ##' ;           do i=0  for 10;     ##.i= translate(#.i, ' #', "# ")
                                     end  /*i*/

say center('UPC', 14, "─") ' ___'copies(1234567, 6)"-----"copies(1234567, 6)'___' @.=. @.1 = ' # # # ## # ## # ## ### ## ### ## #### # # # ## ## # # ## ## ### # ## ## ### # # # ' @.2 = ' # # # ## ## # #### # # ## # ## # ## # # # ### # ### ## ## ### # # ### ### # # # ' @.3 = ' # # # # # ### # # # # # # # # # # ## # ## # ## # ## # # #### ### ## # # ' @.4 = ' # # ## ## ## ## # # # # ### # ## ## # # # ## ## # ### ## ## # # #### ## # # # ' @.5 = ' # # ### ## # ## ## ### ## # ## # # ## # # ### # ## ## # # ### # ## ## # # # ' @.6 = ' # # # # ## ## # # # # ## ## # # # # # #### # ## # #### #### # # ## # #### # # ' @.7 = ' # # # ## ## # # ## ## # ### ## ## # # # # # # # # ### # # ### # # # # # ' @.8 = ' # # # # ## ## # # ## ## ### # # # # # ### ## ## ### ## ### ### ## # ## ### ## # # ' @.9 = ' # # ### ## ## # # #### # ## # #### # #### # # # # # ### # # ### # # # ### # # # ' @.10= ' # # # #### ## # #### # # ## ## ### #### # # # # ### # ### ### # # ### # # # ### # '

     do j=1  while @.j\==.;        $= @.j;             txt=
     if left($, 9)\=     | right($, 9)\=      then txt= 'bad blanks'
     $= strip($);   $$= $;       L= length($)
     if left($, 3)\=='# #' | right($, 3)\=="# #"  then txt= 'bad fence'
     if L\==95             & txt==              then txt= 'bad len'
     $= substr($, 4, L - 3 - 3);               sum= 0                    /*elide  ends.*/
     $= delstr($, length($) % 2 - 1,  5)                                 /*  "  middle.*/
     if txt==  then do k=1  for 12;    parse var  $   x  +7  $         /*get UPC dig.*/
                        do d=0  for 10;  if x==#.d | x==##.d  then leave /*valid digit?*/
                        end   /*d*/
                      if d==10 & k\==12  then do;   txt= 'reversed';   leave;   end
                      if d==10           then do;   txt= 'bad dig';    leave;   end
                      if k//2  then sum= sum +    3 * d                  /*mult. by  3.*/
                               else sum= sum + d                         /*  "    "  1.*/
                      txt= txt || d
                      end   /*k*/
     if sum//10 \== 0  then txt= 'bad chksum'                            /*invalid sum?*/
     say center( strip(txt), 15)  ' '  $$
     end   /*j*/                                /*stick a fork in it,  we're all done. */</lang>
output   when using the internal default input:
─────UPC──────    ___123456712345671234567123456712345671234567-----123456712345671234567123456712345671234567___
 924773271019     # #   # ##  #  ## #   ## ### ## ### ## #### # # # ## ##  #   #  ##  ## ###  # ##  ## ### #  # #
 403944441050     # # #   ##   ## # #### #   # ## #   ## #   ## # # # ###  # ###  ##  ## ###  # #  ### ###  # # #
   reversed       # #    # # #  ###  #   #    # #  #   #    # # # # ## #   ## #   ## #   ##   # # #### ### ## # #
   reversed       # # ##  ## ##  ##   #  #   #  # ###  # ##  ## # # #   ## ##  #  ### ## ## #   # #### ## #   # #
    bad dig       # # ### ## #   ## ## ###  ##  # ##   #   # ## # # ### #  ## ##  #    # ### #  ## ##  #      # #
   reversed       # #  #   # ##  ##  #   #   #  # ##  ##  #   # # # # #### #  ##  # #### #### # #  ##  # #### # #
 214575875608     # #  #  ##  ##  # #   ## ##   # ### ## ##   # # # #  #   #   #  #  ### # #    ###  # #  #   # #
   reversed       # # #    # ##  ##   #  # ##  ##  ### #   #  # # # ### ## ## ### ## ### ### ## #  ##  ### ## # #
 706466743030     # # ### ##   ## # # #### #   ## # #### # #### # # #   #  # ###  #    # ###  # #    # ###  # # #
    bad len       # # # #### ##   # #### # #   ## ## ### #### # # # #  ### # ###  ###  # # ###  #    # #  ### #

Ruby

Translation of: C

<lang ruby>DIGIT_F = {

   "   ## #" => 0,
   "  ##  #" => 1,
   "  #  ##" => 2,
   " #### #" => 3,
   " #   ##" => 4,
   " ##   #" => 5,
   " # ####" => 6,
   " ### ##" => 7,
   " ## ###" => 8,
   "   # ##" => 9,

}

DIGIT_R = {

   "###  # " => 0,
   "##  ## " => 1,
   "## ##  " => 2,
   "#    # " => 3,
   "# ###  " => 4,
   "#  ### " => 5,
   "# #    " => 6,
   "#   #  " => 7,
   "#  #   " => 8,
   "### #  " => 9,

}

END_SENTINEL = "# #" MID_SENTINEL = " # # "

def decode_upc(s)

   def decode_upc_impl(input)
       upc = input.strip
       if upc.length != 95 then
           return false
       end
       pos = 0
       digits = []
       sum = 0
       # end sentinel
       if upc[pos .. pos + 2] == END_SENTINEL then
           pos += 3
       else
           return false
       end
       # 6 left hand digits
       for i in 0 .. 5
           digit = DIGIT_F[upc[pos .. pos + 6]]
           if digit == nil then
               return false
           else
               digits.push(digit)
               sum += digit * [1, 3][digits.length % 2]
               pos += 7
           end
       end
       # mid sentinel
       if upc[pos .. pos + 4] == MID_SENTINEL then
           pos += 5
       else
           return false
       end
       # 6 right hand digits
       for i in 0 .. 5
           digit = DIGIT_R[upc[pos .. pos + 6]]
           if digit == nil then
               return false
           else
               digits.push(digit)
               sum += digit * [1, 3][digits.length % 2]
               pos += 7
           end
       end
       # end sentinel
       if upc[pos .. pos + 2] == END_SENTINEL then
           pos += 3
       else
           return false
       end
       if sum % 10  == 0 then
           print digits, " "
           return true
       else
           print "Failed Checksum "
           return false
       end
   end
   if decode_upc_impl(s) then
       puts "Rightside Up"
   elsif decode_upc_impl(s.reverse) then
       puts "Upside Down"
   else
       puts "Invalid digit(s)"
   end

end

def main

   num = 0
   print "%2d: " % [num += 1]
   decode_upc("         # #   # ##  #  ## #   ## ### ## ### ## #### # # # ## ##  #   #  ##  ## ###  # ##  ## ### #  # #       ")
   print "%2d: " % [num += 1]
   decode_upc("        # # #   ##   ## # #### #   # ## #   ## #   ## # # # ###  # ###  ##  ## ###  # #  ### ###  # # #         ")
   print "%2d: " % [num += 1]
   decode_upc("         # #    # # #  ###  #   #    # #  #   #    # # # # ## #   ## #   ## #   ##   # # #### ### ## # #         ")
   print "%2d: " % [num += 1]
   decode_upc("       # # ##  ## ##  ##   #  #   #  # ###  # ##  ## # # #   ## ##  #  ### ## ## #   # #### ## #   # #        ")
   print "%2d: " % [num += 1]
   decode_upc("         # # ### ## #   ## ## ###  ##  # ##   #   # ## # # ### #  ## ##  #    # ### #  ## ##  #      # #          ")
   print "%2d: " % [num += 1]
   decode_upc("          # #  #   # ##  ##  #   #   #  # ##  ##  #   # # # # #### #  ##  # #### #### # #  ##  # #### # #         ")
   print "%2d: " % [num += 1]
   decode_upc("         # #  #  ##  ##  # #   ## ##   # ### ## ##   # # # #  #   #   #  #  ### # #    ###  # #  #   # #        ")
   print "%2d: " % [num += 1]
   decode_upc("        # # #    # ##  ##   #  # ##  ##  ### #   #  # # # ### ## ## ### ## ### ### ## #  ##  ### ## # #         ")
   print "%2d: " % [num += 1]
   decode_upc("         # # ### ##   ## # # #### #   ## # #### # #### # # #   #  # ###  #    # ###  # #    # ###  # # #       ")
   print "%2d: " % [num += 1]
   decode_upc("        # # # #### ##   # #### # #   ## ## ### #### # # # #  ### # ###  ###  # # ###  #    # #  ### # #         ")

end

main()</lang>

Output:
 1: [9, 2, 4, 7, 7, 3, 2, 7, 1, 0, 1, 9] Rightside Up
 2: [4, 0, 3, 9, 4, 4, 4, 4, 1, 0, 5, 0] Rightside Up
 3: [8, 3, 4, 9, 9, 9, 6, 7, 6, 7, 0, 6] Upside Down
 4: [9, 3, 9, 8, 2, 5, 1, 5, 8, 8, 1, 1] Upside Down
 5: Invalid digit(s)
 6: [3, 1, 6, 3, 1, 3, 7, 1, 8, 7, 1, 7] Upside Down
 7: [2, 1, 4, 5, 7, 5, 8, 7, 5, 6, 0, 8] Rightside Up
 8: [8, 1, 8, 7, 7, 8, 8, 4, 1, 8, 1, 3] Upside Down
 9: [7, 0, 6, 4, 6, 6, 7, 4, 3, 0, 3, 0] Rightside Up
10: [6, 5, 3, 4, 8, 3, 5, 4, 0, 4, 3, 5] Rightside Up

zkl

<lang zkl>var lhd=Dictionary(), rhd=Dictionary(); [0..].zip(List(

   "0 0 0 1 1 0 1", //--> "___##_#":0   "###__#_":0
   "0 0 1 1 0 0 1", 
   "0 0 1 0 0 1 1",
   "0 1 1 1 1 0 1",
   "0 1 0 0 0 1 1",
   "0 1 1 0 0 0 1",
   "0 1 0 1 1 1 1",
   "0 1 1 1 0 1 1",
   "0 1 1 0 1 1 1",
   "0 0 0 1 0 1 1") //--> "___#_##":9    "###_#__":9

).pump(Void,fcn([(n,bs)]){

  bs-=" ";
  lhd[bs.translate("01","_#")]=n;
  rhd[bs.translate("10","_#")]=n;

});

fcn parseBarCode(barcode, one=True){ // --> 12 digits

  upsideDown:='wrap{	// was I looking at this bar code upside down?
     if(one and (r:=parseBarCode(barcode.reverse(),False))) return(r);
     return(False);
  };
  var [const] start=RegExp(String("_"*9, "+#_#")), tail="_"*7;
  if(not start.search(barcode)) return(upsideDown());
  r,idx,d,mark := List(), start.matched[0][1], lhd, "_#_#_";
  do(2){
     do(6){

if(Void==(z:=d.find(barcode[idx,7]))) return(upsideDown()); r.append(z); idx+=7;

     }
     if(barcode[idx,5] != mark) return(Void);
     d,idx,mark = rhd, idx+5, "#_#__";
  }
  if(tail!=barcode[idx,7]) return(Void);  // 9 trailing blanks? two checked above
  r

}</lang> Or, if you like way too long regular expressions: <lang zkl>var upcRE = RegExp(String("_"*9, "+#_#", lhd.keys.concat("|","(",")")*6, "_#_#_", rhd.keys.concat("|","(",")")*6, "#_#", "_"*9)),

   digits=lhd.copy().extend(rhd);

fcn parseBarCode2(barcode){ // --> 12 digits

  if(not (upcRE.search(barcode) or upcRE.search(barcode.reverse()))) return(False);
  upcRE.matched[1,*] // ( (a,b), "_#_####","_##___#", 10 more digit patterns )
  .apply(digits.get)

}</lang> <lang zkl>barcodes:=

  1. <<<"

_________#_#___#_##__#__##_#___##_###_##_###_##_####_#_#_#_##_##__#___#__##__##_###__#_##__##_###_#__#_#_________ _________#_#_#___##___##_#_####_#___#_##_#___##_#___##_#_#_#_###__#_###__##__##_###__#_#__###_###__#_#_#_________ _________#_#____#_#_#__###__#___#____#_#__#___#____#_#_#_#_##_#___##_#___##_#___##___#_#_####_###_##_#_#_________ _________#_#_##__##_##__##___#__#___#__#_###__#_##__##_#_#_#___##_##__#__###_##_##_#___#_####_##_#___#_#_________ _________#_#_###_##_#___##_##_###__##__#_##___#___#_##_#_#_###_#__##_##__#____#_###_#__##_##__#______#_#__________ __________#_#__#___#_##__##__#___#___#__#_##__##__#___#_#_#_#_####_#__##__#_####_####_#_#__##__#_####_#_#____________ _________#_#__#__##__##__#_#___##_##___#_###_##_##___#_#_#_#__#___#___#__#__###_#_#____###__#_#__#___#_#_________ _________#_#_#____#_##__##___#__#_##__##__###_#___#__#_#_#_###_##_##_###_##_###_###_##_#__##__###_##_#_#__________ _________#_#_###_##___##_#_#_####_#___##_#_####_#_####_#_#_#___#__#_###__#____#_###__#_#____#_###__#_#_#_________ _______________#_#_#_####_##___#_####_#_#___##_##_###_####_#_#_#_#__###_#_###__###__#_#_###__#____#_#__###_#_#_________" .split("\n");

  1. <<<

foreach n,barcode in ([1..].zip(barcodes)){

  bc:=parseBarCode(barcode);
  println("%2d: [%s]".fmt(n,bc and bc.concat(" ") or "Not valid"));

}</lang>

Output:
 1: [9 2 4 7 7 3 2 7 1 0 1 9]
 2: [4 0 3 9 4 4 4 4 1 0 5 0]
 3: [8 3 4 9 9 9 6 7 6 7 0 6]
 4: [9 3 9 8 2 5 1 5 8 8 1 1]
 5: [Not valid]
 6: [3 1 6 3 1 3 7 1 8 7 1 7]
 7: [2 1 4 5 7 5 8 7 5 6 0 8]
 8: [8 1 8 7 7 8 8 4 1 8 1 3]
 9: [7 0 6 4 6 6 7 4 3 0 3 0]
10: [6 5 3 4 8 3 5 4 0 4 3 5]