如何生成并验证符合沙特Iqama/国民身份号码格式的随机ID?
沙特Iqama/国民身份号码:正确的生成与验证实现方案
嘿,我来帮你解决这个问题!先聊聊你原代码里的几个关键问题,再给你一套靠谱的实现代码:
原代码的核心问题
- 内存与循环逻辑严重不合理:外层
while(count($random_numbers) < 1000000000)想要生成10亿个ID,这完全不现实——10亿个数字的数组会直接把服务器内存榨干,而且你根本不需要这么多。 - 控制流混乱:
return $sum%10 ? break : echo $random_number;这行有语法错误,而且return会直接终止整个函数,后面的代码永远跑不到;break的作用域也不对,没法正确跳出循环。 - 校验逻辑细节偏差:虽然你用到了Luhn算法,但遍历的方式和校验位的计算逻辑有小问题,导致无法正确验证。
正确的实现方案
首先明确沙特ID的官方规则:
- 必须是10位数字
- 第1位:
1代表沙特国民,2代表Iqama(居住许可) - 第10位是校验位,通过Luhn算法计算得出
1. 验证函数(检查ID是否合法)
这个函数会先验证格式合法性,再用Luhn算法校验最后一位:
function validateSaudiId(string $id): bool { // 第一步:验证基础格式:10位数字,第一位是1或2 if (!preg_match('/^[12]\d{9}$/', $id)) { return false; } // 第二步:执行Luhn算法校验 $sum = 0; $digits = str_split($id); // 从倒数第二位开始向左遍历(Luhn算法标准实现方式) for ($i = 8; $i >= 0; $i--) { $digit = (int)$digits[$i]; // 从右数的奇数位(对应原数组的偶数索引)数字乘以2 if ((8 - $i) % 2 === 0) { $doubled = $digit * 2; // 乘积大于9的话,减去9(等价于拆成两个数字相加) $sum += $doubled > 9 ? $doubled - 9 : $doubled; } else { $sum += $digit; } } // 计算正确的校验位,和实际第10位对比 $checkDigit = (int)$digits[9]; return (10 - ($sum % 10)) % 10 === $checkDigit; }
2. 生成函数(生成合法的随机ID)
这个函数直接生成前9位符合要求的数字,再计算出正确的校验位,确保生成的ID100%有效,不用反复尝试:
function generateSaudiId(bool $isNational = true): string { // 第一位:1=沙特国民,2=Iqama $firstDigit = $isNational ? '1' : '2'; // 生成中间7位随机数字(第2到第9位) $middleDigits = mt_rand(1000000, 9999999); // 拼接成前9位数字(确保是9位,不足补0) $firstNine = $firstDigit . str_pad((string)$middleDigits, 7, '0', STR_PAD_LEFT); $digits = str_split($firstNine); // 计算Luhn校验位 $sum = 0; for ($i = 0; $i < 9; $i++) { $digit = (int)$digits[$i]; // 从左数的奇数索引(0、2、4、6、8)数字乘以2 if ($i % 2 === 0) { $doubled = $digit * 2; $sum += $doubled > 9 ? $doubled - 9 : $doubled; } else { $sum += $digit; } } $checkDigit = (10 - ($sum % 10)) % 10; // 拼接完整的10位ID return $firstNine . (string)$checkDigit; }
使用示例
// 生成一个沙特国民ID $nationalId = generateSaudiId(true); echo "生成的沙特国民ID:{$nationalId}\n"; echo "验证结果:" . (validateSaudiId($nationalId) ? '有效' : '无效') . "\n"; // 生成一个Iqama ID $iqamaId = generateSaudiId(false); echo "生成的Iqama ID:{$iqamaId}\n"; echo "验证结果:" . (validateSaudiId($iqamaId) ? '有效' : '无效') . "\n";
额外说明
- 如果你需要避免生成重复的ID,只需要把已生成的ID存在一个数据库表或者Redis里,生成前检查一下就行——不过10位数字有10亿种组合,除非你生成几百万个以上,否则重复概率极低。
- 验证函数可以直接用于用户输入的ID校验,确保格式和校验位都合法。
内容的提问来源于stack exchange,提问作者sasha




