Поделиться через


Checking a string for a valid ISBN number

Continuing the series of "things you may have done in college" this next one is all about array manipulation. Again it's one I've heard of being used in a whiteboard/tech interview situation (so worth brushing up on!). The scenario here is just the creation of a method which:

  1. Accepts a string
  2. Checks to see if that string is actually a valid 10 digit ISBN number format (valid format - an actually valid ISBN no. is one which has been issued to a published book)

The only other stipulation is to do it in the first instance without using the built-in .NET string methods. So it would be quicker to use String.Split('-') etc... but the idea here is to test the basic knowledge, so the first effort here uses just standard array walking loops. I may add a more modern implementation later, but as previously stated I would love if these post generated some comments, and perhaps other implementation styles in those comments.

I took the "business logic", i.e. the 10 digit ISBN rules from here: https://www.isbn.org/standards/home/isbn/international/html/usm4.htm

This is the first hastily written version:

 

  private static bool IsTenDigitISBN(string isbnString)
 {
 if (string.IsNullOrWhiteSpace(isbnString))
 {
 return false;
 }
 
 char[] isbnArray = isbnString.ToArray();
 
 if (isbnArray[0] == '-' || isbnArray[isbnArray.Length - 1] == '-')
 {
 return false;
 }
 
 if (isbnArray.Length != 10 && isbnArray.Length != 13)
 {
 return false;
 }
 
 int dashCount = 0;
 int sum = 0;
 int digit=0;
 
 //now replace the '-' chars in pos 0-9
 for (int i = 0; i < isbnArray.Length-1; i++)
 {
 if (isbnArray[i] == '-')
 {
 dashCount++;
 isbnArray[i]=isbnArray[i--+1];
 for (int x = i + 1; x < isbnArray.Length-1; x++)
 {
 isbnArray[x] = isbnArray[x + 1];
 }
 }
 }
 if (dashCount != 0 && dashCount != 3)
 {
 return false;
 }
 
 //if array is 13 long, we must have 3 dashes, 10 long must have no dashes
 if ((isbnArray.Length == 13 && dashCount !=3) && (isbnArray.Length == 10 && dashCount !=0))
 {
 return false;
 }
 
 for (int i=0; i < 9; i++)
 {
 if (ToDigit(isbnArray[i], ref digit))
 {
 int multiplier = 10 - i;
 int val=digit*multiplier;
 sum += val;
 }
 else
 {
 return false;
 }
 }
 
 //now add the check digit (or 10 if it is an 'X' character)
 if (isbnArray[9] == 'X' || isbnArray[9] == 'x')
 {
 sum += 10;
 }
 else
 {
 if (ToDigit(isbnArray[9], ref digit))
 {
 sum += digit;
 }
 else
 {
 return false;
 }
 }
 
 //Sum of digits + check digit should have a remainder of 0 when divided by 11
 int check = sum % 11;
 
 if (check == 0)
 {
 return true;
 }
 else
 {
 return false;
 }
 }

 

 

As you can see there is one other small method called within the check method - the ToDigit() method which converts a char to an int (if the char is one of '0' - '9'). It is shown here:

 

  /// <summary>
 /// An "atoi()" style function for single characters
 /// </summary>
 /// <param name="ch">character which should represent a digit</param>
 /// <param name="digit">pass by ref int used to return the value</param>
 /// <returns></returns>
 private static bool ToDigit(char ch, ref int digit)
 {
 char[] digits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
 bool found=false;
 
 for (int i = 0; i < digits.Length; i++)
 {
 if (ch==digits[i])
 {
 digit=i;
 found=true;
 }
 }
 
 if (found)
 {
 return true;
 }
 
 return false;
 }

 

 

So that's it - comments welcome. Questions which could be asked around this include the standard "which unit tests would you write". Here's some examples of strings to pass:

Valid - return true:

IsTenDigitISBN("90-70002-34-5"));
IsTenDigitISBN("0-8436-1072-7"));
IsTenDigitISBN("1590593006"));

 

Invalid - return false:

IsTenDigitISBN("-"));
IsTenDigitISBN("--1234556789--"));
IsTenDigitISBN("123-45-567-8-9"));

 

Other questions to ask then would be perhaps:

  • How to improve it/rewrite it using .NET string methods.
  • How to check for a 13 digit ISBN also - in the same method?