選單

號碼校驗的原理:字元集、長度與校驗位

號碼校驗由三層組成:字元集是否合法、位數對不對、以及能否由其餘位算出校驗位。本文說明三層各自能查出什麼錯誤、處理順序為什麼重要,以及為什麼格式通過不等於號碼真實。

發佈於

  • 號碼校驗
  • 校驗位

貼上一個號碼並按下校驗,背後其實是好幾個彼此獨立的判斷疊在一起。本文說明號碼校驗的原理:字元集、長度與校驗位這三層各自負責什麼,處理順序為什麼不能顛倒,以及介面上的四種結論分別代表什麼。讀完之後,你會清楚一個通過校驗的號碼究竟證明了些什麼,又沒有證明什麼。

什麼是號碼校驗?

號碼校驗是在不連線任何資料庫的前提下,只憑號碼本身的字元與一條公開規則,判斷這串字元是否自洽。它是一種查錯機制,不是查詢機制:不需要知道號碼屬於誰、有沒有被發出去、目前能不能使用,只需要把「這串字元有沒有被打錯」濃縮成一個可以重複執行的動作。

這件事之所以可行,是因為許多編號制度刻意在號碼裡留下一席之地給校驗位。其餘的位可以自由分配,最後一位(少數制度是最後兩位)則由前面那些位按公開規則推算出來。只要有人抄錯一位,重新算出來的結果就會對不上。

也因為它只是自洽性檢查,所以它便宜、可以離線執行、結果可重複,而且完全不需要對方的配合。這正是它被廣泛用在表單、資料匯入與批次處理裡的原因。

它也有明確的極限。校驗能處理的是「這串字元有沒有被打錯」,而不是「這串字元該不該被接受」。後者牽涉到業務規則、權限與登記狀態,屬於流程的其他環節。把兩件事混在一支函式裡,最後會得到一個誰都說不清楚的結論。

三層過濾:字元集、長度與校驗位

這三層能發現的錯誤類型完全不同,混在一起講就很容易誤判。

層次 檢查什麼 抓得住的錯誤 抓不住的錯誤
字元集 是否只含制度允許的字元 誤打了字母、全角符號或空白 字元都合法但位置放錯
長度 位數是否落在制度允許的範圍 少打一位、多打一位、整段被截斷 位數對但內容全錯
校驗位 能否由其餘位算出指定的那一位 單一字元打錯、部分相鄰換位 兩個錯誤恰好互相抵銷

順序是有意義的:先做正規化,再查字元集,接著比對長度,最後才計算校驗位。如果順序顛倒,例如還沒清掉分隔符號就直接算校驗位,就會對一個其實正確的號碼報出「校驗失敗」,把使用者引導到錯誤的方向。

還有一點常被忽略:這三層的失敗訊息應該是不同的。長度不足與校驗位不成立,對使用者而言是完全不同的兩個動作,混成同一句話,等於要他猜。

還有一件事要在同一層處理:輸入被截斷的情況。欄位長度上限若設得太緊,貼上的號碼會在進入校驗之前就被切掉,於是校驗對一個殘缺的值做出判斷,回報的錯誤與實際原因完全無關。這種問題在跨國表單裡特別常見。

為什麼「格式對」不等於「號碼真」?

校驗位證明的只是這串字元自洽,不證明它被發行過、屬於誰、能不能用。這中間隔著好幾層完全不同的問題:發行機構有沒有實際核發過這個號碼、它目前的狀態是正常還是停用、它對應的是哪一位持有人。這些都需要向發行機構查詢,演算法本身答不了。

把「校驗通過」講成「號碼真實有效」,會產生兩種傷害。對使用者而言,他會以為系統已經替他確認了一件其實沒被確認的事;對系統而言,一旦真的有問題,責任歸屬會變得模糊。因此誠實的介面會把結論分成四態,而不是一個真假值:校驗通過、校驗失敗、僅格式、無規則。

最後一態特別容易被忽略。當一個號碼不屬於工具收錄的任何公開制度時,正確的說法是「沒有對應規則」,而不是「這個號碼是假的」。前者描述的是工具的能力邊界,後者卻是對使用者的指控,兩者的差別在客服現場非常明顯。

四態還有一個好處是它可以被統計。若你把每一態的次數分開記錄,就能看出使用者最常卡在哪一關:是長度打錯、校驗位抄錯,還是根本沒有對應規則。這些數字比一句整體成功率有用得多,因為它直接指出該改哪一段流程。

一個號碼可能同時屬於多個制度

同一個字串同時滿足兩個制度的規則,並不少見:長度、字元集與校驗方式可能剛好都對得上。這不是錯誤,而是兩個各自為真的敘述。

比較好的做法是把所有吻合的候選制度並列出來,讓使用者自己判斷脈絡,而不是由工具替他猜一個國家。猜錯的代價很高:一個本來合法的號碼可能因此被標成失敗,使用者改了半天也改不好。

並列還有一個好處:後續處理比較好安排。當你確定某個欄位只會收某一種制度的值時,可以在更外層再篩一次;但在工具層,保持並列是比較誠實的設計,也讓錯誤訊息多了一個可以解釋的角度。

正規化:空格、連字號與大小寫

使用者貼進來的號碼幾乎不會是乾淨的。常見的雜訊包括:

  • 為了閱讀而加入的空格、連字號、句點與斜線。
  • 從全角輸入法帶進來的全角數字與全角字母。
  • 大小寫混用,以及從試算表帶出來的前導單引號與不可見字元。

正規化的做法是先去除分隔符號、統一大小寫,再做後續判斷。它唯一的作用是讓同一個號碼不會因為排版差異被當成兩個不同的輸入;它不能修復真正的輸入錯誤,也不是任何形式的安全措施。

也因為如此,正規化應該集中在一處實作。若前端與後端各寫一份,兩邊對「該去掉哪些字元」的認定遲早會分歧,於是同一個號碼在一邊通過、在另一邊失敗。

有一點要留神:正規化只該影響「怎麼比較」,不該影響「顯示什麼」。介面上仍應保留使用者原本貼上的樣子,讓他知道系統究竟拿什麼去比對,否則他會以為自己打錯了。

還有一個容易被忽略的雜訊來源是複製貼上。從網頁或文件複製時,常會夾帶不斷行空格或方向控制字元,這些字元在畫面上看不出來,卻會讓字元集檢查失敗。若錯誤訊息只說「格式不符」,使用者永遠找不到原因;比較好的做法是同時指出第一個不被接受的字元位置。

給開發者:校驗管線的層次劃分

把校驗寫成單一函式、回傳一個真假值,是後續所有麻煩的來源。比較好的做法是拆成四段,每段回傳不同的結果型態:

  • 正規化:輸出清洗後的字串,同時保留原始輸入。
  • 制度判定:輸出所有吻合的候選制度,可以是零到多個。
  • 逐層檢查:分別回報字元集、長度與校驗位的結果,而不是只給一個總結。
  • 結論組裝:把上一步的結果映射到四態之一,並附上依據。

這樣拆的好處是錯誤訊息可以具體到「位數不足」,而不是籠統的「號碼錯誤」,使用者才知道要改什麼。真正會算錯的地方通常不在算式,而在這些前置處理的順序。想看家族層級的差異,可以接著讀校驗位演算法家族。

還有一種情況值得提早設計:有些號碼根本沒有公開的演算法可算,只能確認格式。這類情境的處理方式整理在沒有校驗位的號碼。

下一步

先在紙上畫出你的校驗管線,確認正規化、字元集、長度與校驗位是四件事而不是一件事,然後替每一層各準備幾個刻意失敗的樣本。想直接看四種結論長什麼樣子,可以開號碼校驗工具貼上一組明顯是構造值的號碼試試,觀察它有沒有誠實地把「僅格式」與「無規則」分開顯示。

文中出現的號碼與校驗結果都是為說明演算法而構造的示例,不對應任何真實的個人、企業或帳戶,也不能當成校驗通過或真實可用的依據。

繼續閱讀

卡號與身分證號校驗工具相關文章