Try   HackMD

最近看到一則有趣的新聞,原來身分字號 A123456789 真有其人阿!決定來稍微了解一下,身分證字號是怎產生出來的,順便寫寫身分證字號檢查器原本想寫產生器的,但想想還是算了 XD

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →
中華民國身分證

編號規則

目前現行的身分證字號一共有 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

10 11 12 13 14 15 16 17 34 18
19 20 21 22 35 23 24 25 26 27
28 29 32 30 31 33

將轉換完成的數值,乘上相對應的權重後進行加總:

Index
n0
n1
n2
n3
n4
n5
n6
n7
n8
n9
n10
權重 1 9 8 7 6 5 4 3 2 1 1

若總和為 10 的倍數,即為有效的驗證碼。


若改寫成數學判斷式:

(n0×1+n1×9+n2×8+n3×7+n4×6+n5×5+n6×4+n7×3+n8×2+n9×1+n10×1)%10=0


A123456789 轉換成 10123456789 後套入公式如下:

(1×1+0×9+1×8+2×7+3×6+4×5+5×4+6×3+7×2+8×1+9×1)%10=(1+0+8+14+18+20+20+18+14+8+9)%10=130%10=0

餘數為 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"));

進一步驗證規則

因為上述的驗證規則是用於僅輸入身分證字號的情境,若使用情境中有輸入性別、戶籍地與出生日期的情況,可在新增:

  1. 性別
    當然就是檢查第二碼來確認啦。

  2. 縣市代碼與戶籍地的對照:
    不過這個用到的機會不大,鮮少有情境是輸入戶籍地,多數時候都是輸入通訊地 XD

  3. 縣市代碼與出生日期的比較:
    因為縣市合併的關係,目前有部份縣市代碼已不再賦配。所以可以比較出生日期與停發日期做進一步檢查。

    縣市代碼 原行政區 停發日期
    L 臺中縣 2010/12/25
    R 臺南縣 2010/12/25
    S 高雄縣 2010/12/25
    Y 陽明山管理局 1974/01/01

參考資料

  1. 林姸君 (2020-07-10)。身分證A123456789真有人 一條龍伯「信用破產」冤跑法庭:別再害我了。檢自 ctwant (2020-07-10)。
  2. (2006-02-17)。身分證「A123456789」老被冒用。檢自 ctwant阿特拉斯的部落格 (2020-07-10)。
  3. 協同撰寫。中華民國國民身分證。檢自 維基百科 (2020-07-10)。
  4. 內政部 (2019-10)。「外來人口統一證號格式專案」修正計畫(核定本)。檢自 內政部 (2021-03-10)。

更新紀錄

最後更新日期:2021-03-10
  • 2021-03-10 更新:新增身分碼資料
  • 2020-08-25 更新:新增 Regular expression
  • 2020-08-10 發布
  • 2020-07-13 完稿
  • 2020-07-10 起稿



本文作者:辛西亞.Cynthia
網站連結辛西亞的技能樹 / hackmd 版本
版權聲明:部落格中所有文章,均採用 CC BY-NC-SA 4.0 許可協議。轉載請標明作者、連結與出處!

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →