#!/usr/bin/perl use strict; use warnings; use feature 'say'; sub validate_serial_number { my $v = shift; $v =~ s/\s//g; # remove whitespace $v = uc $v; # convert to uppercase # 1) Try regex: return 0 if !length $v; return 1 if $v =~ /^JM-\d+-\S{4}[1-9Zz]{6}$/; # 2) Initialize the %map hash with (A=>0, B=>1, C=>2, ...): my $dv = 0; my %map = map { $_ => $dv++ } qw(A B C D E F G H J K L M N P Q R T U V W Y 3 4 6 7 8 9); # 3) Extract significant character data, for example: # 'FOOBAR123ABCDE' -> chars: qw(D C B A 3 R A B F) # check: 'E' my @chars = reverse split '', $v; my $check = shift @chars; @chars = grep { exists $map{$_} } @chars; # 4) Sanity check: return 0 if @chars != 9; # 5) Perform check digit algorithm: my $totalVal = 0; # Loop over the reversed array of input characters: for my $i (0..$#chars) { # Map the character to a unique numeric value: my $posVal = $map{$chars[$i]}; # Double the value, but only for every second character: $posVal *= 2 if !($i % 2); # Add the sum of the digits of $posVal to $totalVal: while($posVal){ $totalVal += $posVal % 10; $posVal = int($posVal / 10); } } # Substract the last digit of $totalVal from 10, and take the # last digit of *that* to get the check digit: return ( $check eq ( 10 - $totalVal % 10 ) % 10 ); } for ('Foo abr 123 ABCD6', 'Foo bar 123 ABCD6') { say "'$_' is " . (validate_serial_number($_) ? "valid" : "invalid"); } #### 'Foo abr 123 ABCD6' is invalid 'Foo bar 123 ABCD6' is valid