我有一个If-else语句,用于检查字符串是否有ISBN-10或ISBN-13(书籍ID)。
我面临的问题是在ISBN-13检查之前发生的ISBN-10检查,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;
}
如何确保避免此问题?
答案 0 :(得分:40)
你真的只需要一个正则表达式。然后执行更有效的strlen()
检查以查看哪一个匹配。以下内容将匹配带或不带连字符的字符串中的ISBN-10和ISBN-13值,并且可选地以字符串ISBN:
,ISBN:(space)
或ISBN(space)
开头。
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的维基百科article具有ISBN-10和ISBN-13的一些PHP验证功能。下面是复制,整理和修改的示例,以用于上述函数的略微修改版本。
将退货块更改为:
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。
答案 1 :(得分:3)
使用^
和$
来匹配字符串的开头和结尾。通过使用字符串分隔符,您测试10位或13位代码的顺序无关紧要。
/^ISBN:(\d{9}(?:\d|X))$/
/^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
答案 2 :(得分:1)
切换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;
}
答案 3 :(得分:1)
在ISBN-10检查之前进行ISBN-13检查?这假设您希望将它们作为任何字符串的一部分进行匹配,即(您的示例在开头有一个额外的“ISBN:”,因此匹配字符串中的任何位置似乎是某种要求)
答案 4 :(得分:0)
ISBN10_REGEX = /^(?:\d[\ |-]?){9}[\d|X]$/i
ISBN13_REGEX = /^(?:\d[\ |-]?){13}$/i