Enable Javascript in your browser and then refresh this page, for a much enhanced experience.
Functional vs. class-driven, very pedantic solution in Clear category for Barcode Reader by kkkkk
def sanity_check(bin_str):
"""Return True if start, end and center codes are present and correct."""
if ''.join(bin_str[0:3]) != '101':
return False
if ''.join(bin_str[92:]) != '101':
return False
if ''.join(bin_str[45:50]) != '01010':
return False
return True
def isolate_groups(bin_str):
"""Return two lists of six 7-digit numbers from left and right side."""
left_group = [''.join(bin_str[0+i:7+i]) for i in range(3, 45, 7)]
right_group = [''.join(bin_str[0+i:7+i]) for i in range(50, 92, 7)]
return left_group, right_group
def calculate_parity_string(bin_group):
"""Return 6-letter string representing parity of each subgroup.
'L' indicates odd parity, 'G' indicates even parity.
"""
return ''.join(['G' if sum(int(n) for n in bin_subgroup) % 2 == 0 else 'L'
for bin_subgroup in bin_group])
def calculate_first_digit(parity_str):
"""Return the first digit of barcode based on parity string."""
first_digit_map = ['LLLLLL', 'LLGLGG', 'LLGGLG', 'LLGGGL', 'LGLLGG',
'LGGLLG', 'LGGGLL', 'LGLGLG', 'LGLGGL', 'LGGLGL']
if parity_str not in first_digit_map:
return None
return str(first_digit_map.index(parity_str))
def calculate_lg_digits(bin_group, parity_str):
"""Return 6-digits of mapped L, G code for binary subgroups."""
l_code_map = ['0001101', '0011001', '0010011', '0111101', '0100011',
'0110001', '0101111', '0111011', '0110111', '0001011']
g_code_map = ['0100111', '0110011', '0011011', '0100001', '0011101',
'0111001', '0000101', '0010001', '0001001', '0010111']
digits = []
for idx, bin_subgroup in enumerate(bin_group):
if parity_str[idx] == 'L':
if bin_subgroup not in l_code_map:
return None
digits.append(str(l_code_map.index(bin_subgroup)))
else:
if bin_subgroup not in g_code_map:
return None
digits.append(str(g_code_map.index(bin_subgroup)))
return ''.join(digits)
def calculate_r_digits(bin_group):
"""Return 6-digits of mapped R code for binary subgroups."""
r_code_map = ['1110010', '1100110', '1101100', '1000010', '1011100',
'1001110', '1010000', '1000100', '1001000', '1110100']
r_digits = []
for bin_subgroup in bin_group:
if bin_subgroup not in r_code_map:
return None
r_digits.append(str(r_code_map.index(bin_subgroup)))
return ''.join(r_digits)
def is_checksum_good(barcode):
"""Return True if checksum is valid.
The checksum is calculated by multipling a weight against the first 12
digits of the barcode, using a weight of 1 for digits in an even position
and a weight of 3 for digits in the odd position. The results of those
multiplications are added up and the result is used to calculate the
difference to the nearest multiple of 10. That difference must be equal
to the 13th digit.
"""
checksum = 0
for idx, digit in enumerate(barcode[:-1]):
weight = 1 if idx % 2 == 0 else 3
checksum += int(digit) * weight
modulo_checksum = checksum % 10
if modulo_checksum != 0:
modulo_checksum = 10 - modulo_checksum
return True if modulo_checksum == int(barcode[-1]) else False
def barcode_reader(barcode):
"""Return the EAN-13 barcode from the barcode graphical representation."""
# Convert underscores and spaces to a string of 1s and 0s.
bin_str = ['1' if symbol == '_' else '0' for symbol in barcode]
# Verify the start, center and end codes are present in the string.
if not sanity_check(bin_str):
return None
# Isolate the left and right sets of 7-digit binary digits in the
# barcode string and calculate the parity for each set.
group1, group2 = isolate_groups(bin_str)
group1_parity = calculate_parity_string(group1)
group2_parity = calculate_parity_string(group2)
# One of these groups must have even parity for all 7-digit numbers,
# i.e., we need a R code.
if group1_parity != 'GGGGGG'and group2_parity != 'GGGGGG':
return None
# One set should have all even parity ('G' for even parity, 'L' for
# odd parity). If they're switched, the barcode was scanned backwards,
# so swap the parities and the numbers in the groups.
if group1_parity == 'GGGGGG':
group1_parity, group2_parity = group2_parity[::-1], group1_parity
group1, group2 = ([g[::-1] for g in group2[::-1]],
[g[::-1] for g in group1[::-1]])
# Use the first set to determine the first digit of the numerical
# barcode.
first_digit = calculate_first_digit(group1_parity)
if not first_digit:
return None
# Now map both sets of binary digits to numbers. If either set of
# binary digits failed to map into a number, the barcode string is bad.
lg_digits = calculate_lg_digits(group1, group1_parity)
r_digits = calculate_r_digits(group2)
if not lg_digits or not r_digits:
return None
# Now verify the checksum. The checksum uses the first 12 digits to
# calculate the 13th, which must match.
ein13_barcode = first_digit + lg_digits + r_digits
if not is_checksum_good(ein13_barcode):
return None
# The barcode string has been validated and the barcode digits computed.
return ein13_barcode
Sept. 15, 2020
Comments: