--- title: ID驗證系列|身分證字號驗證 date: 2021-03-10 is_modified: true disqus: cynthiahackmd categories: - "先備知識" tags: - "編碼規則" - "Javascript" - "regexp" - "臺灣ID驗證系列" - "Published" --- {%hackmd @CynthiaChuang/Github-Page-Theme %} <br> 最近看到一則[有趣的新聞](https://www.ctwant.com/article/61090),原來身分字號 `A123456789` 真有其人阿!決定來稍微了解一下,身分證字號是怎產生出來的,順便寫寫身分證字號檢查器...原本想寫產生器的,但想想還是算了 XD <!--more--> <p class="illustration"> <img src="https://i.imgur.com/ggfZhwS.png" alt="中華民國身分證"> 中華民國身分證 </p> ## 編號規則 目前現行的身分證字號一共有 **10 碼**,包括起首的大寫的英文字母與接續的九個阿拉伯數字(如:A123456789),大抵可以將身分證字號分成五區:**區域碼**、**性別碼**、**身分碼**、**流水碼**跟**檢核碼**。 <table> <tbody> <tr> <th>區域碼</th> <th>性別碼</th> <th>身分碼</th> <th colspan="6">流水碼 </th> <th>檢核碼</th> </tr> <tr> <td>A-Z</td> <td>男:1<br> 女:2</td> <td>其他:0-5<br> 取得國籍之外國人:6<br> 無戶籍國民:7<br> 港澳居民:8<br> 大陸地區人民:9 </td> <td colspan="6">阿拉伯數字 </td> <td>阿拉伯數字</td> </tr> </tbody> </table> 其中首碼的縣市代碼是以報戶口的地區來區分的、而性別代碼則是指首位數字,其中男性為1、女性為2,最後第三碼是身分碼,其中 0-5 是保留給國人,6-9 則是保留給歸化的外國人與中港澳人民使用。 關於第三碼的**身分碼**,在我 2020-08 第一次寫這篇的時候,我並沒有注意到身分碼的資料,但在今天為了寫[《【臺灣ID驗證系列】居留證驗證》]( https://hackmd.io/@CynthiaChuang/Check-Resident-Certificate-Number)在[查資料](http://www.academic.fcu.edu.tw/wSite/public/Attachment/f1582594331972.pdf)的時候,發現了這東西。不過再細查資料,這條規則是在內政部 92 年 4 月 24 日台內戶字第 0920063929 號函規定就被定下了,所以應該是我上次資料漏看了!? <br> 整體來說,一個完整的身份證字號如下: <table> <tbody> <tr> <th>區域碼</th> <th>性別碼</th> <th>身分碼</th> <th colspan="6">流水碼 </th> <th>檢核碼</th> </tr> <tr> <td>A </td> <td>1 </td> <td>2 </td> <td>3 </td> <td>4 </td> <td>5 </td> <td>6 </td> <td>7 </td> <td>8 </td> <td>9 </td> </tr> </tbody> </table> <br> 在進行編碼檢查時,會將縣市代碼轉換成相對應的數值,如 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| <br> 將轉換完成的數值,乘上相對應的權重後進行加總: |Index|$n_0$|$n_1$|$n_2$|$n_3$|$n_4$|$n_5$|$n_6$|$n_7$|$n_8$|$n_9$|$n_{10}$| |---|---|---|---|---|---|---|---|---|---|---|---| |權重|1|9|8|7|6|5|4|3|2|1|1| 若總和為 **10 的倍數**,即為有效的驗證碼。 <br> 若改寫成數學判斷式: $$ (n_0\times 1+n_1\times 9+n_2\times 8+n_3\times 7+n_4\times 6+n_5\times 5+n_6\times 4+n_7\times 3+n_8\times 2+n_9\times 1+n_{10}\times 1)\%10 = 0 $$ <br> 將 `A123456789` 轉換成 `10123456789` 後套入公式如下: $$ \begin{aligned} &(1\times 1+0\times 9+1\times 8+2\times 7+3\times 6+4\times 5+5\times 4+6\times 3+7\times 2+8\times 1+9\times 1)\%10 \\ &= (1+0+8+14+18+20+20+18+14+8+9)\%10\\ &= 130\%10\\ &= 0 \end{aligned} $$ 餘數為 0,表有效的 ID。 ## 程式碼 有點久沒寫 js 了,順便寫寫 js 練練手好了。把上面的規則寫成程式,如下: ```javascript= 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")); ``` <br> 是說如果不要顯示 log , Regular Expression 可以涵蓋前半段的檢查: ```javascript= 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真有人 一條龍伯「信用破產」冤跑法庭:別再害我了](https://www.ctwant.com/article/61090)。檢自 ctwant (2020-07-10)。 2. (2006-02-17)。[身分證「A123456789」老被冒用](https://blog.xuite.net/sinner66/blog/5201507-%E8%BA%AB%E5%88%86%E8%AD%89%E3%80%8CA123456789%E3%80%8D%E8%80%81%E8%A2%AB%E5%86%92%E7%94%A8)。檢自 ctwant阿特拉斯的部落格 (2020-07-10)。 3. 協同撰寫。[中華民國國民身分證](https://zh.wikipedia.org/wiki/%E4%B8%AD%E8%8F%AF%E6%B0%91%E5%9C%8B%E5%9C%8B%E6%B0%91%E8%BA%AB%E5%88%86%E8%AD%89#%E9%A9%97%E8%AD%89%E8%A6%8F%E5%89%87)。檢自 維基百科 (2020-07-10)。 4. 內政部 (2019-10)。[「外來人口統一證號格式專案」修正計畫(核定本)](http://www.academic.fcu.edu.tw/wSite/public/Attachment/f1582594331972.pdf)。檢自 內政部 (2021-03-10)。 ## 更新紀錄 :::spoiler 最後更新日期:2021-03-10 - 2021-03-10 更新:新增身分碼資料 - 2020-08-25 更新:新增 Regular expression - 2020-08-10 發布 - 2020-07-13 完稿 - 2020-07-10 起稿 ::: {%hackmd @CynthiaChuang/Github-Page-Footer %}