Regex, различающийся между 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;
}
Как я могу избежать этой проблемы?
Ответы
Ответ 1
Вам действительно нужно только одно регулярное выражение. Затем выполните более эффективную проверку 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
.
См. DEMO выше.
Проверка ISBN:
Для строгого подтверждения статьи Wikipedia для 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;
}
См. DEMO выше.
Ответ 2
Используйте ^
и $
для соответствия началу и концу строки. Используя ограничители строк, порядок, в котором вы тестируете 10 или 13-значные коды, не имеет значения.
10 цифр
/^ISBN:(\d{9}(?:\d|X))$/
13 цифр
/^ISBN:(\d{12}(?:\d|X))$/
Примечание.. Согласно http://en.wikipedia.org/wiki/International_Standard_Book_Number, похоже, что ISBN могут иметь -
в них также. Но на основе $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
Поместите проверку ISBN-13 перед проверкой ISBN-10? Это предполагает, что вы хотите сопоставить их как часть любой строки, то есть (в вашем примере есть дополнительный "ISBN:" в начале, поэтому совпадение в любом месте строки, по-видимому, является требованием какого-то рода)
Ответ 4
Переключите порядок блока 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;
}
Ответ 5
ISBN10_REGEX = /^(?:\d[\ |-]?){9}[\d|X]$/i
ISBN13_REGEX = /^(?:\d[\ |-]?){13}$/i