регулярное выражение, различающее 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;
}
Как мне убедиться, что я избегаю этой проблемы?
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;
}
См. ДЕМО-ВЕРСИЯ из вышеперечисленного.
Используйте ^
и $
для сопоставления начала и конца строки. При использовании разделителей строк порядок, в котором вы проверяете 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
Поставить проверку ISBN-13 перед проверкой ISBN-10? Это предполагает, что вы хотите сопоставить их как часть любой строки, то есть (в вашем примере есть дополнительный "ISBN:" в начале, поэтому сопоставление в любом месте строки кажется каким-то требованием)
Измените порядок блока 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;
}
ISBN10_REGEX = /^(?:\d[\ |-]?){9}[\d|X]$/i
ISBN13_REGEX = /^(?:\d[\ |-]?){13}$/i