最近看到一則有趣的新聞,原來身分字號 A123456789
真有其人阿!決定來稍微了解一下,身分證字號是怎產生出來的,順便寫寫身分證字號檢查器…原本想寫產生器的,但想想還是算了 XD
目前現行的身分證字號一共有 10 碼,包括起首的大寫的英文字母與接續的九個阿拉伯數字(如:A123456789),大抵可以將身分證字號分成五區:區域碼、性別碼、身分碼、流水碼跟檢核碼。
區域碼 | 性別碼 | 身分碼 | 流水碼 | 檢核碼 | |||||
---|---|---|---|---|---|---|---|---|---|
A-Z | 男:1 女:2 |
其他:0-5 取得國籍之外國人:6 無戶籍國民:7 港澳居民:8 大陸地區人民:9 |
阿拉伯數字 | 阿拉伯數字 |
其中首碼的縣市代碼是以報戶口的地區來區分的、而性別代碼則是指首位數字,其中男性為1、女性為2,最後第三碼是身分碼,其中 0-5 是保留給國人,6-9 則是保留給歸化的外國人與中港澳人民使用。
關於第三碼的身分碼,在我 2020-08 第一次寫這篇的時候,我並沒有注意到身分碼的資料,但在今天為了寫《【臺灣ID驗證系列】居留證驗證》在查資料的時候,發現了這東西。不過再細查資料,這條規則是在內政部 92 年 4 月 24 日台內戶字第 0920063929 號函規定就被定下了,所以應該是我上次資料漏看了!?
整體來說,一個完整的身份證字號如下:
區域碼 | 性別碼 | 身分碼 | 流水碼 | 檢核碼 | |||||
---|---|---|---|---|---|---|---|---|---|
A | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
在進行編碼檢查時,會將縣市代碼轉換成相對應的數值,如 A 就會被轉換成 10
:
A | B | C | D | E | F | G | H | I | J |
---|---|---|---|---|---|---|---|---|---|
10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 34 | 18 |
K | L | M | N | O | P | Q | R | S | T |
---|---|---|---|---|---|---|---|---|---|
19 | 20 | 21 | 22 | 35 | 23 | 24 | 25 | 26 | 27 |
U | V | W | X | Y | Z |
---|---|---|---|---|---|
28 | 29 | 32 | 30 | 31 | 33 |
將轉換完成的數值,乘上相對應的權重後進行加總:
Index | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
權重 | 1 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 1 |
若總和為 10 的倍數,即為有效的驗證碼。
若改寫成數學判斷式:
將 A123456789
轉換成 10123456789
後套入公式如下:
餘數為 0,表有效的 ID。
有點久沒寫 js 了,順便寫寫 js 練練手好了。把上面的規則寫成程式,如下:
function verifyId(id) {
id = id.trim();
if (id.length != 10) {
console.log("Fail,長度不正確");
return false
}
let countyCode = id.charCodeAt(0);
if (countyCode < 65 | countyCode > 90) {
console.log("Fail,字首英文代號,縣市不正確");
return false
}
let genderCode = id.charCodeAt(1);
if (genderCode != 49 && genderCode != 50) {
console.log("Fail,性別代碼不正確");
return false
}
let serialCode = id.slice(2)
for (let i in serialCode) {
let c = serialCode.charCodeAt(i);
if (c < 48 | c > 57) {
console.log("Fail,數字區出現非數字字元");
return false
}
}
let conver = "ABCDEFGHJKLMNPQRSTUVXYWZIO"
let weights = [1, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1]
id = String(conver.indexOf(id[0]) + 10) + id.slice(1);
checkSum = 0
for (let i = 0; i < id.length; i++) {
c = parseInt(id[i])
w = weights[i]
checkSum += c * w
}
verification = checkSum % 10 == 0
if (verification) {
console.log("Pass");
} else {
console.log("Fail,檢核碼錯誤");
}
return verification
}
console.log(verifyId("A123456789"));
是說如果不要顯示 log , Regular Expression 可以涵蓋前半段的檢查:
function verifyId(id) {
id = id.trim();
<!-- 在 js 中遇到反斜線要跳脫,所以這邊用兩個反斜線 -->
<!-- 如果你看到四個反斜線,那是我為了讓 NexT.Mist 主題順利渲染所再做跳脫 -->
verification = id.match("^[A-Z][12]\\d{8}$")
if(!verification){
return false
}
let conver = "ABCDEFGHJKLMNPQRSTUVXYWZIO"
let weights = [1, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1]
id = String(conver.indexOf(id[0]) + 10) + id.slice(1);
checkSum = 0
for (let i = 0; i < id.length; i++) {
c = parseInt(id[i])
w = weights[i]
checkSum += c * w
}
return checkSum % 10 == 0
}
console.log(verifyId("A123456789"));
因為上述的驗證規則是用於僅輸入身分證字號的情境,若使用情境中有輸入性別、戶籍地與出生日期的情況,可在新增:
性別:
當然就是檢查第二碼來確認啦。
縣市代碼與戶籍地的對照:
不過這個用到的機會不大,鮮少有情境是輸入戶籍地,多數時候都是輸入通訊地 XD
縣市代碼與出生日期的比較:
因為縣市合併的關係,目前有部份縣市代碼已不再賦配。所以可以比較出生日期與停發日期做進一步檢查。
縣市代碼 | 原行政區 | 停發日期 |
---|---|---|
L | 臺中縣 | 2010/12/25 |
R | 臺南縣 | 2010/12/25 |
S | 高雄縣 | 2010/12/25 |
Y | 陽明山管理局 | 1974/01/01 |