Miryloth
Miryloth

Reputation: 55

How to validate ISBN-13 input with Python

I need to write a function that validates a 13 digit ISBN. It needs to start with 978 or 979, end with a single digit, and the remaining sections need to be at least 1 digit in length. I need some help to make this work, I don't understand why it never returns true

def validate(s)
 lst = s.split("-")
 isbn= False
 if lst[0] == "978" or lst[0] == "979":
     if len(lst[1])>=1 and len(lst[2])>=1:
         if len(lst[3])==1:
            isbn= True
return isbn

Upvotes: 0

Views: 4251

Answers (4)

clp2
clp2

Reputation: 176

Error 1: Missing a colon at end of the 'def' statement.

Error 2: The 'return isbn' statement is not indented; it is outside the function but should be inside.

Error 3: The line checking the length of lst[3] does not check the last element of the isbn string when there are more than four elements in lst.

The split command creates five elements in lst for your first string, 978-3-16-148410-0; but lst[3] has 6 digits, and the length test fails.

The split command creates four elements in lst for your second string, 978-3-16-148410; but lst[3] has 6 digits, and the length test fails.

Consider using lst[-1] to specify the last element of lst, regardless of how many elements it contains. If your i/p is formatted correctly, the o/p should be correct.

Upvotes: 0

Mark Tolonen
Mark Tolonen

Reputation: 177481

An ISBN-13 consists of five groups of numbers and the last digit is a check digit. Here's a function to make sure there are five groups, exactly 13 digits, and validates the check digit. It works with your samples:

import re

def validate(s):
    d = re.findall(r'\d',s)
    if len(d) != 13:
        return False
    if not re.match(r'97[89](?:-\d+){3}-\d$',s):
        return False

    # The ISBN-13 check digit, which is the last digit of the ISBN, must range from 0 to 9
    # and must be such that the sum of all the thirteen digits, each multiplied by its
    # (integer) weight, alternating between 1 and 3, is a multiple of 10.
    odd = [int(x) for x in d[::2]]
    even = [int(x)*3 for x in d[1::2]]
    return (sum(odd)+sum(even)) % 10 == 0

trials = '''\
978-3-16-148410-0
978-3-16-148410
978-0-306-40615-7
978-0306-40615-7
979-11111-11-11-2
978-7654-321-12-4
977-7654-321-12-4
978-7654-321-1-41
978-7654-321-1-4
978-7654-321-122-4
'''.splitlines()

for trial in trials:
    print(validate(trial),trial)

Output:

True 978-3-16-148410-0
False 978-3-16-148410        # too few numbers and groups       
True 978-0-306-40615-7
False 978-0306-40615-7       # too few groups
True 979-11111-11-11-2
False 978-7654-321-12-4      # wrong check digit
False 977-7654-321-12-4      # didn't start with 978 or 979
False 978-7654-321-1-41      # didn't end in one digit.
False 978-7654-321-1-4       # too few digits
False 978-7654-321-122-4     # too many digits

Upvotes: 0

mhawke
mhawke

Reputation: 87064

ISBN-13 requires 13 digits to be valid. Your code does not check that all characters are digits (excluding the - separator), nor does it check the actual length. Also, five parts are required, and you could be verifying the check digit.

Specifically your code fails to ever return True because the fourth segment (lst[3]) checks for exactly one character (if len(lst[3])==1:), however, that element will typically be longer than 1 digit.

There are python libraries available via PyPI that can validate ISBN codes. Here's an example using isbnlib:

>>> import isbnlib
>>> isbnlib.is_isbn13('978-3-16-148410-0')
True
>>> isbnlib.is_isbn13('978-3-16-148410-5')
False
>>> isbnlib.is_isbn13('978-3-16-148410-A')
False
>>> isbnlib.is_isbn13('979-3-16-148410-9')
True

Another, lighter weight library is pyisbn:

>>> import pysisbn
>>> pyisbn.validate('979-3-16-148410-9')
True
>>> pyisbn.validate('979-3-16-148410-0')
False

The advantage of using these libraries, other than saving you the hassle of parsing ISBN strings yourself, is that they offer additional functionality such as converting from ISBN-13 to ISBN-10.

Upvotes: 2

Devi Prasad Khatua
Devi Prasad Khatua

Reputation: 1235

You should use regular expression and this is exactly why it is used for:

>>> import re
>>> def validate(isbn):
        isbn_regex = re.compile('^(978|979)-\d+-\d+-\d$')
        return isbn_regex.search(isbn) is not None

>>> print validate('978-12-12-2')
    True

Note: This works as per your logic in the above code(except for you didn't check whether it's a digit).

Upvotes: 1

Related Questions