Convert UPC bar codes to decimal.

Task
UPC
You are encouraged to solve this task according to the task description, using any language you may know.
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.

11l

Translation of: Kotlin
V LEFT_DIGITS = [
   ‘   ## #’ = 0,
   ‘  ##  #’ = 1,
   ‘  #  ##’ = 2,
   ‘ #### #’ = 3,
   ‘ #   ##’ = 4,
   ‘ ##   #’ = 5,
   ‘ # ####’ = 6,
   ‘ ### ##’ = 7,
   ‘ ## ###’ = 8,
   ‘   # ##’ = 9
]

V RIGHT_DIGITS = Dict(LEFT_DIGITS.items(), (k, v) -> (k.replace(‘ ’, ‘s’).replace(‘#’, ‘ ’).replace(‘s’, ‘#’), v))

V END_SENTINEL = ‘# #’
V MID_SENTINEL = ‘ # # ’

F decodeUPC(input)
   F decode(candidate)
      V pos = 0
      V part = candidate[pos .+ :END_SENTINEL.len]
      I part == :END_SENTINEL
         pos += :END_SENTINEL.len
      E
         R (0B, [Int]())

      [Int] output
      L 6
         part = candidate[pos .+ 7]
         pos += 7

         I part C :LEFT_DIGITS
            output [+]= :LEFT_DIGITS[part]
         E
            R (0B, output)

      part = candidate[pos .+ :MID_SENTINEL.len]
      I part == :MID_SENTINEL
         pos += :MID_SENTINEL.len
      E
         R (0B, output)

      L 6
         part = candidate[pos .+ 7]
         pos += 7

         I part C :RIGHT_DIGITS
            output [+]= :RIGHT_DIGITS[part]
         E
            R (0B, output)

      part = candidate[pos .+ :END_SENTINEL.len]
      I part == :END_SENTINEL
         pos += :END_SENTINEL.len
      E
         R (0B, output)

      V sum = 0
      L(v) output
         I L.index % 2 == 0
            sum += 3 * v
         E
            sum += v
      R (sum % 10 == 0, output)

   V candidate = input.trim(‘ ’)
   V out = decode(candidate)
   I out[0]
      print(out[1])
   E
      out = decode(reversed(candidate))
      I out[0]
         print(out[1]‘ Upside down’)
      E
         I out[1].len == 12
            print(‘Invalid checksum’)
         E
            print(‘Invalid digit(s)’)

V barcodes = [
   ‘         # #   # ##  #  ## #   ## ### ## ### ## #### # # # ## ##  #   #  ##  ## ###  # ##  ## ### #  # #       ’,
   ‘        # # #   ##   ## # #### #   # ## #   ## #   ## # # # ###  # ###  ##  ## ###  # #  ### ###  # # #         ’,
   ‘         # #    # # #  ###  #   #    # #  #   #    # # # # ## #   ## #   ## #   ##   # # #### ### ## # #         ’,
   ‘       # # ##  ## ##  ##   #  #   #  # ###  # ##  ## # # #   ## ##  #  ### ## ## #   # #### ## #   # #        ’,
   ‘         # # ### ## #   ## ## ###  ##  # ##   #   # ## # # ### #  ## ##  #    # ### #  ## ##  #      # #          ’,
   ‘          # #  #   # ##  ##  #   #   #  # ##  ##  #   # # # # #### #  ##  # #### #### # #  ##  # #### # #         ’,
   ‘         # #  #  ##  ##  # #   ## ##   # ### ## ##   # # # #  #   #   #  #  ### # #    ###  # #  #   # #        ’,
   ‘        # # #    # ##  ##   #  # ##  ##  ### #   #  # # # ### ## ## ### ## ### ### ## #  ##  ### ## # #         ’,
   ‘         # # ### ##   ## # # #### #   ## # #### # #### # # #   #  # ###  #    # ###  # #    # ###  # # #       ’,
   ‘        # # # #### ##   # #### # #   ## ## ### #### # # # #  ### # ###  ###  # # ###  #    # #  ### # #         ’
]

L(barcode) barcodes
   decodeUPC(barcode)
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]

Action!

DEFINE PTR="CARD"
DEFINE RESOK="255"
DEFINE RESUPSIDEDOWN="254"
DEFINE RESINVALID="253"
DEFINE DIGITCOUNT="12"
DEFINE DIGITLEN="7"

PTR ARRAY ldigits(10),rdigits(10)
CHAR ARRAY marker="# #",midmarker=" # # "

PROC Init()
  ldigits(0)="   ## #" ldigits(1)="  ##  #"
  ldigits(2)="  #  ##" ldigits(3)=" #### #"
  ldigits(4)=" #   ##" ldigits(5)=" ##   #"
  ldigits(6)=" # ####" ldigits(7)=" ### ##"
  ldigits(8)=" ## ###" ldigits(9)="   # ##"
  rdigits(0)="###  # " rdigits(1)="##  ## "
  rdigits(2)="## ##  " rdigits(3)="#    # "
  rdigits(4)="# ###  " rdigits(5)="#  ### "
  rdigits(6)="# #    " rdigits(7)="#   #  "
  rdigits(8)="#  #   " rdigits(9)="### #  "
RETURN

BYTE FUNC DecodeMarker(CHAR ARRAY s BYTE POINTER pos CHAR ARRAY marker)
  CHAR ARRAY tmp(6)
  BYTE x

  x=pos^+marker(0)
  IF x>s(0) THEN
    RETURN (RESINVALID)
  ELSE
    SCopyS(tmp,s,pos^,pos^+marker(0)-1)
    pos^==+marker(0)
    IF SCompare(tmp,marker)#0 THEN
      RETURN (RESINVALID)
    FI
  FI
RETURN (RESOK)

BYTE FUNC DecodeDigit(CHAR ARRAY s BYTE POINTER pos PTR ARRAY digits)
  CHAR ARRAY tmp(DIGITLEN+1)
  BYTE i,x

  x=pos^+DIGITLEN
  IF x>s(0) THEN
    RETURN (RESINVALID)
  ELSE
    SCopyS(tmp,s,pos^,pos^+DIGITLEN-1)
    pos^==+DIGITLEN
    FOR i=0 TO 9
    DO
      IF SCompare(tmp,digits(i))=0 THEN
        RETURN (i)
      FI
    OD
  FI
RETURN (RESINVALID)

BYTE FUNC Validation(BYTE ARRAY code)
  BYTE ARRAY mult=[3 1 3 1 3 1 3 1 3 1 3 1]
  BYTE i
  INT sum

  sum=0
  FOR i=0 TO DIGITCOUNT-1
  DO
    sum==+code(i)*mult(i)
  OD
  IF sum MOD 10=0 THEN
    RETURN (RESOK)
  FI
RETURN (RESINVALID)

BYTE FUNC DecodeInternal(CHAR ARRAY s BYTE ARRAY code)
  BYTE res,pos,i

  pos=1
  WHILE pos<=s(0) AND s(pos)=32
  DO pos==+1 OD

  res=DecodeMarker(s,@pos,marker)
  IF res=RESINVALID THEN RETURN (res) FI

  FOR i=0 TO 5
  DO
    res=DecodeDigit(s,@pos,ldigits)
    IF res=RESINVALID THEN RETURN (res) FI
    code(i)=res
  OD
    
  res=DecodeMarker(s,@pos,midmarker)
  IF res=RESINVALID THEN RETURN (res) FI

  FOR i=6 TO 11
  DO
    res=DecodeDigit(s,@pos,rdigits)
    IF res=RESINVALID THEN RETURN (res) FI
    code(i)=res
  OD

  res=DecodeMarker(s,@pos,marker)
  IF res=RESINVALID THEN RETURN (res) FI
  res=Validation(code)
RETURN (res)

PROC Reverse(CHAR ARRAY src,dst)
  BYTE i,j

  i=1 j=src(0) dst(0)=j
  WHILE j>0
  DO
    dst(j)=src(i)
    i==+1 j==-1
  OD
RETURN

BYTE FUNC Decode(CHAR ARRAY s BYTE ARRAY code)
  CHAR ARRAY tmp(256)
  BYTE res

  res=DecodeInternal(s,code)
  IF res=RESOK THEN RETURN (res) FI
  Reverse(s,tmp)
  res=DecodeInternal(tmp,code)
  IF res=RESINVALID THEN RETURN (res) FI
RETURN (RESUPSIDEDOWN)

PROC Test(BYTE id CHAR ARRAY s)
  BYTE ARRAY code(DIGITCOUNT)
  BYTE res,i

  res=Decode(s,code)
  IF id<10 THEN Put(32) FI
  PrintF("%B: ",id)
  IF res=RESINVALID THEN
    PrintE("invalid")
  ELSE
    FOR i=0 TO DIGITCOUNT-1
    DO
      PrintB(code(i))
    OD
    IF res=RESUPSIDEDOWN THEN
      PrintE(" valid (upside down)")
    ELSE
      PrintE(" valid")
    FI
  FI
RETURN

PROC Main()
  PTR ARRAY codes(10)
  BYTE i
  
  Init()
  codes(0)="         # #   # ##  #  ## #   ## ### ## ### ## #### # # # ## ##  #   #  ##  ## ###  # ##  ## ### #  # #       "
  codes(1)="        # # #   ##   ## # #### #   # ## #   ## #   ## # # # ###  # ###  ##  ## ###  # #  ### ###  # # #         "
  codes(2)="         # #    # # #  ###  #   #    # #  #   #    # # # # ## #   ## #   ## #   ##   # # #### ### ## # #         "
  codes(3)="       # # ##  ## ##  ##   #  #   #  # ###  # ##  ## # # #   ## ##  #  ### ## ## #   # #### ## #   # #        "
  codes(4)="         # # ### ## #   ## ## ###  ##  # ##   #   # ## # # ### #  ## ##  #    # ### #  ## ##  #      # #          "
  codes(5)="          # #  #   # ##  ##  #   #   #  # ##  ##  #   # # # # #### #  ##  # #### #### # #  ##  # #### # #         "
  codes(6)="         # #  #  ##  ##  # #   ## ##   # ### ## ##   # # # #  #   #   #  #  ### # #    ###  # #  #   # #        "
  codes(7)="        # # #    # ##  ##   #  # ##  ##  ### #   #  # # # ### ## ## ### ## ### ### ## #  ##  ### ## # #         "
  codes(8)="         # # ### ##   ## # # #### #   ## # #### # #### # # #   #  # ###  #    # ###  # #    # ###  # # #       "
  codes(9)="        # # # #### ##   # #### # #   ## ## ### #### # # # #  ### # ###  ###  # # ###  #    # #  ### # #         "
  
  FOR i=0 TO 9
  DO
    Test(i+1,codes(i))
  OD
RETURN
Output:

Screenshot from Atari 8-bit computer

 1: 924773271019 valid
 2: 403944441050 valid
 3: 834999676706 valid (upside down)
 4: 939825158811 valid (upside down)
 5: invalid
 6: 316313718717 valid (upside down)
 7: 214575875608 valid
 8: 818778841813 valid (upside down)
 9: 706466743030 valid
10: 653483540435 valid

Ada

-- Parse scanned UPC-A codes
-- J. Carter     2024 May

with Ada.Containers.Hashed_Maps;
with Ada.Strings.Fixed;
with Ada.Strings.Hash;
with Ada.Text_IO;

procedure UPC is
   subtype Digit_Pattern is String (1 .. 7);
   subtype Digit_Character is Character range '0' .. '9';
   type Pattern_Map is array (Digit_Character) of Digit_Pattern;
   
   package Digit_Maps is new Ada.Containers.Hashed_Maps (Key_Type        => Digit_Pattern,
                                                         Element_Type    => Digit_Character,
                                                         Hash            => Ada.Strings.Hash,
                                                         Equivalent_Keys => "=");
   
   Start_Stop_Marker  : constant String := "# #";
   Middle_Marker      : constant String := " # # ";
                                                            
   Scan_Length : constant := 2 * Start_Stop_Marker'Length + Middle_Marker'Length + 12 * Digit_Pattern'Length;

   procedure Parse (Count : in Positive; Scan : in String; Reversed : in Boolean := False) with
      Pre => Scan'First = 1 and Scan'Length = Scan_Length;
   -- Parses Scan and prints the result
   -- Count is the line #
   -- If Scan is invalid and not Reversed, tries to parse a reversed version of Scan before printing that Scan is invalid
   
   Left_Pattern_Map : constant Pattern_Map :=
      ('0' => "   ## #",
       '1' => "  ##  #",
       '2' => "  #  ##",
       '3' => " #### #",
       '4' => " #   ##",
       '5' => " ##   #",
       '6' => " # ####",
       '7' => " ### ##",
       '8' => " ## ###",
       '9' => "   # ##");
   Right_Pattern_Map : constant Pattern_Map :=
      ('0' => "###  # ",
       '1' => "##  ## ",
       '2' => "## ##  ",
       '3' => "#    # ",
       '4' => "# ###  ",
       '5' => "#  ### ",
       '6' => "# #    ",
       '7' => "#   #  ",
       '8' => "#  #   ",
       '9' => "### #  ");
       
   Left_Map  : Digit_Maps.Map;
   Right_Map : Digit_Maps.Map;
   
   function Image (Count : in Positive) return String is
      (if Count < 10 then Count'Image else Count'Image (2 .. 3) );
                                                         
   procedure Parse (Count : in Positive; Scan : in String; Reversed : in Boolean := False) is
      function Backwards return String with
         Post => Backwards'Result'First = Scan'First and
                 Backwards'Result'Last = Scan'Last and
                 (for all I in Scan'Range => Backwards'Result (I) = Scan (Scan'Last - I + 1) );
         
      function Backwards return String is
         Right  : Positive := Scan'Last;
         Result : String   := Scan;
      begin -- Backwards
         Swap : for Left in 1 .. Scan'Last / 2 loop
            Result (Left) := Scan (Right);
            Result (Right) := Scan (Left);
            Right := Right - 1;
         end loop Swap;
         
         return Result;
      end Backwards;
      
      Result : String (1 .. 12);
      Start  : Positive := 1 + Start_Stop_Marker'Length;
      Stop   : Positive;
   begin -- Parse
      if Scan (1 .. Start_Stop_Marker'Length) /= Start_Stop_Marker or
         Scan (Scan'Last - Start_Stop_Marker'Length + 1 .. Scan'Last) /= Start_Stop_Marker
      then
         Ada.Text_IO.Put_Line (Item => Image (Count) & ' ' & Scan & ": invalid, missing start or stop marker");
         
         return;
      end if;
      
      if Scan (1 + Start_Stop_Marker'Length + 6 * Digit_Pattern'Length ..
               1 + Start_Stop_Marker'Length + 6 * Digit_Pattern'Length + Middle_Marker'Length - 1) /= Middle_Marker
      then
         Ada.Text_IO.Put_Line (Item => Image (Count) & ' ' & Scan & ": invalid, missing middle marker");
         
         return;
      end if;
      
      Left_Side : for I in 1 .. 6 loop
         Stop := Start + Digit_Pattern'Length - 1;
         
         if not Left_Map.Contains (Scan (Start .. Stop) ) then
            if not Reversed then
               Parse (Count => Count, Scan => Backwards, Reversed => True);
            else
               Ada.Text_IO.Put_Line (Item => Image (Count) & ": invalid");
            end if;
            
            return;
         end if;
         
         Result (I) := Left_Map.Element (Scan (Start .. Stop) );
         Start := Stop + 1;
      end loop Left_Side;

      Start := Start + Middle_Marker'Length;
           
      Right_Side : for I in 7 .. 12 loop
         Stop := Start + Digit_Pattern'Length - 1;
         
         if not Right_Map.Contains (Scan (Start .. Stop) ) then
            if not Reversed then
               Parse (Count => Count, Scan => Backwards, Reversed => True);
            else
               Ada.Text_IO.Put_Line (Item => Image (Count) & ": invalid");
            end if;
            
            return;
         end if;
         
         Result (I) := Right_Map.Element (Scan (Start .. Stop) );
         Start := Stop + 1;
      end loop Right_Side;
      
      Ada.Text_IO.Put_Line (Item => Image (Count) & ": " & Result & (if Reversed then " reversed" else "") );
   end Parse;
   
   Count : Natural := 0;
begin -- UPC
   Fill_Left : for D in Left_Pattern_Map'Range loop
      Left_Map.Insert (Key => Left_Pattern_Map (D), New_Item => D);
   end loop Fill_Left;
   
   Fill_Right : for D in Right_Pattern_Map'Range loop
      Right_Map.Insert (Key => Right_Pattern_Map (D), New_Item => D);
   end loop Fill_Right;
   
   All_Lines : loop
      exit All_Lines when Ada.Text_IO.End_Of_File;
      
      One_Line : declare
         Line : constant String := Ada.Strings.Fixed.Trim (Ada.Text_IO.Get_Line, Ada.Strings.Both);
      begin -- One_Line
         Count := Count + 1;
         
         if Line'Length /= Scan_Length then
            Ada.Text_IO.Put_Line (Item => Image (Count) & " invalid scan;" & Line'Length'Image & "/=" & Scan_Length'Image);
         else
            Parse (Count => Count, Scan => Line);
         end if;
      end One_Line;
   end loop All_Lines;
end UPC;
Output:
 1: 924773271019
 2: 403944441050
 3: 834999676706 reversed
 4: 939825158811 reversed
 5: invalid
 6: 316313718717 reversed
 7: 214575875608
 8: 818778841813 reversed
 9: 706466743030
10: 653483540435

ALGOL 68

BEGIN
    # number of digits encoded by UPC                                         #
    INT upc digits = 12;
    # MODE to hold UPC bar code parse results                                 #
    MODE UPC = STRUCT( BOOL      valid         # TRUE if the UPC was valid,   #
                                               # FASLE otherwise              #
                     , [ 1 : upc digits ]INT digits
                                               # the digits encoded by the    #
                                               # UPC if it was valid          #
                     , BOOL      inverted      # TRUE if the code was scanned #
                                               # upside down, FALSE if it was #
                                               # right-side up                #
                     , STRING    error message # erro rmessage if the string  #
                                               # was invalid                  #
                     );
    # parses the UPC string s and returns a UPC containing the results        #
    PROC parse upc = ( STRING s )UPC:
         BEGIN
             # returns TRUE if we are at the end of s, FALSE otherwise        #
             PROC at end = BOOL: s pos > UPB s;
             # counts and skips spaces in s                                   #
             PROC skip spaces = INT:
                  BEGIN
                     INT spaces := 0;
                     WHILE IF at end THEN FALSE ELSE s[ s pos ] = " " FI DO
                         spaces +:= 1;
                         s pos  +:= 1
                     OD;
                     spaces
                  END; # skip spaces #
             # skips over the next number of bits characters of s and returns #
             # a bit representation ( space = 0, anything else = 1 )          #
             PROC get value = ( INT number of bits )BITS:
                  BEGIN
                     BITS value := 2r0;
                     TO number of bits WHILE NOT at end DO
                         value := ( value SHL 1 ) OR IF s[ s pos ] = " " THEN 2r0 ELSE 2r1 FI;
                         s pos +:= 1
                     OD;
                     value
                  END; # get value #
             # the representations of the digits                              #
             []BITS representations = ( 2r 0 0 0 1 1 0 1    # 0 #
                                      , 2r 0 0 1 1 0 0 1    # 1 #
                                      , 2r 0 0 1 0 0 1 1    # 2 #
                                      , 2r 0 1 1 1 1 0 1    # 3 #
                                      , 2r 0 1 0 0 0 1 1    # 4 #
                                      , 2r 0 1 1 0 0 0 1    # 5 #
                                      , 2r 0 1 0 1 1 1 1    # 6 #
                                      , 2r 0 1 1 1 0 1 1    # 7 #
                                      , 2r 0 1 1 0 1 1 1    # 8 #
                                      , 2r 0 0 0 1 0 1 1    # 9 #
                                      );
             []BITS reversed digits = ( 2r 0 1 0 0 1 1 1    # 0 #
                                      , 2r 0 1 1 0 0 1 1    # 1 #
                                      , 2r 0 0 1 1 0 1 1    # 2 #
                                      , 2r 0 1 0 0 0 0 1    # 3 #
                                      , 2r 0 0 1 1 1 0 1    # 4 #
                                      , 2r 0 1 1 1 0 0 1    # 5 #
                                      , 2r 0 0 0 0 1 0 1    # 6 #
                                      , 2r 0 0 1 0 0 0 1    # 7 #
                                      , 2r 0 0 0 1 0 0 1    # 8 #
                                      , 2r 0 0 1 0 1 1 1    # 9 #
                                      );
             # number of digits in the left and right sides of the UPC        #
             INT digits per side = upc digits OVER 2;
             UPC result;
             FOR d TO upc digits DO ( digits OF result )[ d ] := 0 OD;
             inverted OF result      := FALSE;
             error message OF result := "unknown error";
             valid OF result         := FALSE;
             INT s pos               := LWB s;
             IF skip spaces < 6 # should be 9 but we are being tolerant       #
             THEN
                 # insufficient leading spaces                                #
                 error message OF result := "missing leading spaces"
             ELIF get value( 3 ) /= 2r101 THEN
                 # no start                                                   #
                 error message OF result := "missing start sequence"
             ELSE
                 # ok so far - should now have six digits, each encoded in    #
                 # seven bits                                                 #
                 # note we store the digits as 1..10 if the are right-sid up  #
                 # and -1..-10 if they are inverted, so we can distinguish    #
                 # right-side up 0 and inverted 0                             #
                 # the digits are corrected to be 0..9 later                  #
                 BOOL valid digits := TRUE;
                 FOR d TO digits per side WHILE valid digits DO
                     BITS code = get value( 7 );
                     BOOL found digit := FALSE;
                     FOR d pos TO UPB representations WHILE NOT found digit DO
                         IF   code = representations[ d pos ] THEN
                             # found a "normal" digit                         #
                             found digit               := TRUE;
                             ( digits OF result )[ d ] := d pos
                         ELIF code = reversed digits[ d pos ] THEN
                             # found a reversed digit                         #
                             found digit               := TRUE;
                             inverted OF result        := TRUE;
                             ( digits OF result )[ d ] := - d pos
                         FI
                     OD;
                     IF NOT found digit THEN
                         # have an invalid digit                              #
                         error message OF result := "invalid digit " + whole( d, 0 );
                         valid digits := FALSE
                     FI
                 OD;
                 IF NOT valid digits THEN
                     # had an error                                           #
                     SKIP
                 ELIF get value( 5 ) /= 2r01010 THEN
                     # no middle separator                                    #
                     error message OF result := "missing middle sequence"
                 ELSE
                     # should now have 6 negated digits                      #
                     FOR d FROM digits per side + 1 TO upc digits WHILE valid digits DO
                         BITS code = NOT get value( 7 ) AND 16r7f;
                         BOOL found digit := FALSE;
                         FOR d pos TO UPB representations WHILE NOT found digit DO
                             IF  code = representations[ d pos ] THEN
                                 # found a normal negated digit               #
                                 found digit               := TRUE;
                                 ( digits OF result )[ d ] := d pos
                             ELIF code = reversed digits[ d pos ] THEN
                                 # found reversed negated digit               #
                                 found digit               := TRUE;
                                 inverted OF result        := TRUE;
                                 ( digits OF result )[ d ] := - d pos
                             FI
                         OD;
                         IF NOT found digit THEN
                             # have an invalid digit                          #
                             error message OF result := "invalid digit " + whole( d, 0 );
                             valid digits            := FALSE
                         FI
                     OD;
                     IF NOT valid digits THEN
                         # had an error                                       #
                         SKIP
                     ELIF get value( 3 ) /= 2r101 THEN
                         # no end sequence                                    #
                         error message OF result := "missing end sequence"
                     ELIF skip spaces < 6 # should be 9 but we are being      #
                                          # tolerant                          #
                     THEN
                         # insufficient trailing spaces                       #
                         error message OF result := "insufficient trailing spaces"
                     ELIF NOT at end THEN
                         # extraneous stuff after the trailing spaces         #
                         error message OF result := "unexpected trailing bits"
                     ELSE
                         # valid so far - if there were reversed digits,      #
                         # check they were all reversed                       #
                         # and correct the digits to be in 0..9               #
                         BOOL all reversed  := TRUE;
                         FOR d TO upc digits DO
                             IF ( digits OF result )[ d ] < 0 THEN
                                 # reversd digit                             #
                                 ( digits OF result )[ d ]  := ABS ( digits OF result )[ d ] - 1
                             ELSE
                                 # normal digit                               #
                                 ( digits OF result )[ d ] -:= 1;
                                 all reversed               := FALSE
                             FI
                         OD;
                         IF inverted OF result AND NOT all reversed
                         THEN
                             # had a mixture of inverted and non-inverted     #
                             # digits                                         #
                             error message OF result := "some reversed digits found"
                         ELSE
                             # the code appears valid - check the check sum   #
                             IF inverted OF result THEN
                                 # the code was reversed - reverse the digits #
                                 FOR d TO digits per side DO
                                     INT right digit
                                          = ( digits OF result )[ ( upc digits + 1 ) - d ];
                                     ( digits OF result )[ ( upc digits + 1 ) - d ]
                                         := ( digits OF result )[ d ];
                                     ( digits OF result )[ d ] := right digit
                                 OD
                             FI;
                             INT check sum := 0;
                             FOR d FROM 1 BY 2 TO upc digits DO
                                 check sum +:= 3 * ( digits OF result )[ d ]
                             OD;
                             FOR d FROM 2 BY 2 TO upc digits DO
                                 check sum +:=     ( digits OF result )[ d ]
                             OD;
                             IF check sum MOD 10 /= 0 THEN
                                 # invalid check digit                        #
                                 error message OF result := "incorrect check digit"
                             ELSE
                                 # the UPC code appears valid                 #
                                 valid OF result         := TRUE
                             FI
                         FI
                     FI
                 FI
             FI;
             result
         END; # parse upc #
    # task test cases                                                         #
    []STRING tests =
( "         # #   # ##  #  ## #   ## ### ## ### ## #### # # # ## ##  #   #  ##  ## ###  # ##  ## ### #  # #       "
, "        # # #   ##   ## # #### #   # ## #   ## #   ## # # # ###  # ###  ##  ## ###  # #  ### ###  # # #         "
, "         # #    # # #  ###  #   #    # #  #   #    # # # # ## #   ## #   ## #   ##   # # #### ### ## # #         "
, "       # # ##  ## ##  ##   #  #   #  # ###  # ##  ## # # #   ## ##  #  ### ## ## #   # #### ## #   # #        "
, "         # # ### ## #   ## ## ###  ##  # ##   #   # ## # # ### #  ## ##  #    # ### #  ## ##  #      # #          "
, "          # #  #   # ##  ##  #   #   #  # ##  ##  #   # # # # #### #  ##  # #### #### # #  ##  # #### # #         "
, "         # #  #  ##  ##  # #   ## ##   # ### ## ##   # # # #  #   #   #  #  ### # #    ###  # #  #   # #        "
, "        # # #    # ##  ##   #  # ##  ##  ### #   #  # # # ### ## ## ### ## ### ### ## #  ##  ### ## # #         "
, "         # # ### ##   ## # # #### #   ## # #### # #### # # #   #  # ###  #    # ###  # #    # ###  # # #       "
, "        # # # #### ##   # #### # #   ## ## ### #### # # # #  ### # ###  ###  # # ###  #    # #  ### # #         "
);
    FOR t pos FROM LWB tests TO UPB tests DO
        UPC code = parse upc( tests[ t pos ] );
        print( ( whole( t pos, -2 ), ":" ) );
        IF NOT valid OF code THEN
            # invalid UPC code                                                #
            print( ( "                       error: ", error message OF code, newline ) )
        ELSE
            # valid code                                                      #
            FOR d TO upc digits - 1 DO print( ( whole( ( digits OF code )[ d ], -2 ) ) ) OD;
            print( ( " valid" ) );
            IF inverted OF code THEN print( ( " (inverted)" ) ) FI;
            print( ( newline ) )
        FI
    OD
END
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 (inverted)
 4: 9 3 9 8 2 5 1 5 8 8 1 valid (inverted)
 5:                       error: invalid digit 12
 6: 3 1 6 3 1 3 7 1 8 7 1 valid (inverted)
 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 (inverted)
 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

AutoHotkey

UPC2Dec(code){
    lBits :={"   ## #":0,"  ##  #":1,"  #  ##":2," #### #":3," #   ##":4," ##   #":5," # ####":6," ### ##":7," ## ###":8,"   # ##":9}
    xlBits:={"# ##   ":0,"#  ##  ":1,"##  #  ":2,"# #### ":3,"##   # ":4,"#   ## ":5,"#### # ":6,"## ### ":7,"### ## ":8,"## #   ":9}
    rBits :={"###  # ":0,"##  ## ":1,"## ##  ":2,"#    # ":3,"# ###  ":4,"#  ### ":5,"# #    ":6,"#   #  ":7,"#  #   ":8,"### #  ":9}
    xrBits:={" #  ###":0," ##  ##":1,"  ## ##":2," #    #":3,"  ### #":4," ###  #":5,"    # #":6,"  #   #":7,"   #  #":8,"  # ###":9}
    UPC := "", CD := 0,	code := Trim(code, " ")
    S := SubStr(code, 1, 3), code := SubStr(code, 4)			; start or "upside down" end sequence
    loop 6
        C := SubStr(code, 1, 7), code := SubStr(code, 8)		; six left hand or "upside down" right hand digits
        , UPC := lBits[C] <> "" ? UPC . lBits[C] : xrBits[C] . UPC
    M := SubStr(code, 1, 5), code := SubStr(code, 6)			; middle sequence
    loop 6
        C := SubStr(code, 1, 7), code := SubStr(code, 8)		; six right hand or "upside down" left hand digits
        , UPC := rBits[C] <> "" ? UPC . rBits[C] : xlBits[C] . UPC
    E := SubStr(code, 1, 3), code := SubStr(code, 4)			; end or "upside down" start sequence
    for k, v in StrSplit(UPC)
        CD += Mod(A_Index, 2) ? v*3 : v					; Check Digit
    if (S <> "# #") || (M <> " # # ") || (E <> "# #") || Mod(CD, 10)
        return "Invalid!"
    return UPC
}

Examples:

data =
(
         # #   # ##  #  ## #   ## ### ## ### ## #### # # # ## ##  #   #  ##  ## ###  # ##  ## ### #  # #         
         # # #   ##   ## # #### #   # ## #   ## #   ## # # # ###  # ###  ##  ## ###  # #  ### ###  # # #         
         # #    # # #  ###  #   #    # #  #   #    # # # # ## #   ## #   ## #   ##   # # #### ### ## # #         
         # # ##  ## ##  ##   #  #   #  # ###  # ##  ## # # #   ## ##  #  ### ## ## #   # #### ## #   # #         
         # # ### ## #   ## ## ###  ##  # ##   #   # ## # # ### #  ## ##  #    # ### #  ## ##  #      # #         
         # #  #   # ##  ##  #   #   #  # ##  ##  #   # # # # #### #  ##  # #### #### # #  ##  # #### # #         
         # #  #  ##  ##  # #   ## ##   # ### ## ##   # # # #  #   #   #  #  ### # #    ###  # #  #   # #         
         # # #    # ##  ##   #  # ##  ##  ### #   #  # # # ### ## ## ### ## ### ### ## #  ##  ### ## # #         
         # # ### ##   ## # # #### #   ## # #### # #### # # #   #  # ###  #    # ###  # #    # ###  # # #         
         # # # #### ##   # #### # #   ## ## ### #### # # # #  ### # ###  ###  # # ###  #    # #  ### # #         
)
for i, code in StrSplit(data, "`n", "`r")
    output .= i ":`t" UPC2Dec(code) "`n"
MsgBox, 262144, ,% output
return
Output:
1:	924773271019
2:	403944441050
3:	834999676706
4:	939825158811
5:	Invalid!
6:	316313718717
7:	214575875608
8:	818778841813
9:	706466743030
10:	653483540435

AWK

# 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++) {
#     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)
}
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

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#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];
        }

#pragma warning(push)
#pragma warning(disable : 6386)
        // if len + 1 bytes are allocated, and len bytes are writable, there is no buffer overrun
        rev[len] = 0;
#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;
}
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

C++

Translation of: Kotlin
#include <iostream>
#include <locale>
#include <map>
#include <vector>

std::string trim(const std::string &str) {
    auto s = str;

    //rtrim
    auto it1 = std::find_if(s.rbegin(), s.rend(), [](char ch) { return !std::isspace<char>(ch, std::locale::classic()); });
    s.erase(it1.base(), s.end());

    //ltrim
    auto it2 = std::find_if(s.begin(), s.end(), [](char ch) { return !std::isspace<char>(ch, std::locale::classic()); });
    s.erase(s.begin(), it2);

    return s;
}

template <typename T>
std::ostream &operator<<(std::ostream &os, const std::vector<T> &v) {
    auto it = v.cbegin();
    auto end = v.cend();

    os << '[';
    if (it != end) {
        os << *it;
        it = std::next(it);
    }
    while (it != end) {
        os << ", " << *it;
        it = std::next(it);
    }
    return os << ']';
}

const std::map<std::string, int> LEFT_DIGITS = {
    {"   ## #", 0},
    {"  ##  #", 1},
    {"  #  ##", 2},
    {" #### #", 3},
    {" #   ##", 4},
    {" ##   #", 5},
    {" # ####", 6},
    {" ### ##", 7},
    {" ## ###", 8},
    {"   # ##", 9}
};

const std::map<std::string, int> RIGHT_DIGITS = {
    {"###  # ", 0},
    {"##  ## ", 1},
    {"## ##  ", 2},
    {"#    # ", 3},
    {"# ###  ", 4},
    {"#  ### ", 5},
    {"# #    ", 6},
    {"#   #  ", 7},
    {"#  #   ", 8},
    {"### #  ", 9}
};

const std::string END_SENTINEL = "# #";
const std::string MID_SENTINEL = " # # ";

void decodeUPC(const std::string &input) {
    auto decode = [](const std::string &candidate) {
        using OT = std::vector<int>;
        OT output;
        size_t pos = 0;

        auto part = candidate.substr(pos, END_SENTINEL.length());
        if (part == END_SENTINEL) {
            pos += END_SENTINEL.length();
        } else {
            return std::make_pair(false, OT{});
        }

        for (size_t i = 0; i < 6; i++) {
            part = candidate.substr(pos, 7);
            pos += 7;

            auto e = LEFT_DIGITS.find(part);
            if (e != LEFT_DIGITS.end()) {
                output.push_back(e->second);
            } else {
                return std::make_pair(false, output);
            }
        }

        part = candidate.substr(pos, MID_SENTINEL.length());
        if (part == MID_SENTINEL) {
            pos += MID_SENTINEL.length();
        } else {
            return std::make_pair(false, OT{});
        }

        for (size_t i = 0; i < 6; i++) {
            part = candidate.substr(pos, 7);
            pos += 7;

            auto e = RIGHT_DIGITS.find(part);
            if (e != RIGHT_DIGITS.end()) {
                output.push_back(e->second);
            } else {
                return std::make_pair(false, output);
            }
        }

        part = candidate.substr(pos, END_SENTINEL.length());
        if (part == END_SENTINEL) {
            pos += END_SENTINEL.length();
        } else {
            return std::make_pair(false, OT{});
        }

        int sum = 0;
        for (size_t i = 0; i < output.size(); i++) {
            if (i % 2 == 0) {
                sum += 3 * output[i];
            } else {
                sum += output[i];
            }
        }
        return std::make_pair(sum % 10 == 0, output);
    };

    auto candidate = trim(input);

    auto out = decode(candidate);
    if (out.first) {
        std::cout << out.second << '\n';
    } else {
        std::reverse(candidate.begin(), candidate.end());
        out = decode(candidate);
        if (out.first) {
            std::cout << out.second << " Upside down\n";
        } else if (out.second.size()) {
            std::cout << "Invalid checksum\n";
        } else {
            std::cout << "Invalid digit(s)\n";
        }
    }
}

int main() {
    std::vector<std::string> barcodes = {
        "         # #   # ##  #  ## #   ## ### ## ### ## #### # # # ## ##  #   #  ##  ## ###  # ##  ## ### #  # #       ",
        "        # # #   ##   ## # #### #   # ## #   ## #   ## # # # ###  # ###  ##  ## ###  # #  ### ###  # # #         ",
        "         # #    # # #  ###  #   #    # #  #   #    # # # # ## #   ## #   ## #   ##   # # #### ### ## # #         ",
        "       # # ##  ## ##  ##   #  #   #  # ###  # ##  ## # # #   ## ##  #  ### ## ## #   # #### ## #   # #        ",
        "         # # ### ## #   ## ## ###  ##  # ##   #   # ## # # ### #  ## ##  #    # ### #  ## ##  #      # #          ",
        "          # #  #   # ##  ##  #   #   #  # ##  ##  #   # # # # #### #  ##  # #### #### # #  ##  # #### # #         ",
        "         # #  #  ##  ##  # #   ## ##   # ### ## ##   # # # #  #   #   #  #  ### # #    ###  # #  #   # #        ",
        "        # # #    # ##  ##   #  # ##  ##  ### #   #  # # # ### ## ## ### ## ### ### ## #  ##  ### ## # #         ",
        "         # # ### ##   ## # # #### #   ## # #### # #### # # #   #  # ###  #    # ###  # #    # ###  # # #       ",
        "        # # # #### ##   # #### # #   ## ## ### #### # # # #  ### # ###  ###  # # ###  #    # #  ### # #         ",
    };
    for (auto &barcode : barcodes) {
        decodeUPC(barcode);
    }
    return 0;
}
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]

D

Translation of: C++
import std.algorithm : countUntil, each, map;
import std.array : array;
import std.conv : to;
import std.range : empty, retro;
import std.stdio : writeln;
import std.string : strip;
import std.typecons : tuple;

immutable LEFT_DIGITS = [
    "   ## #",
    "  ##  #",
    "  #  ##",
    " #### #",
    " #   ##",
    " ##   #",
    " # ####",
    " ### ##",
    " ## ###",
    "   # ##"
];
immutable RIGHT_DIGITS = LEFT_DIGITS.map!`a.map!"a == '#' ? ' ' : '#'".array`.array;

immutable END_SENTINEL = "# #";
immutable MID_SENTINEL = " # # ";

void decodeUPC(string input) {
    auto decode(string candidate) {
        int[] output;
        size_t pos = 0;

        auto next = pos + END_SENTINEL.length;
        auto part = candidate[pos .. next];
        if (part == END_SENTINEL) {
            pos = next;
        } else {
            return tuple(false, cast(int[])[]);
        }

        foreach (_; 0..6) {
            next = pos + 7;
            part = candidate[pos .. next];
            auto i = countUntil(LEFT_DIGITS, part);
            if (i >= 0) {
                output ~= i;
                pos = next;
            } else {
                return tuple(false, cast(int[])[]);
            }
        }

        next = pos + MID_SENTINEL.length;
        part = candidate[pos .. next];
        if (part == MID_SENTINEL) {
            pos = next;
        } else {
            return tuple(false, cast(int[])[]);
        }

        foreach (_; 0..6) {
            next = pos + 7;
            part = candidate[pos .. next];
            auto i = countUntil(RIGHT_DIGITS, part);
            if (i >= 0) {
                output ~= i;
                pos = next;
            } else {
                return tuple(false, cast(int[])[]);
            }
        }

        next = pos + END_SENTINEL.length;
        part = candidate[pos .. next];
        if (part == END_SENTINEL) {
            pos = next;
        } else {
            return tuple(false, cast(int[])[]);
        }

        int sum = 0;
        foreach (i,v; output) {
            if (i % 2 == 0) {
                sum += 3 * v;
            } else {
                sum += v;
            }
        }
        return tuple(sum % 10 == 0, output);
    }

    auto candidate = input.strip;
    auto output = decode(candidate);
    if (output[0]) {
        writeln(output[1]);
    } else {
        output = decode(candidate.retro.array.to!string);
        if (output[0]) {
            writeln(output[1], " Upside down");
        } else if (output[1].empty) {
            writeln("Invalid digit(s)");
        } else {
            writeln("Invalid checksum", output);
        }
    }
}

void main() {
    auto barcodes = [
        "         # #   # ##  #  ## #   ## ### ## ### ## #### # # # ## ##  #   #  ##  ## ###  # ##  ## ### #  # #       ",
        "        # # #   ##   ## # #### #   # ## #   ## #   ## # # # ###  # ###  ##  ## ###  # #  ### ###  # # #         ",
        "         # #    # # #  ###  #   #    # #  #   #    # # # # ## #   ## #   ## #   ##   # # #### ### ## # #         ",
        "       # # ##  ## ##  ##   #  #   #  # ###  # ##  ## # # #   ## ##  #  ### ## ## #   # #### ## #   # #        ",
        "         # # ### ## #   ## ## ###  ##  # ##   #   # ## # # ### #  ## ##  #    # ### #  ## ##  #      # #          ",
        "          # #  #   # ##  ##  #   #   #  # ##  ##  #   # # # # #### #  ##  # #### #### # #  ##  # #### # #         ",
        "         # #  #  ##  ##  # #   ## ##   # ### ## ##   # # # #  #   #   #  #  ### # #    ###  # #  #   # #        ",
        "        # # #    # ##  ##   #  # ##  ##  ### #   #  # # # ### ## ## ### ## ### ### ## #  ##  ### ## # #         ",
        "         # # ### ##   ## # # #### #   ## # #### # #### # # #   #  # ###  #    # ###  # #    # ###  # # #       ",
        "        # # # #### ##   # #### # #   ## ## ### #### # # # #  ### # ###  ###  # # ###  #    # #  ### # #         ",
    ];
    barcodes.each!decodeUPC;
}
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]

EasyLang

proc trim . s$ .
   a = 1
   while substr s$ a 1 = " "
      a += 1
   .
   b = len s$
   while substr s$ b 1 = " "
      b -= 1
   .
   s$ = substr s$ a (b - a + 1)
.
func$ rev s$ .
   a$[] = strchars s$
   for i to len a$[] div 2
      swap a$[i] a$[len a$[] - i + 1]
   .
   return strjoin a$[]
.
func$ invert s$ .
   for c$ in strchars s$
      if c$ = " "
         r$ &= "#"
      else
         r$ &= " "
      .
   .
   return r$
.
digs$[] = [ "   ## #" "  ##  #" "  #  ##" " #### #" " #   ##" " ##   #" " # ####" " ### ##" " ## ###" "   # ##" ]
func[] decode_upc upc$ .
   subr getdigs
      for _ to 6
         h$ = substr upc$ pos 7
         for dig to 10
            d$ = digs$[dig]
            if isright = 1
               d$ = invert d$
            .
            if h$ = d$
               break 1
            .
         .
         if dig = 11
            return [ ]
         .
         dig -= 1
         digs[] &= dig
         sum += dig * sumf
         sumf = 4 - sumf
         pos = pos + 7
      .
   .
   if len upc$ <> 95
      return [ ]
   .
   if substr upc$ 1 3 <> "# #"
      return [ ]
   .
   pos = 4
   sumf = 3
   getdigs
   if substr upc$ pos 5 <> " # # "
      return [ ]
   .
   pos += 5
   isright = 1
   getdigs
   if substr upc$ 1 3 <> "# #"
      return [ ]
   .
   if sum mod 10 <> 0
      return [ ]
   .
   return digs[]
.
barcodes$[] = [ "         # #   # ##  #  ## #   ## ### ## ### ## #### # # # ## ##  #   #  ##  ## ###  # ##  ## ### #  # #       " "        # # #   ##   ## # #### #   # ## #   ## #   ## # # # ###  # ###  ##  ## ###  # #  ### ###  # # #         " "         # #    # # #  ###  #   #    # #  #   #    # # # # ## #   ## #   ## #   ##   # # #### ### ## # #         " "       # # ##  ## ##  ##   #  #   #  # ###  # ##  ## # # #   ## ##  #  ### ## ## #   # #### ## #   # #        " "         # # ### ## #   ## ## ###  ##  # ##   #   # ## # # ### #  ## ##  #    # ### #  ## ##  #      # #          " "          # #  #   # ##  ##  #   #   #  # ##  ##  #   # # # # #### #  ##  # #### #### # #  ##  # #### # #         " "         # #  #  ##  ##  # #   ## ##   # ### ## ##   # # # #  #   #   #  #  ### # #    ###  # #  #   # #        " "        # # #    # ##  ##   #  # ##  ##  ### #   #  # # # ### ## ## ### ## ### ### ## #  ##  ### ## # #         " "         # # ### ##   ## # # #### #   ## # #### # #### # # #   #  # ###  #    # ###  # #    # ###  # # #       " "        # # # #### ##   # #### # #   ## ## ### #### # # # #  ### # ###  ###  # # ###  #    # #  ### # #         " ]
for s$ in barcodes$[]
   trim s$
   r[] = decode_upc s$
   if r[] = [ ]
      r[] = decode_upc rev s$
   .
   print r[]
.
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 ]
[ ]
[ 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 ]

Factor

Works with: Factor version 0.99 2019-10-06
USING: combinators combinators.short-circuit formatting grouping
kernel locals math math.functions 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 divisor? ;

: 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
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 }

FreeBASIC

Translation of: Phix
Sub decode(bar_code As String)
    Dim As String numbers(9) => { _
    "   ## #", _ '-- 0
    "  ##  #", _ '-- 1
    "  #  ##", _ '-- 2
    " #### #", _ '-- 3
    " #   ##", _ '-- 4
    " ##   #", _ '-- 5
    " # ####", _ '-- 6
    " ### ##", _ '-- 7
    " ## ###", _ '-- 8
    "   # ##" }  '-- 9
    Dim As Integer i, j
    Dim As String temp
    
    bar_code = Trim(bar_code)
    If Len(bar_code) = 95 And Mid(bar_code, 1, 3) = "# #" And Mid(bar_code, 46, 5) = " # # " And Mid(bar_code, 93, 3) = "# #" Then
        For reversed As Integer = 0 To 1
            Dim r(11) As Integer
            For i = 1 To 12
                Dim st As Integer = Iif(i <= 6, i * 7 - 3, i * 7 + 2)
                Dim number As String = Mid(bar_code, st, 7)
                If i > 6 Then
                    temp = ""
                    For j = 1 To Len(number)
                        Select Case Mid(number, j, 1) 
                        Case " " : temp += "#"
                        Case "#" : temp += " "
                        Case Else: temp += Mid(number, j, 1)
                        End Select
                    Next
                    number = temp
                End If
                r(i - 1) = -1
                For j = 0 To 9
                    If number = numbers(j) Then
                        r(i - 1) = j
                        Exit For
                    End If
                Next
            Next
            Dim valid As Integer = 1
            For i = 0 To 11
                If r(i) = -1 Then
                    valid = 0
                    Exit For
                End If
            Next
            If valid Then
                Dim sum As Integer = 0
                For i = 0 To 11
                    sum += r(i) * Iif(i Mod 2 = 0, 3, 1)
                Next
                If sum Mod 10 = 0 Then
                    For i = 0 To 11
                        Print r(i);
                    Next
                    Print Iif(reversed, " (upside down)", "")
                Else
                    Print "invalid checksum"
                End If
                Exit Sub
            End If
            temp = ""
            For i = Len(bar_code) To 1 Step -1
                temp += Mid(bar_code, i, 1)
            Next
            bar_code = temp
        Next
    End If
    Print " invalid"
End Sub

Dim bar_codes(9) As String
bar_codes(0) = "         # #   # ##  #  ## #   ## ### ## ### ## #### # # # ## ##  #   #  ##  ## ###  # ##  ## ### #  # #       "
bar_codes(1) = "        # # #   ##   ## # #### #   # ## #   ## #   ## # # # ###  # ###  ##  ## ###  # #  ### ###  # # #         "
bar_codes(2) = "         # #    # # #  ###  #   #    # #  #   #    # # # # ## #   ## #   ## #   ##   # # #### ### ## # #         "
bar_codes(3) = "       # # ##  ## ##  ##   #  #   #  # ###  # ##  ## # # #   ## ##  #  ### ## ## #   # #### ## #   # #        "
bar_codes(4) = "         # # ### ## #   ## ## ###  ##  # ##   #   # ## # # ### #  ## ##  #    # ### #  ## ##  #      # #          "
bar_codes(5) = "          # #  #   # ##  ##  #   #   #  # ##  ##  #   # # # # #### #  ##  # #### #### # #  ##  # #### # #         "
bar_codes(6) = "         # #  #  ##  ##  # #   ## ##   # ### ## ##   # # # #  #   #   #  #  ### # #    ###  # #  #   # #        "
bar_codes(7) = "        # # #    # ##  ##   #  # ##  ##  ### #   #  # # # ### ## ## ### ## ### ### ## #  ##  ### ## # #         "
bar_codes(8) = "         # # ### ##   ## # # #### #   ## # #### # #### # # #   #  # ###  #    # ###  # #    # ###  # # #       "
bar_codes(9) = "        # # # #### ##   # #### # #   ## ## ### #### # # # #  ### # ###  ###  # # ###  #    # #  ### # #         "

For i As Integer = 0 To 9
    decode(bar_codes(i))
Next

Sleep
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

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
            }
        }
    }
}
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:

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.
)

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

Task example:

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

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.

Java

Translation of: Kotlin
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

public class UPC {
    private static final int SEVEN = 7;

    private static final Map<String, Integer> LEFT_DIGITS = Map.of(
        "   ## #", 0,
        "  ##  #", 1,
        "  #  ##", 2,
        " #### #", 3,
        " #   ##", 4,
        " ##   #", 5,
        " # ####", 6,
        " ### ##", 7,
        " ## ###", 8,
        "   # ##", 9
    );

    private static final Map<String, Integer> RIGHT_DIGITS = LEFT_DIGITS.entrySet()
        .stream()
        .collect(Collectors.toMap(
            entry -> entry.getKey()
                .replace(' ', 's')
                .replace('#', ' ')
                .replace('s', '#'),
            Map.Entry::getValue
        ));

    private static final String END_SENTINEL = "# #";
    private static final String MID_SENTINEL = " # # ";

    private static void decodeUPC(String input) {
        Function<String, Map.Entry<Boolean, List<Integer>>> decode = (String candidate) -> {
            int pos = 0;
            var part = candidate.substring(pos, pos + END_SENTINEL.length());

            List<Integer> output = new ArrayList<>();
            if (END_SENTINEL.equals(part)) {
                pos += END_SENTINEL.length();
            } else {
                return Map.entry(false, output);
            }

            for (int i = 1; i < SEVEN; i++) {
                part = candidate.substring(pos, pos + SEVEN);
                pos += SEVEN;

                if (LEFT_DIGITS.containsKey(part)) {
                    output.add(LEFT_DIGITS.get(part));
                } else {
                    return Map.entry(false, output);
                }
            }

            part = candidate.substring(pos, pos + MID_SENTINEL.length());
            if (MID_SENTINEL.equals(part)) {
                pos += MID_SENTINEL.length();
            } else {
                return Map.entry(false, output);
            }

            for (int i = 1; i < SEVEN; i++) {
                part = candidate.substring(pos, pos + SEVEN);
                pos += SEVEN;

                if (RIGHT_DIGITS.containsKey(part)) {
                    output.add(RIGHT_DIGITS.get(part));
                } else {
                    return Map.entry(false, output);
                }
            }

            part = candidate.substring(pos, pos + END_SENTINEL.length());
            if (!END_SENTINEL.equals(part)) {
                return Map.entry(false, output);
            }

            int sum = 0;
            for (int i = 0; i < output.size(); i++) {
                if (i % 2 == 0) {
                    sum += 3 * output.get(i);
                } else {
                    sum += output.get(i);
                }
            }
            return Map.entry(sum % 10 == 0, output);
        };

        Consumer<List<Integer>> printList = list -> {
            var it = list.iterator();
            System.out.print('[');
            if (it.hasNext()) {
                System.out.print(it.next());
            }
            while (it.hasNext()) {
                System.out.print(", ");
                System.out.print(it.next());
            }
            System.out.print(']');
        };

        var candidate = input.trim();
        var out = decode.apply(candidate);
        if (out.getKey()) {
            printList.accept(out.getValue());
            System.out.println();
        } else {
            StringBuilder builder = new StringBuilder(candidate);
            builder.reverse();
            out = decode.apply(builder.toString());
            if (out.getKey()) {
                printList.accept(out.getValue());
                System.out.println(" Upside down");
            } else if (out.getValue().size() == 12) {
                System.out.println("Invalid checksum");
            } else {
                System.out.println("Invalid digit(s)");
            }
        }
    }

    public static void main(String[] args) {
        var barcodes = List.of(
            "         # #   # ##  #  ## #   ## ### ## ### ## #### # # # ## ##  #   #  ##  ## ###  # ##  ## ### #  # #       ",
            "        # # #   ##   ## # #### #   # ## #   ## #   ## # # # ###  # ###  ##  ## ###  # #  ### ###  # # #         ",
            "         # #    # # #  ###  #   #    # #  #   #    # # # # ## #   ## #   ## #   ##   # # #### ### ## # #         ",
            "       # # ##  ## ##  ##   #  #   #  # ###  # ##  ## # # #   ## ##  #  ### ## ## #   # #### ## #   # #        ",
            "         # # ### ## #   ## ## ###  ##  # ##   #   # ## # # ### #  ## ##  #    # ### #  ## ##  #      # #          ",
            "          # #  #   # ##  ##  #   #   #  # ##  ##  #   # # # # #### #  ##  # #### #### # #  ##  # #### # #         ",
            "         # #  #  ##  ##  # #   ## ##   # ### ## ##   # # # #  #   #   #  #  ### # #    ###  # #  #   # #        ",
            "        # # #    # ##  ##   #  # ##  ##  ### #   #  # # # ### ## ## ### ## ### ### ## #  ##  ### ## # #         ",
            "         # # ### ##   ## # # #### #   ## # #### # #### # # #   #  # ###  #    # ###  # #    # ###  # # #       ",
            "        # # # #### ##   # #### # #   ## ## ### #### # # # #  ### # ###  ###  # # ###  #    # #  ### # #         "
        );
        barcodes.forEach(UPC::decodeUPC);
    }
}
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]

jq

Adapted from Wren

Works with: jq

Also works with gojq, the Go implementation of jq

## Generic utilities

def enumerate(stream): foreach stream as $x (0; .+1; "\(.): \($x)");
def reverseString: explode | reverse | implode;

## UPC functions

def digitL: {
    "   ## #": 0,
    "  ##  #": 1,
    "  #  ##": 2,
    " #### #": 3,
    " #   ##": 4,
    " ##   #": 5,
    " # ####": 6,
    " ### ##": 7,
    " ## ###": 8,
    "   # ##": 9
};

def digitR: {
    "###  # ": 0,
    "##  ## ": 1,
    "## ##  ": 2,
    "#    # ": 3,
    "# ###  ": 4,
    "#  ### ": 5,
    "# #    ": 6,
    "#   #  ": 7,
    "#  #   ": 8,
    "### #  ": 9
};

def endSentinel: "# #";  #  also at start

def midSentinel:" # # ";

def decodeUpc:
  # Emit {rc: boolean, message: string} 
  def decodeUpcImpl:
  
    # The "nil" result
    def nil: { rc: false, message: ""};

    (sub("^  *";"") | sub(" *$"; "")) as $upc
    | if ($upc|length != 95) then nil
      else [1, 3] as $oneThree
      | { pos: 0,
          digits: [],
          sum:  0 }
      | # end sentinel
        if $upc[.pos: .pos+3] != endSentinel
        then nil
        else .pos += 3
        # 6 left hand digits
        | .i = 0
        | until( .i == 6 or .i == null;
            .i += 1
            | digitL[$upc[.pos: .pos+7]] as $digit
            | if ($digit|not) then .i = null
              else .digits += [$digit]
              | .sum += $digit * $oneThree[(.digits|length) % 2]
              | .pos += 7
              end )
        | if .i == null then nil
          else # mid sentinel
            if $upc[.pos: .pos+5] != midSentinel
            then nil
            else .pos += 5
            # 6 right hand digits
            | .i = 0
            | until( .i == 6 or .i == null;
                .i += 1
                | digitR[$upc[.pos: .pos+7]] as $digit
                | if ($digit|not) then .i = null
                  else .digits += [$digit]
                  | .sum += $digit * $oneThree[(.digits|length) % 2]
                  | .pos += 7
                  end )
            | if .i == null then nil
              else # end sentinel
                if ($upc[.pos : .pos+3] != endSentinel) then false
                else if (.sum % 10 == 0)
                     then {rc: true,  message: "\(.digits) "}
                     else {rc: false, message: "Failed Checksum "}
                     end
                end
              end
            end
          end
        end
      end ;

    . as $in
    | decodeUpcImpl
    | .message 
      + (if .rc then "Rightside Up"
         else ($in | reverseString | decodeUpcImpl)
         | .message
           + (if .rc then "Upside Down"
              else "Invalid digit(s)"
              end)
         end);

## Examples

def barcodes: [
    "         # #   # ##  #  ## #   ## ### ## ### ## #### # # # ## ##  #   #  ##  ## ###  # ##  ## ### #  # #       ",
    "        # # #   ##   ## # #### #   # ## #   ## #   ## # # # ###  # ###  ##  ## ###  # #  ### ###  # # #         ",
    "         # #    # # #  ###  #   #    # #  #   #    # # # # ## #   ## #   ## #   ##   # # #### ### ## # #         ",
    "       # # ##  ## ##  ##   #  #   #  # ###  # ##  ## # # #   ## ##  #  ### ## ## #   # #### ## #   # #        ",
    "         # # ### ## #   ## ## ###  ##  # ##   #   # ## # # ### #  ## ##  #    # ### #  ## ##  #      # #          ",
    "          # #  #   # ##  ##  #   #   #  # ##  ##  #   # # # # #### #  ##  # #### #### # #  ##  # #### # #         ",
    "         # #  #  ##  ##  # #   ## ##   # ### ## ##   # # # #  #   #   #  #  ### # #    ###  # #  #   # #        ",
    "        # # #    # ##  ##   #  # ##  ##  ### #   #  # # # ### ## ## ### ## ### ### ## #  ##  ### ## # #         ",
    "         # # ### ##   ## # # #### #   ## # #### # #### # # #   #  # ###  #    # ###  # #    # ###  # # #       ",
    "        # # # #### ##   # #### # #   ## ## ### #### # # # #  ### # ###  ###  # # ###  #    # #  ### # #         "
];

enumerate( barcodes[] | decodeUpc )
Output:

Essentially as for Wren.


Julia

Translation of: Raku
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
Output:
924773271019
403944441050
834999676706
939825158811
Invalid
316313718717
214575875608
818778841813
706466743030
653483540435

Kotlin

Translation of: C
val LEFT_DIGITS = mapOf(
    "   ## #" to 0,
    "  ##  #" to 1,
    "  #  ##" to 2,
    " #### #" to 3,
    " #   ##" to 4,
    " ##   #" to 5,
    " # ####" to 6,
    " ### ##" to 7,
    " ## ###" to 8,
    "   # ##" to 9
)
val RIGHT_DIGITS = LEFT_DIGITS.mapKeys {
    it.key.replace(' ', 's').replace('#', ' ').replace('s', '#')
}

const val END_SENTINEL = "# #"
const val MID_SENTINEL = " # # "

fun decodeUPC(input: String) {
    fun decode(candidate: String): Pair<Boolean, List<Int>> {
        var pos = 0
        var part = candidate.slice(pos until pos + END_SENTINEL.length)
        if (part == END_SENTINEL) {
            pos += END_SENTINEL.length
        } else {
            return Pair(false, emptyList())
        }

        val output = mutableListOf<Int>()
        for (i in 0 until 6) {
            part = candidate.slice(pos until pos + 7)
            pos += 7

            if (LEFT_DIGITS.containsKey(part)) {
                output.add(LEFT_DIGITS.getOrDefault(part, -1))
            } else {
                return Pair(false, output.toList())
            }
        }

        part = candidate.slice(pos until pos + MID_SENTINEL.length)
        if (part == MID_SENTINEL) {
            pos += MID_SENTINEL.length
        } else {
            return Pair(false, output.toList())
        }

        for (i in 0 until 6) {
            part = candidate.slice(pos until pos + 7)
            pos += 7

            if (RIGHT_DIGITS.containsKey(part)) {
                output.add(RIGHT_DIGITS.getOrDefault(part, -1))
            } else {
                return Pair(false, output.toList())
            }
        }

        part = candidate.slice(pos until pos + END_SENTINEL.length)
        if (part == END_SENTINEL) {
            pos += END_SENTINEL.length
        } else {
            return Pair(false, output.toList())
        }

        val sum = output.mapIndexed { i, v -> if (i % 2 == 0) v * 3 else v }.sum()
        return Pair(sum % 10 == 0, output.toList())
    }

    val candidate = input.trim()

    var out = decode(candidate)
    if (out.first) {
        println(out.second)
    } else {
        out = decode(candidate.reversed())
        if (out.first) {
            print(out.second)
            println(" Upside down")
        } else {
            if (out.second.size == 12) {
                println("Invalid checksum")
            } else {
                println("Invalid digit(s)")
            }
        }
    }

}

fun main() {
    val barcodes = listOf(
        "         # #   # ##  #  ## #   ## ### ## ### ## #### # # # ## ##  #   #  ##  ## ###  # ##  ## ### #  # #       ",
        "        # # #   ##   ## # #### #   # ## #   ## #   ## # # # ###  # ###  ##  ## ###  # #  ### ###  # # #         ",
        "         # #    # # #  ###  #   #    # #  #   #    # # # # ## #   ## #   ## #   ##   # # #### ### ## # #         ",
        "       # # ##  ## ##  ##   #  #   #  # ###  # ##  ## # # #   ## ##  #  ### ## ## #   # #### ## #   # #        ",
        "         # # ### ## #   ## ## ###  ##  # ##   #   # ## # # ### #  ## ##  #    # ### #  ## ##  #      # #          ",
        "          # #  #   # ##  ##  #   #   #  # ##  ##  #   # # # # #### #  ##  # #### #### # #  ##  # #### # #         ",
        "         # #  #  ##  ##  # #   ## ##   # ### ## ##   # # # #  #   #   #  #  ### # #    ###  # #  #   # #        ",
        "        # # #    # ##  ##   #  # ##  ##  ### #   #  # # # ### ## ## ### ## ### ### ## #  ##  ### ## # #         ",
        "         # # ### ##   ## # # #### #   ## # #### # #### # # #   #  # ###  #    # ###  # #    # ###  # # #       ",
        "        # # # #### ##   # #### # #   ## ## ### #### # # # #  ### # ###  ###  # # ###  #    # #  ### # #         ",
    )

    for (barcode in barcodes) {
        decodeUPC(barcode)
    }
}
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]

Ksh

#!/bin/ksh

# Find the corresponding UPC decimal representation of each, rejecting the error

#	# Variables:
#
UPC_data_file="../upc.data"
END_SEQ='# #'		# Start and End sequence
MID_SEQ=' # # '		# Middle sequence
typeset -a CHK_ARR=( 3 1 3 1 3 1 3 1 3 1 3 1 )
integer bpd=7		# Number of bits per digit
integer numdig=6	# Number of digits per "side"
typeset -a umess=( '' 'Upside down')
typeset -a udig=( 0001101 0011001 0010011 0111101 0100011 0110001 0101111 0111011 0110111 0001011 )

#	# Functions:
#

#	# Function _validate(array) - verify result with CHK_ARR
#
function _validate {
	typeset _arr ; nameref _arr="$1"
	typeset _ifs ; _ifs="$2"
	typeset _dp _singlearr _oldIFS

	_oldIFS=$IFS ; IFS=${_ifs}
	typeset -ia _singlearr=( ${_arr[@]} )
	integer _dp=$(_dotproduct _singlearr CHK_ARR)
	IFS=${_oldIFS}

	return $(( _dp % 10 ))
}

#	# Function _dotproduct(arr1, arr2) - return dot product
#
function _dotproduct {
	typeset _arr1 ; nameref _arr1="$1"
	typeset _arr2 ; nameref _arr2="$2"
	typeset _i _dp ; integer _i _dp

	for (( _i=0; _i<${#_arr1[*]}; _i++ )); do
		(( _dp += ( _arr1[_i] * _arr2[_i] ) ))
	done
	echo ${_dp}
}

#	# Function _flipit(string) - return flipped string
#
function _flipit {
	typeset _buf ; _buf="$1"
	typeset _tmp ; unset _tmp

	for (( _i=$(( ${#_buf}-1 )); _i>=0; _i-- )); do
		_tmp="${_tmp}${_buf:${_i}:1}"
	done

	echo "${_tmp}"
}

#	# Function _bitget(string, side) - return bitless string & bit
#
function _bitget {
	typeset _buff ; _buff="$1"
	typeset _side ; integer _side=$2
	typeset _ubit _bit

	_ubit=${_buff:0:1}
	[[ ${_ubit} == \# ]] && _bit=1 || _bit=0
	(( _side )) && (( _bit = ! _bit ))

	echo ${_buff#*${_ubit}}
	return ${_bit}
}

#	# Function _decode(upc_arr, digit_arr)
#
function _decode {
	typeset _uarr ; nameref _uarr="$1"	# UPC code array
	typeset _darr ; nameref _darr="$2"	# Decimal array

	typeset _s _d _b _bit _digit _uarrcopy ; integer _s _d _b _bit
	typeset -a _uarrcopy=( ${_uarr[@]} )

	for (( _s=0; _s<${#_uarr[*]}; _s++ )); do	# each "side"
		for (( _d=0; _d<numdig; _d++ ))  ; do	# each "digit"
			for (( _b=0; _b<bpd; _b++ )) ; do	# each "bit"
				_uarr[_s]=$(_bitget ${_uarr[_s]} ${_s}) ; _bit=$?
				_digit="${_digit}${_bit}"
			done

			_darr[_s]="${_darr[_s]} $(_todec ${_digit})"
			if (( $? )); then		# May be upside-down
				typeset -a _uarr=( ${_uarrcopy[@]} )	# Replace
				return 1
			fi
			unset _digit
		done
	done
}

#	# Function _todec(digit) - Return numeric digit from upc code
#
function _todec {
	typeset _bdig ; _bdig="$1"
	typeset _i ; integer _i

	for (( _i=0; _i<${#udig[*]}; _i++ )); do
		[[ ${_bdig} == ${udig[_i]} ]] && echo ${_i} && return 0
	done
	return 1
}

#	# Function _parseUPC(str, arr) - parse UPS string into 2 ele array
#
function _parseUPC {
	typeset _buf ; typeset _buf="$1"
	typeset _arr ; nameref _arr="$2"
	typeset _pre _mid

	_pre="${_buf%%${END_SEQ}*}"
	_buf="${_buf#*${_pre}}"				# Strip preamble
	_buf="${_buf#*${END_SEQ}}"			# Strip $SEQ

	_arr[0]="${_buf:0:$((bpd * numdig))}"	# Get the left hand digits
	_buf="${_buf#*${_arr[0]}}"				# Strip left side digits

	_mid="${_buf:0:5}"					# Check the middle SEQ
	_buf="${_buf#*${MID_SEQ}}"			# Strip $SEQ

	_arr[1]="${_buf:0:$((bpd * numdig))}"	# Get the right hand digits
	_buf="${_buf#*${_arr[1]}}"				# Strip right side digits

	_end="${_buf:0:3}"					# Check the end SEQ
	_buf="${_buf#*${END_SEQ}}"			# Strip $SEQ
}

 ######
# main #
 ######

oldIFS="$IFS" ; IFS=''
while read; do
	[[ "$REPLY" == \;* ]] && continue

	unset side_arr ; typeset -a side_arr	# [0]=left [1]=right
	_parseUPC "$REPLY" side_arr

	unset digit_arr ; typeset -a digit_arr	# [0]=left [1]=right
	_decode side_arr digit_arr ; integer uflg=$?
	if (( uflg )); then			# Flip sides and reverse UPC_code
		unset digit_arr ; typeset -a digit_arr	# [0]=left [1]=right
		buff="$(_flipit "${side_arr[0]}")"
		side_arr[0]="$(_flipit "${side_arr[1]}")"
		side_arr[1]="${buff}"
		_decode side_arr digit_arr ; integer vflg=$?
	fi

	(( ! vflg )) && _validate digit_arr "${oldIFS}" ; integer vflg=$?
	if (( vflg )); then 
		print "INVALID DIGIT(S)" 
		unset vflg
	else
		print "${digit_arr[*]} ${umess[uflg]}"
		unset uflg
	fi

done < ${UPC_data_file}
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

Mathematica /Wolfram Language

s="         # #   # ##  #  ## #   ## ### ## ### ## #### # # # ## ##  #   #  ##  ## ###  # ##  ## ### #  # #       
        # # #   ##   ## # #### #   # ## #   ## #   ## # # # ###  # ###  ##  ## ###  # #  ### ###  # # #         
         # #    # # #  ###  #   #    # #  #   #    # # # # ## #   ## #   ## #   ##   # # #### ### ## # #         
       # # ##  ## ##  ##   #  #   #  # ###  # ##  ## # # #   ## ##  #  ### ## ## #   # #### ## #   # #        
         # # ### ## #   ## ## ###  ##  # ##   #   # ## # # ### #  ## ##  #    # ### #  ## ##  #      # #          
          # #  #   # ##  ##  #   #   #  # ##  ##  #   # # # # #### #  ##  # #### #### # #  ##  # #### # #         
         # #  #  ##  ##  # #   ## ##   # ### ## ##   # # # #  #   #   #  #  ### # #    ###  # #  #   # #        
        # # #    # ##  ##   #  # ##  ##  ### #   #  # # # ### ## ## ### ## ### ### ## #  ##  ### ## # #         
         # # ### ##   ## # # #### #   ## # #### # #### # # #   #  # ###  #    # ###  # #    # ###  # # #       
        # # # #### ##   # #### # #   ## ## ### #### # # # #  ### # ###  ###  # # ###  #    # #  ### # #         ";
s//=Characters[StringSplit[#,"\n"]]&;
s=%/.{"#"->0," "->1};
Map[BarcodeRecognize@*Image@*List][s]
Output:
{"924773271019", "403944441050", "834999676706", "939825158811", {}, "316313718717", "214575875608", "818778841813", "706466743030", "653483540435"}

Nim

Translation of: D
import algorithm, sequtils, strutils

const

  LeftDigits = ["   ## #",
                "  ##  #",
                "  #  ##",
                " #### #",
                " #   ##",
                " ##   #",
                " # ####",
                " ### ##",
                " ## ###",
                "   # ##"]

  RightDigits = LeftDigits.mapIt(it.multiReplace(("#", " "), (" ", "#")))

  EndSentinel = "# #"
  MidSentinel = " # # "


template isEven(n: int): bool = (n and 1) == 0


proc decodeUPC(input: string) =

  #.................................................................................................

  proc decode(candidate: string): tuple[valid: bool, list: seq[int]] =

    const Invalid = (false, @[])

    var pos = 0
    var next = pos + EndSentinel.len
    if candidate[pos..<next] == EndSentinel:
      pos = next
    else:
      return Invalid

    for _ in 1..6:
      next = pos + 7
      let i = LeftDigits.find(candidate[pos..<next])
      if i >= 0:
        result.list.add i
        pos = next
      else:
        return Invalid

    next = pos + MidSentinel.len
    if candidate[pos..<next] == MidSentinel:
      pos = next
    else:
      return Invalid

    for _ in 1..6:
      next = pos + 7
      let i = RightDigits.find(candidate[pos..<next])
      if i >= 0:
        result.list.add i
        pos = next
      else:
        return Invalid

    next = pos + EndSentinel.len
    if candidate[pos..<next] == EndSentinel:
      pos = next
    else:
      return Invalid

    var sum = 0
    for i, v in result.list:
      sum += (if i.isEven: 3 * v else: v)
    result.valid = sum mod 10 == 0

  #.................................................................................................

  var candidate = input.strip()
  let output = candidate.decode()
  if output.valid:
    echo output.list.join(", ")
  else:
    candidate.reverse()
    let output = candidate.decode()
    if output.valid:
      echo output.list.join(", "), " Upside down"
    elif output.list.len == 0:
      echo "Invalid digit(s)"
    else:
      echo "Invalid checksum: ", output.list.join(", ")


#———————————————————————————————————————————————————————————————————————————————————————————————————

when isMainModule:

  const BarCodes = [
    "         # #   # ##  #  ## #   ## ### ## ### ## #### # # # ## ##  #   #  ##  ## ###  # ##  ## ### #  # #       ",
    "        # # #   ##   ## # #### #   # ## #   ## #   ## # # # ###  # ###  ##  ## ###  # #  ### ###  # # #         ",
    "         # #    # # #  ###  #   #    # #  #   #    # # # # ## #   ## #   ## #   ##   # # #### ### ## # #         ",
    "       # # ##  ## ##  ##   #  #   #  # ###  # ##  ## # # #   ## ##  #  ### ## ## #   # #### ## #   # #        ",
    "         # # ### ## #   ## ## ###  ##  # ##   #   # ## # # ### #  ## ##  #    # ### #  ## ##  #      # #          ",
    "          # #  #   # ##  ##  #   #   #  # ##  ##  #   # # # # #### #  ##  # #### #### # #  ##  # #### # #         ",
    "         # #  #  ##  ##  # #   ## ##   # ### ## ##   # # # #  #   #   #  #  ### # #    ###  # #  #   # #        ",
    "        # # #    # ##  ##   #  # ##  ##  ### #   #  # # # ### ## ## ### ## ### ### ## #  ##  ### ## # #         ",
    "         # # ### ##   ## # # #### #   ## # #### # #### # # #   #  # ###  #    # ###  # #    # ###  # # #       ",
    "        # # # #### ##   # #### # #   ## ## ### #### # # # #  ### # ###  ###  # # ###  #    # #  ### # #         ",
    ]

  for barcode in BarCodes:
    barcode.decodeUPC()
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

Perl

Translation of: Raku
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';
}
Output:
924773271019
403944441050
834999676706
939825158811
Invalid
316313718717
214575875608
818778841813
706466743030
653483540435

Phix

with javascript_semantics
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
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

(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)) )
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)

Python

"""UPC-A barcode reader. Requires Python =>3.6"""
import itertools
import re

RE_BARCODE = re.compile(
    r"^(?P<s_quiet> +)"  # quiet zone
    r"(?P<s_guard># #)"  # start guard
    r"(?P<left>[ #]{42})"  # left digits
    r"(?P<m_guard> # # )"  # middle guard
    r"(?P<right>[ #]{42})"  # right digits
    r"(?P<e_guard># #)"  # end guard
    r"(?P<e_quiet> +)$"  # quiet zone
)

LEFT_DIGITS = {
    (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,
}

RIGHT_DIGITS = {
    (1, 1, 1, 0, 0, 1, 0): 0,
    (1, 1, 0, 0, 1, 1, 0): 1,
    (1, 1, 0, 1, 1, 0, 0): 2,
    (1, 0, 0, 0, 0, 1, 0): 3,
    (1, 0, 1, 1, 1, 0, 0): 4,
    (1, 0, 0, 1, 1, 1, 0): 5,
    (1, 0, 1, 0, 0, 0, 0): 6,
    (1, 0, 0, 0, 1, 0, 0): 7,
    (1, 0, 0, 1, 0, 0, 0): 8,
    (1, 1, 1, 0, 1, 0, 0): 9,
}


MODULES = {
    " ": 0,
    "#": 1,
}

DIGITS_PER_SIDE = 6
MODULES_PER_DIGIT = 7


class ParityError(Exception):
    """Exception raised when a parity error is found."""


class ChecksumError(Exception):
    """Exception raised when check digit does not match."""


def group(iterable, n):
    """Chunk the iterable into groups of size ``n``."""
    args = [iter(iterable)] * n
    return tuple(itertools.zip_longest(*args))


def parse(barcode):
    """Return the 12 digits represented by the given barcode. Raises a
    ParityError if any digit fails the parity check."""
    match = RE_BARCODE.match(barcode)

    # Translate bars and spaces to 1s and 0s so we can do arithmetic
    # with them. Group "modules" into chunks of 7 as we go.
    left = group((MODULES[c] for c in match.group("left")), MODULES_PER_DIGIT)
    right = group((MODULES[c] for c in match.group("right")), MODULES_PER_DIGIT)

    # Parity check
    left, right = check_parity(left, right)

    # Lookup digits
    return tuple(
        itertools.chain(
            (LEFT_DIGITS[d] for d in left),
            (RIGHT_DIGITS[d] for d in right),
        )
    )


def check_parity(left, right):
    """Check left and right parity. Flip left and right if the barcode
    was scanned upside down."""
    # When reading from left to right, each digit on the left should
    # have odd parity, and each digit on the right should have even
    # parity.
    left_parity = sum(sum(d) % 2 for d in left)
    right_parity = sum(sum(d) % 2 for d in right)

    # Use left and right parity to check if the barcode was scanned
    # upside down. Flip it if it was.
    if left_parity == 0 and right_parity == DIGITS_PER_SIDE:
        _left = tuple(tuple(reversed(d)) for d in reversed(right))
        right = tuple(tuple(reversed(d)) for d in reversed(left))
        left = _left
    elif left_parity != DIGITS_PER_SIDE or right_parity != 0:
        # Error condition. Mixed parity.
        error = tuple(
            itertools.chain(
                (LEFT_DIGITS.get(d, "_") for d in left),
                (RIGHT_DIGITS.get(d, "_") for d in right),
            )
        )
        raise ParityError(" ".join(str(d) for d in error))

    return left, right


def checksum(digits):
    """Return the check digit for the given digits. Raises a
    ChecksumError if the check digit does not match."""
    odds = (digits[i] for i in range(0, 11, 2))
    evens = (digits[i] for i in range(1, 10, 2))

    check_digit = (sum(odds) * 3 + sum(evens)) % 10

    if check_digit != 0:
        check_digit = 10 - check_digit

    if digits[-1] != check_digit:
        raise ChecksumError(str(check_digit))

    return check_digit


def main():
    barcodes = [
        "         # #   # ##  #  ## #   ## ### ## ### ## #### # # # ## ##  #   #  ##  ## ###  # ##  ## ### #  # #       ",
        "        # # #   ##   ## # #### #   # ## #   ## #   ## # # # ###  # ###  ##  ## ###  # #  ### ###  # # #         ",
        "         # #    # # #  ###  #   #    # #  #   #    # # # # ## #   ## #   ## #   ##   # # #### ### ## # #         ",
        "       # # ##  ## ##  ##   #  #   #  # ###  # ##  ## # # #   ## ##  #  ### ## ## #   # #### ## #   # #        ",
        "         # # ### ## #   ## ## ###  ##  # ##   #   # ## # # ### #  ## ##  #    # ### #  ## ##  #      # #          ",
        "          # #  #   # ##  ##  #   #   #  # ##  ##  #   # # # # #### #  ##  # #### #### # #  ##  # #### # #         ",
        "         # #  #  ##  ##  # #   ## ##   # ### ## ##   # # # #  #   #   #  #  ### # #    ###  # #  #   # #        ",
        "        # # #    # ##  ##   #  # ##  ##  ### #   #  # # # ### ## ## ### ## ### ### ## #  ##  ### ## # #         ",
        "         # # ### ##   ## # # #### #   ## # #### # #### # # #   #  # ###  #    # ###  # #    # ###  # # #       ",
        "        # # # #### ##   # #### # #   ## ## ### #### # # # #  ### # ###  ###  # # ###  #    # #  ### # #         ",
        "        # # # #### ##   # #### # #   ## ## ### #### # # # #  ### # ###  ###  # # ###  ## ##  #  ### # #         ",
    ]

    for barcode in barcodes:
        try:
            digits = parse(barcode)
        except ParityError as err:
            print(f"{err} parity error!")
            continue

        try:
            check_digit = checksum(digits)
        except ChecksumError as err:
            print(f"{' '.join(str(d) for d in digits)} checksum error! ({err})")
            continue

        print(f"{' '.join(str(d) for d in digits)}")


if __name__ == "__main__":
    main()
Output:
$ python barcode.py 
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
7 4 8 1 5 9 9 2 3 9 2 _ parity error!
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
6 5 3 4 8 3 5 4 0 4 2 5 checksum error! (8)

Racket

#lang racket

;; inspired by Kotlin

(define (is-#? c) (char=? c #\#))

(define left-digits
  (for/hash ((i (in-naturals))
             (c '((#f #f #t #t #f)
                  (#f #t #t #f #f)
                  (#f #t #f #f #t)
                  (#t #t #t #t #f)
                  (#t #f #f #f #t)
                  (#t #t #f #f #f)
                  (#t #f #t #t #t)
                  (#t #t #t #f #t)
                  (#t #t #f #t #t)
                  (#f #f #t #f #t))))
    (values `(#f ,@c #t) i)))

(define right-digits (for/hash (([k v] left-digits)) (values (map not k) v)))

(define (lookup-blocks bits hsh fail)
  (let recur ((bs bits) (r null))
    (if (null? bs)
        (reverse r)
        (let-values (((bs′ tl) (split-at bs 7)))
          (let ((d (hash-ref hsh bs′ (λ () (fail (list 'not-found bs′))))))
            (recur tl (cons d r)))))))

(define (extract-blocks b fail)
  (let*-values
      (((e-l-m-r-e) (map is-#? (string->list (string-trim b))))
       ((_) (unless (= (length e-l-m-r-e) (+ 3 (* 7 6) 5 (* 7 6) 3))
              (fail 'wrong-length)))
       ((e l-m-r-e) (split-at e-l-m-r-e 3))
       ((_) (unless (equal? e '(#t #f #t)) (fail 'left-sentinel)))
       ((l-m-r e) (split-at-right l-m-r-e 3))
       ((_) (unless (equal? e '(#t #f #t)) (fail 'right-sentinel)))
       ((l m-r) (split-at l-m-r 42))
       ((m r) (split-at m-r 5))
       ((_) (unless (equal? m '(#f #t #f #t #f)) (fail 'mid-sentinel))))
    (values l r)))

(define (upc-checksum? ds)
  (zero? (modulo (for/sum ((m (in-cycle '(3 1))) (d ds)) (* m d)) 10)))

(define (lookup-digits l r fail (transform values))
  (let/ec fail-lookups
    (define ds (append (lookup-blocks l left-digits (λ _ (fail-lookups #f)))
                       (lookup-blocks r right-digits (λ _ (fail-lookups #f)))))
    (if (upc-checksum? ds)
        (transform ds)
        (fail (list 'checksum (transform ds))))))

(define (decode-upc barcode upside-down fail)
  (define-values (l r) (extract-blocks barcode fail))
  (or (lookup-digits l r fail)
      (lookup-digits (reverse r) (reverse l) fail upside-down)))

(define (report-upc barcode)
  (displayln (decode-upc barcode
                         (λ (v) (cons 'upside-down v))
                         (λ (e) (format "invalid: ~s" e)))))

(define (UPC)
  (for-each report-upc
            '("         # #   # ##  #  ## #   ## ### ## ### ## #### # # # ## ##  #   #  ##  ## ###  # ##  ## ### #  # #       "
              "        # # #   ##   ## # #### #   # ## #   ## #   ## # # # ###  # ###  ##  ## ###  # #  ### ###  # # #         "
              "         # #    # # #  ###  #   #    # #  #   #    # # # # ## #   ## #   ## #   ##   # # #### ### ## # #         "
              "       # # ##  ## ##  ##   #  #   #  # ###  # ##  ## # # #   ## ##  #  ### ## ## #   # #### ## #   # #        "
              "         # # ### ## #   ## ## ###  ##  # ##   #   # ## # # ### #  ## ##  #    # ### #  ## ##  #      # #          "
              "          # #  #   # ##  ##  #   #   #  # ##  ##  #   # # # # #### #  ##  # #### #### # #  ##  # #### # #         "
              "         # #  #  ##  ##  # #   ## ##   # ### ## ##   # # # #  #   #   #  #  ### # #    ###  # #  #   # #        "
              "        # # #    # ##  ##   #  # ##  ##  ### #   #  # # # ### ## ## ### ## ### ### ## #  ##  ### ## # #         "
              "         # # ### ##   ## # # #### #   ## # #### # #### # # #   #  # ###  #    # ###  # #    # ###  # # #       "
              "        # # # #### ##   # #### # #   ## ## ### #### # # # #  ### # ###  ###  # # ###  #    # #  ### # #         "
              ; first element again, with corrupted second digit
              "         # #   # ##   # ## #   ## ### ## ### ## #### # # # ## ##  #   #  ##  ## ###  # ##  ## ### #  # #       ")))

(module+ main (UPC))
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)
(upside-down 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)
#f
(upside-down 3 1 6 3 1 3 7 1 8 7 1 7)
(2 1 4 5 7 5 8 7 5 6 0 8)
(upside-down 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)
invalid: (checksum (9 9 4 7 7 3 2 7 1 0 1 9))

Raku

(formerly Perl 6)

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';
}
Output:
924773271019
403944441050
834999676706
939825158811
Invalid
316313718717
214575875608
818778841813
706466743030
653483540435

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= '          # # # #### ##   # #### # #   ## ## ### #### # # # #  ### # ###  ###  # # ###  #    # #  ### #          '
          ends= '# #'                                        /*define ENDS literal const*/
   do j=1  while @.j\==.;        $= @.j
   txt=                                                      /*initialize  TXT  variable*/
   if left($, 9)\=''     | right($, 9)\=''      then txt= 'bad blanks'
   $= strip($);   $$= $                                      /*elide blanks at ends of $*/
   L= length($)                                              /*obtain length of $ string*/
   if left($, 3) \==ends | right($, 3) \==ends  then txt= 'bad fence'
   if L \== 95           & txt==''              then txt= 'bad length'
   $= substr($, 4, L - length(ends)*2)                                  /*elide "ends". */
   $= delstr($, length($) % 2 - 1,  5)                                  /*  "   middle. */
   sum= 0                                                               /*initialize SUM*/
   if txt==''  then do k=1  for 12;     parse var  $   x  +7  $         /*get UPC digit.*/
                       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 digit';   leave;   end
                    if k // 2  then sum= sum +    d * 3                 /*mult. by  3.  */
                               else sum= sum +    d                     /*  "    "  1.  */
                    txt= txt  ||  d
                    end     /*k*/

   if left(txt,1)\=="b"  then if sum//10\==0  then txt= 'bad checksum'  /*invalid sum?  */
   say center( strip(txt), 15)  ' '   $$         /*show chksum (or err msg) with the UPC*/
   end   /*j*/                                   /*stick a fork in it,  we're all done. */
output   when using the internal default input:
─────UPC──────    ---123456712345671234567123456712345671234567-----123456712345671234567123456712345671234567---
 924773271019     # #   # ##  #  ## #   ## ### ## ### ## #### # # # ## ##  #   #  ##  ## ###  # ##  ## ### #  # #
 403944441050     # # #   ##   ## # #### #   # ## #   ## #   ## # # # ###  # ###  ##  ## ###  # #  ### ###  # # #
   reversed       # #    # # #  ###  #   #    # #  #   #    # # # # ## #   ## #   ## #   ##   # # #### ### ## # #
   reversed       # # ##  ## ##  ##   #  #   #  # ###  # ##  ## # # #   ## ##  #  ### ## ## #   # #### ## #   # #
   bad digit      # # ### ## #   ## ## ###  ##  # ##   #   # ## # # ### #  ## ##  #    # ### #  ## ##  #      # #
   reversed       # #  #   # ##  ##  #   #   #  # ##  ##  #   # # # # #### #  ##  # #### #### # #  ##  # #### # #
 214575875608     # #  #  ##  ##  # #   ## ##   # ### ## ##   # # # #  #   #   #  #  ### # #    ###  # #  #   # #
   reversed       # # #    # ##  ##   #  # ##  ##  ### #   #  # # # ### ## ## ### ## ### ### ## #  ##  ### ## # #
 706466743030     # # ### ##   ## # # #### #   ## # #### # #### # # #   #  # ###  #    # ###  # #    # ###  # # #
  bad length      # # # #### ##   # #### # #   ## ## ### #### # # # #  ### # ###  ###  # # ###  #    # #  ### #

Ruby

Translation of: C
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()
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

VBScript

'read UPC barcode Antoni Gual 10/2022  https://rosettacode.org/wiki/UPC

Option Explicit
Const m_limit ="# #"
Const m_middle=" # # "
Dim a,bnum,i,check,odic
a=array("         # #   # ##  #  ## #   ## ### ## ### ## #### # # # ## ##  #   #  ##  ## ###  # ##  ## ### #  # #       ",_
         "        # # #   ##   ## # #### #   # ## #   ## #   ## # # # ###  # ###  ##  ## ###  # #  ### ###  # # #         ",_
        "         # #    # # #  ###  #   #    # #  #   #    # # # # ## #   ## #   ## #   ##   # # #### ### ## # #         ",_
          "       # # ##  ## ##  ##   #  #   #  # ###  # ##  ## # # #   ## ##  #  ### ## ## #   # #### ## #   # #        ",_
        "         # # ### ## #   ## ## ###  ##  # ##   #   # ## # # ### #  ## ##  #    # ### #  ## ##  #      # #          ",_
       "          # #  #   # ##  ##  #   #   #  # ##  ##  #   # # # # #### #  ##  # #### #### # #  ##  # #### # #         ",_
        "         # #  #  ##  ##  # #   ## ##   # ### ## ##   # # # #  #   #   #  #  ### # #    ###  # #  #   # #        ",_
         "        # # #    # ##  ##   #  # ##  ##  ### #   #  # # # ### ## ## ### ## ### ### ## #  ##  ### ## # #         ",_
        "         # # ### ##   ## # # #### #   ## # #### # #### # # #   #  # ###  #    # ###  # #    # ###  # # #       ",_
         "        # # # #### ##   # #### # #   ## ## ### #### # # # #  ### # ###  ###  # # ###  #    # #  ### # #         ")

'                 0         1         2         3         4          5         6         7         8         9
bnum=Array("0001101","0011001","0010011","0111101","0100011"," 0110001","0101111","0111011","0110111","0001011")

Set oDic = WScript.CreateObject("scripting.dictionary")
For i=0 To 9: 
  odic.Add bin2dec(bnum(i),Asc("1")),i+1 
  odic.Add bin2dec(bnum(i),Asc("0")),-i-1  
Next

For i=0 To UBound(a) : print pad(i+1,-2) & ": "& upc(a(i)) :Next
  WScript.Quit(1)
  
  Function bin2dec(ByVal B,a) 'binary,ascii of bit 1
    Dim n
    While len(b)
      n =n *2 - (asc(b)=a)  'true is -1 in vbs
      b=mid(b,2) 
    Wend
    bin2dec= n And 127
  End Function
  
  Sub print(s): 
    On Error Resume Next
    WScript.stdout.WriteLine (s)  
    If  err= &h80070006& Then WScript.Echo " Please run this script with CScript": WScript.quit
  End Sub
  function pad(s,n) if n<0 then pad= right(space(-n) & s ,-n) else  pad= left(s& space(n),n) end if :end function
   
  Function iif(t,a,b)  If t Then iif=a Else iif=b End If :End Function
  
  Function getnum(s,r) 'get a number from code, check if its's reversed and trim the code
    Dim n,s1,r1 
    'returns number or 0 if not found
    s1=Left(s,7)
    s=Mid(s,8)
    r1=r
    Do
      If r Then s1=StrReverse(s1) 
      n=bin2dec(s1,asc("#"))
      If odic.exists(n) Then
        getnum=odic(n)
        Exit Function
      Else
        If r1<>r Then getnum=0:Exit Function
        r=Not r
      End If
    Loop 
  End Function          
  
  Function getmarker(s,m) 'get a marker and trim the code
    getmarker= (InStr(s,m)= 1)
    s=Mid(s,Len(m)+1)
  End Function
  
  Function checksum(ByVal s)
    Dim n,i : n=0
    do
       n=n+(Asc(s)-48)*3
       s=Mid(s,2)
       n=n+(Asc(s)-48)*1
       s=Mid(s,2)
    Loop until Len(s)=0
    checksum= ((n mod 10)=0)
  End function      
      
  Function upc(ByVal s1)
    Dim i,n,s,out,rev,j 
    
    'forget about the leading adn trailing spaces, the task says they may be wrong
    s=Trim(s1)
    If getmarker(s,m_limit)=False  Then upc= "bad start marker ":Exit function
    rev=False
    out=""
    For j= 0 To 1
      For i=0 To 5
        n=getnum(s,rev)
        If n=0 Then upc= pad(out,16) & pad ("bad code",-10) & pad("pos "& i+j*6+1,-11): Exit Function
        out=out & Abs(n)-1
      Next
      If j=0 Then If getmarker(s,m_middle)=False  Then upc= "bad middle marker " & out :Exit Function
    Next  
    If getmarker(s,m_limit)=False  Then upc= "bad end marker "  :Exit function
    If rev Then out=strreverse(out)
    upc= pad(out,16) &  pad(iif (checksum(out),"valid","not valid"),-10)&  pad(iif(rev,"reversed",""),-11)
  End Function
Output:


 1: 924773271019         valid           
 2: 403944441050         valid           
 3: 834999676706         valid   reversed
 4: 939825158811         valid   reversed
 5: 74815992392       bad code     pos 12
 6: 316313718717         valid   reversed
 7: 214575875608         valid           
 8: 818778841813         valid   reversed
 9: 706466743030         valid           
10: 653483540435         valid           


Wren

Translation of: Ruby
Library: Wren-fmt
import "./fmt" for Fmt

var digitL = {
    "   ## #": 0,
    "  ##  #": 1,
    "  #  ##": 2,
    " #### #": 3,
    " #   ##": 4,
    " ##   #": 5,
    " # ####": 6,
    " ### ##": 7,
    " ## ###": 8,
    "   # ##": 9
}

var digitR = {
    "###  # ": 0,
    "##  ## ": 1,
    "## ##  ": 2,
    "#    # ": 3,
    "# ###  ": 4,
    "#  ### ": 5,
    "# #    ": 6,
    "#   #  ": 7,
    "#  #   ": 8,
    "### #  ": 9
}

var endSentinel = "# #"  //  also at start
var midSentinel = " # # "

var decodeUpc = Fn.new { |s|
    var decodeUpcImpl = Fn.new { |input|
        var upc = input.trim()
        if (upc.count != 95) return false
        var pos = 0
        var digits = []
        var sum = 0
        var oneThree = [1, 3]

        // end sentinel
        if (upc[pos..pos+2] == endSentinel) {
            pos = pos + 3
        } else {
            return false
        }

        // 6 left hand digits
        for (i in 0..5) {
            var digit = digitL[upc[pos..pos+6]]
            if (!digit) return false
            digits.add(digit)
            sum = sum + digit * oneThree[digits.count % 2]
            pos = pos + 7
        }

        // mid sentinel
        if (upc[pos..pos+4] == midSentinel) {
            pos = pos + 5
        } else {
            return false
        }

        // 6 right hand digits
        for (i in 0..5) {
            var digit = digitR[upc[pos..pos+6]]
            if (!digit) return false
            digits.add(digit)
            sum = sum + digit * oneThree[digits.count % 2]
            pos = pos + 7
        }

        // end sentinel
        if (upc[pos..pos+2] != endSentinel) return false

        if (sum%10 == 0) {
            System.write("%(digits) ")
            return true
        }
        System.write("Failed Checksum ")
        return false
    }

    if (decodeUpcImpl.call(s)) {
        System.print("Rightside Up")
    } else if (decodeUpcImpl.call(s[-1..0])) {
        System.print("Upside Down")
    } else {
        System.print("Invalid digit(s)")
    }
}

var barcodes = [
    "         # #   # ##  #  ## #   ## ### ## ### ## #### # # # ## ##  #   #  ##  ## ###  # ##  ## ### #  # #       ",
    "        # # #   ##   ## # #### #   # ## #   ## #   ## # # # ###  # ###  ##  ## ###  # #  ### ###  # # #         ",
    "         # #    # # #  ###  #   #    # #  #   #    # # # # ## #   ## #   ## #   ##   # # #### ### ## # #         ",
    "       # # ##  ## ##  ##   #  #   #  # ###  # ##  ## # # #   ## ##  #  ### ## ## #   # #### ## #   # #        ",
    "         # # ### ## #   ## ## ###  ##  # ##   #   # ## # # ### #  ## ##  #    # ### #  ## ##  #      # #          ",
    "          # #  #   # ##  ##  #   #   #  # ##  ##  #   # # # # #### #  ##  # #### #### # #  ##  # #### # #         ",
    "         # #  #  ##  ##  # #   ## ##   # ### ## ##   # # # #  #   #   #  #  ### # #    ###  # #  #   # #        ",
    "        # # #    # ##  ##   #  # ##  ##  ### #   #  # # # ### ## ## ### ## ### ### ## #  ##  ### ## # #         ",
    "         # # ### ##   ## # # #### #   ## # #### # #### # # #   #  # ###  #    # ###  # #    # ###  # # #       ",
    "        # # # #### ##   # #### # #   ## ## ### #### # # # #  ### # ###  ###  # # ###  #    # #  ### # #         "
]
var n = 0
for (barcode in barcodes) {
    n = n + 1
    Fmt.write("$2d: ", n)
    decodeUpc.call(barcode)
}
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

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
}

Or, if you like way too long regular expressions:

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)
}
barcodes:=
#<<<"
_________#_#___#_##__#__##_#___##_###_##_###_##_####_#_#_#_##_##__#___#__##__##_###__#_##__##_###_#__#_#_________
_________#_#_#___##___##_#_####_#___#_##_#___##_#___##_#_#_#_###__#_###__##__##_###__#_#__###_###__#_#_#_________
_________#_#____#_#_#__###__#___#____#_#__#___#____#_#_#_#_##_#___##_#___##_#___##___#_#_####_###_##_#_#_________
_________#_#_##__##_##__##___#__#___#__#_###__#_##__##_#_#_#___##_##__#__###_##_##_#___#_####_##_#___#_#_________
_________#_#_###_##_#___##_##_###__##__#_##___#___#_##_#_#_###_#__##_##__#____#_###_#__##_##__#______#_#__________
__________#_#__#___#_##__##__#___#___#__#_##__##__#___#_#_#_#_####_#__##__#_####_####_#_#__##__#_####_#_#____________
_________#_#__#__##__##__#_#___##_##___#_###_##_##___#_#_#_#__#___#___#__#__###_#_#____###__#_#__#___#_#_________
_________#_#_#____#_##__##___#__#_##__##__###_#___#__#_#_#_###_##_##_###_##_###_###_##_#__##__###_##_#_#__________
_________#_#_###_##___##_#_#_####_#___##_#_####_#_####_#_#_#___#__#_###__#____#_###__#_#____#_###__#_#_#_________
_______________#_#_#_####_##___#_####_#_#___##_##_###_####_#_#_#_#__###_#_###__###__#_#_###__#____#_#__###_#_#_________"
.split("\n");
#<<<

foreach n,barcode in ([1..].zip(barcodes)){
   bc:=parseBarCode(barcode);
   println("%2d: [%s]".fmt(n,bc and bc.concat(" ") or "Not valid"));
}
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]