регулярное выражение, различающее ISBN-10 и ISBN-13


У меня есть оператор If-else, который проверяет строку, чтобы узнать, есть ли ISBN-10 или ISBN-13 (идентификатор книги).

Проблема, с которой я сталкиваюсь, связана с проверкой ISBN-10, которая происходит до проверки ISBN-13, проверка ISBN-10 будет соответствовать чему-либо с 10 символами или более, и поэтому может ошибочно принять ISBN-13 за ISBN-10.

Вот код...

$str = "ISBN:9780113411436";

if(preg_match("/\d{9}(?:\d|X)/", $str, $matches)){
   echo "ISBN-10 FOUND\n";  
   //isbn returned will be 9780113411
   return 0;
}

else if(preg_match("/\d{12}(?:\d|X)/", $str, $matches)){
   echo "ISBN-13 FOUND\n";
   //isbn returned will be 9780113411436
   return 1;
}

Как мне убедиться, что я избегаю этой проблемы?

Author: cryptic ツ, 2012-12-31

5 answers

Для этого вам действительно нужно только одно регулярное выражение. Затем выполните более эффективную проверку strlen(), чтобы увидеть, какой из них был сопоставлен. Следующее будет соответствовать значениям ISBN-10 и ISBN-13 в строке с дефисами или без них, и необязательно перед строкой ISBN:, ISBN:(space) или ISBN(space).

Поиск ISBN:

function findIsbn($str)
{
    $regex = '/\b(?:ISBN(?:: ?| ))?((?:97[89])?\d{9}[\dx])\b/i';

    if (preg_match($regex, str_replace('-', '', $str), $matches)) {
        return (10 === strlen($matches[1]))
            ? 1   // ISBN-10
            : 2;  // ISBN-13
    }
    return false; // No valid ISBN found
}

var_dump(findIsbn('ISBN:0-306-40615-2'));     // return 1
var_dump(findIsbn('0-306-40615-2'));          // return 1
var_dump(findIsbn('ISBN:0306406152'));        // return 1
var_dump(findIsbn('0306406152'));             // return 1
var_dump(findIsbn('ISBN:979-1-090-63607-1')); // return 2
var_dump(findIsbn('979-1-090-63607-1'));      // return 2
var_dump(findIsbn('ISBN:9791090636071'));     // return 2
var_dump(findIsbn('9791090636071'));          // return 2
var_dump(findIsbn('ISBN:97811'));             // return false

Это приведет к поиску предоставленной строки, чтобы увидеть, содержит ли она возможное значение ISBN-10 (возвращает 1) или значение ISBN-13 (возвращает 2). Если этого не произойдет, он вернется false.

См. ДЕМО-ВЕРСИЯ из вышеперечисленного.


Проверка ISBN:

Для строгой проверки в статье Википедии для ISBN есть некоторые функции проверки PHP для ISBN-10 и ISBN-13. Ниже приведены эти примеры, скопированные, исправленные и измененные для использования в слегка измененной версии вышеупомянутой функции.

Измените блок возврата на этот:

    return (10 === strlen($matches[1]))
        ? isValidIsbn10($matches[1])  // ISBN-10
        : isValidIsbn13($matches[1]); // ISBN-13

Подтвердить ISBN-10:

function isValidIsbn10($isbn)
{
    $check = 0;

    for ($i = 0; $i < 10; $i++) {
        if ('x' === strtolower($isbn[$i])) {
            $check += 10 * (10 - $i);
        } elseif (is_numeric($isbn[$i])) {
            $check += (int)$isbn[$i] * (10 - $i);
        } else {
            return false;
        }
    }

    return (0 === ($check % 11)) ? 1 : false;
}

Проверить ISBN-13:

function isValidIsbn13($isbn)
{
    $check = 0;

    for ($i = 0; $i < 13; $i += 2) {
        $check += (int)$isbn[$i];
    }

    for ($i = 1; $i < 12; $i += 2) {
        $check += 3 * $isbn[$i];
    }

    return (0 === ($check % 10)) ? 2 : false;
}

См. ДЕМО-ВЕРСИЯ из вышеперечисленного.

 39
Author: cryptic ツ, 2014-02-21 19:00:26

Используйте ^ и $ для сопоставления начала и конца строки. При использовании разделителей строк порядок, в котором вы проверяете 10 или 13-значные коды, не будет иметь значения.

10 цифр

/^ISBN:(\d{9}(?:\d|X))$/

13 цифр

/^ISBN:(\d{12}(?:\d|X))$/

Примечание: В соответствии с http://en.wikipedia.org/wiki/International_Standard_Book_Number , похоже, что в ISBNs также может быть -. Но, судя по $str, который вы используете, похоже, что вы удалили дефисы перед проверкой на 10 или 13 цифр.

Дополнительное примечание: Поскольку последняя цифра ISBN используется как своего рода контрольная сумма для предыдущих цифр, только регулярные выражения не могут подтвердить, что ISBN является действительным. Он может проверять только 10 или 13-значные форматы.


$isbns = array(
  'ISBN:1234567890',       // 10-digit
  'ISBN:123456789X',       // 10-digit ending in X
  'ISBN:1234567890123',    // 13-digit
  'ISBN:123456789012X',    // 13-digit ending in X
  'ISBN:1234'              // invalid
);

function get_isbn($str) {
   if (preg_match('/^ISBN:(\d{9}(?:\d|X))$/', $str, $matches)) {
      echo "found 10-digit ISBN\n";
      return $matches[1];
   }
   elseif (preg_match('/^ISBN:(\d{12}(?:\d|X))$/', $str, $matches)) {
      echo "found 13-digit ISBN\n";
      return $matches[1];
   }
   else {
      echo "invalid ISBN\n";
      return null;
   }
}

foreach ($isbns as $str) {
   $isbn = get_isbn($str);
   echo $isbn."\n\n";
}

Вывод

found 10-digit ISBN
1234567890

found 10-digit ISBN
123456789X

found 13-digit ISBN
1234567890123

found 13-digit ISBN
123456789012X

invalid ISBN
 3
Author: maček, 2012-12-30 23:56:20

Поставить проверку ISBN-13 перед проверкой ISBN-10? Это предполагает, что вы хотите сопоставить их как часть любой строки, то есть (в вашем примере есть дополнительный "ISBN:" в начале, поэтому сопоставление в любом месте строки кажется каким-то требованием)

 1
Author: Matti Virkkunen, 2012-12-30 23:32:59

Измените порядок блока if else, также удалите все пробелы, двоеточия и дефисы из вашего ISBN:

//Replace all the fluff that some companies add to ISBNs
$str = preg_replace('/(\s+|:|-)/', '', $str);

if(preg_match("/^ISBN\d{12}(?:\d|X)$/", $str, $matches)){
   echo "ISBN-13 FOUND\n";
   //isbn returned will be 9780113411436
   return 1;
}

else if(preg_match("/^ISBN\d{9}(?:\d|X)$/", $str, $matches)){
   echo "ISBN-10 FOUND\n";  
   //isbn returned will be 9780113411
   return 0;
}
 0
Author: Will C., 2012-12-31 00:02:01
ISBN10_REGEX = /^(?:\d[\ |-]?){9}[\d|X]$/i
ISBN13_REGEX = /^(?:\d[\ |-]?){13}$/i
 0
Author: Naeem Ul Hassan, 2016-08-11 07:46:49