中国的居民身份证编码格式为:2位一级行政区代码;2位二级行政区代码;2位三级行政区代码;8位出生日期(年月日);3位识别码;1位校验码。其中校验码执行的是中华人民共和国国家标准GB11643-1999,即ISO7064:1983.MOD 11-2。
    前6位的行政区划代码包含的信息只能存储于数据文件中。我看到许多例子都使用数据库或文本文件对其存储,查询起来很不方便。而使用Microsoft.XMLDOM则简单易行:首先将身份证数据转换为XML格式,然后通过XPath进寻查询,体积小,速度快,Client或Server均可执行,也可通过AJAX访问;而不像数据库,只能在Server执行。以下是全部JavaScript代码和XML生成算法的C#代码。

1、HTML文件:

  1. <html xmlns="http://www.w3.org/1999/xhtml"> 
  2. <head> 
  3. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
  4. <title>居民身份证编码合法性检测(基于客户端XML数据)</title> 
  5. <style type="text/css"> 
  6. body, table {  
  7.     font-family:宋体;  
  8.     font-size:9pt;  
  9. }  
  10. table, td {  
  11.     border-width:1px;  
  12.     border-style:solid;  
  13.     border-color:#000000;  
  14. }  
  15. td {  
  16.     height:30;  
  17. }  
  18. input {  
  19.     width:200px;  
  20.     height:22px;  
  21. }  
  22. </style> 
  23. <script type="text/javascript" src="CheckChineseID.js"></script> 
  24. <script type="text/javascript"> 
  25. function xmlLoad() {  
  26.     var xmlDoc = null;  
  27.     try {  
  28.         if (window.ActiveXObject) //IE  
  29.             xmlDoc = new ActiveXObject("Microsoft.XMLDOM");  
  30.         else if (document.implementation.createDocument) //Mozilla, Firefox, Opera, etc.  
  31.             xmlDoc = document.implementation.createDocument("", "", null);  
  32.         xmlDoc.async = false;  
  33.         xmlDoc.load("ChineseID.xml");  
  34.         xmlDoc.setProperty("SelectionLanguage", "XPath");  
  35.     }  
  36.     finally {  
  37.         return xmlDoc;  
  38.     }  
  39. }  
  40. var xmlDoc = xmlLoad();  
  41. function show() {  
  42.     if (xmlDoc == null)  
  43.         alert(navigator.appName + "不支持XML对象,程序无法运行!");  
  44.     else {  
  45.         var result = checkId(id.value, xmlDoc);  
  46.         if (typeof result == "string") {  
  47.             t0.innerHTML = result;  
  48.             t0.style.color = "#FF0000";  
  49.             t1.innerHTML = t2.innerHTML = t3.innerHTML = t4.innerHTML = t5.innerHTML = "N/A";  
  50.         }  
  51.         else {  
  52.             t0.innerHTML = "检测通过!";  
  53.             t0.style.color = "#008000";  
  54.             t1.innerHTML = result[0];  
  55.             t2.innerHTML = result[1];  
  56.             t3.innerHTML = result[2];  
  57.             t4.innerHTML = result[3] + "年" + result[4] + "月" + result[5] + "日【" + result[6] + "岁】";  
  58.             t5.innerHTML = result[7] + "【" + result[8] + "】";  
  59.         }  
  60.         var blink = 0;  
  61.         var blinkId = setInterval(function () {  
  62.             blink % 2 == 0 ? t0.style.backgroundColor = "#FFFFC0" : t0.style.backgroundColor = "#FFFFFF";  
  63.             if (blink++ == 5) clearInterval(blinkId);  
  64.         }, 200);  
  65.     }  
  66. }  
  67. </script> 
  68. </head> 
  69. <body> 
  70. <p align="center"> 
  71. <small><sub>居民</sub></small>身份证编码合法性检测(基于客户端XML数据)<br /><br /> 
  72. 白宇 - 梦辽软件工作室 - 博讯网络有限责任公司<br /> 
  73. Baiyu - Mengliao Software Studio - Bosun Network Co., Ltd.<br /> 
  74. 2012.03.02</p> 
  75. <center> 
  76. <table cellpadding="5" cellspacing="0" width="320"> 
  77.     <tr> 
  78.         <td width="100">身份证号码</td> 
  79.         <td align="center"> 
  80.             <input type="text" maxlength="18" id="id" value="请输入18位身份证号码" style="color:#CCCCCC" 
  81.                 onfocus="if(this.value=='请输入18位身份证号码'){this.value='';this.style.color='#000000';}" 
  82.                 onblur="if(this.value==''){this.value='请输入18位身份证号码';this.style.color='#CCCCCC';}" 
  83.                 ondblclick="clipboardData.setData('Text','150722201203020010');alert('示例:150722201203020010\n\n该示例已复制到剪贴板!');" 
  84.                 onkeypress="if(event.keyCode==13) btn.click()" /> 
  85.         </td> 
  86.     </tr> 
  87.     <tr> 
  88.         <td>检测结果</td> 
  89.         <td id="t0">N/A</td> 
  90.     </tr> 
  91.     <tr> 
  92.         <td>一级行政区</td> 
  93.         <td id="t1">N/A</td> 
  94.     </tr> 
  95.     <tr> 
  96.         <td>二级行政区</td> 
  97.         <td id="t2">N/A</td> 
  98.     </tr> 
  99.     <tr> 
  100.         <td>三级行政区</td> 
  101.         <td id="t3">N/A</td> 
  102.     </tr> 
  103.     <tr> 
  104.         <td>出生日期</td> 
  105.         <td id="t4">N/A</td> 
  106.     </tr> 
  107.     <tr> 
  108.         <td>识别码</td> 
  109.         <td id="t5">N/A</td> 
  110.     </tr> 
  111.     <tr> 
  112.         <td colspan="2" align="center"><input type="button" id="btn" value="检测" onclick="show()" /></td> 
  113.     </tr> 
  114. </table> 
  115. </center> 
  116. </body> 
  117. </html> 

2、JavaScript文件:

  1. function checkId(id, xmlDoc) {  
  2.     var result = new Array();  
  3.     var months = new Array(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);  
  4.     if (!id.match(/^\d{17}[\d|x]$/i))  
  5.         return "【格式】检测错误!";  
  6.     var t = xmlDoc.selectNodes("/China/Province[Code='" + id.substr(0, 2) + "0000']/Name")  
  7.     if (t[0] == null)  
  8.         return "【一级行政区】检测错误!";  
  9.     else 
  10.         result.push(t[0].text);  
  11.     t = xmlDoc.selectNodes("/China/Province/City[Code='" + id.substr(0, 4) + "00']/Name")  
  12.     if (t[0] == null)  
  13.         return "【二级行政区】检测错误!";  
  14.     else 
  15.         result.push(t[0].text);  
  16.     t = xmlDoc.selectNodes("/China/Province/City/District[Code='" + id.substr(0, 6) + "']/Name")  
  17.     if (t[0] == null)  
  18.         return "【三级行政区】检测错误!";  
  19.     else 
  20.         result.push(t[0].text);  
  21.     var y, m, d, now = new Date();  
  22.     y = parseInt(id.substr(6, 4));  
  23.     m = parseInt(id.substr(10, 2));  
  24.     d = parseInt(id.substr(12, 2));  
  25.     if (new Date(y + 150, m - 1, d) < now || new Date(y, m - 1, d) > now)  
  26.         return "【日期范围】检测错误!";  
  27.     else 
  28.         result.push(y);  
  29.     if (m < 1 || m > 12)  
  30.         return "【月】检测错误!";  
  31.     else 
  32.         result.push(m);  
  33.     if (y % 4 == 0 && y % 100 != 0 || y % 400 == 0)  
  34.         months[1] = 29;  
  35.     if (d < 1 || d > months[m - 1])  
  36.         return "【日】检测错误!";  
  37.     else 
  38.         result.push(d);  
  39.     result.push(now.getFullYear() - y);   
  40.     if (id.substr(14, 3) == 0)  
  41.         return "【识别码】检测错误!";  
  42.     else {  
  43.         result.push(id.substr(14, 3));  
  44.         result.push(id.substr(14, 3) % 2 == 0 ? "女" : "男");  
  45.     }  
  46.     /*  
  47.     校验码Hash算法  
  48.     中华人民共和国国家标准GB11643-1999  
  49.     ISO 7064:1983.MOD 11-2校验码算法:  
  50.     1) 十七位数字本体码加权求和公式  
  51.     S = Sum(Ai * Wi), i = 0, ... , 16 ,先对前17位数字的权求和  
  52.     Ai:表示第i位置上的身份证号码数字值  
  53.     Wi:表示第i位置上的加权因子  
  54.     Wi:7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4 2  
  55.     Wi也可表示为:2^(17 - i) % 11  
  56.     2) 计算模  
  57.     Y = mod(S, 11)  
  58.     3) 通过模得到对应的校验码V  
  59.     Y:0 1 2 3 4 5 6 7 8 9 10  
  60.     V:1 0 X 9 8 7 6 5 4 3 2  
  61.     V也可表示为:mod(12 - Y, 11), 如结果为10,则转换为X  
  62.     */ 
  63.     var Y = 0;  
  64.     for (var i = 0; i < 17; i++) {  
  65.         var Ai = parseInt(id.substr(i, 1));  
  66.         var Wi = Math.pow(2, 17 - i) % 11;  
  67.         Y += Ai * Wi;  
  68.     }  
  69.     Y = (12 - Y % 11) % 11;  
  70.     var V = Y == 10 ? "X" : Y.toString();  
  71.     if (V != id.substr(17, 1).toUpperCase())  
  72.         return "【校验码】检测错误!" 
  73.     return result;  

3、已经转换好的“行政区划XML数据”,下载后将其保存为ChineseID.xml:

http://mengliao.blog.51cto.com/attachment/201204/876134_1334981976.xml

    这个XML文件是用C#编写了一个小程序,对统计局的纯文本文件进行转换,使用了LINQ,核心代码如下:

  1. class Program  
  2. {  
  3.     static void Main()  
  4.     {  
  5.         Console.WriteLine("开始生成XML文件...\n");  
  6.         using (StreamReader sr = new StreamReader("行政区划代码.txt"))  
  7.         {  
  8.             int Pid = 0, Cid = 0, Did = 0;  
  9.             XElement xe = new XElement("China");  
  10.             while (!sr.EndOfStream)  
  11.             {  
  12.                 string[] items = sr.ReadLine().Split(',');  
  13.                 if (items[0].Substring(2, 4) == "0000"// 后四位为0,省区级行政区  
  14.                 {  
  15.                     xe.Add(new XElement("Province",  
  16.                         new XAttribute("P.ID", Pid++),  
  17.                         new XElement("Name", items[1]),  
  18.                         new XElement("Code", items[0])));  
  19.                     Cid = 0; // 文件格式中按置省区、市县、三级行政区依次排列,故没开始一个新省区行政区,则市县行政区ID归0  
  20.                     Console.Write("生成[{0}]...", items[1]);  
  21.                 }  
  22.                 else if (items[0].Substring(4, 2) == "00"// 否则后两位为0,市县级行政区  
  23.                 {  
  24.                     var query = (from p in xe.Elements()  
  25.                                  where p.Element("Code").Value.Substring(0, 2) == items[0].Substring(0, 2)  
  26.                                  select p).Single();  
  27.                     query.Add(new XElement("City",  
  28.                         new XAttribute("C.ID", Cid++),  
  29.                         new XElement("Name", items[1]),  
  30.                         new XElement("Code", items[0])));  
  31.                     Did = 0; // 文件格式中按置省区、市县、三级行政区依次排列,故没开始一个新市县行政区,则三级行政区ID归0  
  32.                 }  
  33.                 else // 否则后两位不为0,三级行政区  
  34.                 {  
  35.                     // 分两步执行查询,提高性能  
  36.                     var query = (from p in xe.Elements() // 获取所有Province元素  
  37.                                  where p.Element("Code").Value.Substring(0, 2) == items[0].Substring(0, 2)  
  38.                                  from c in p.Elements("City"// 获取该Province元素下的所有City元素  
  39.                                  where c.Element("Code").Value.Substring(2, 2) == items[0].Substring(2, 2)  
  40.                                  select c).Single();  
  41.                     query.Add(new XElement("District",  
  42.                         new XAttribute("D.ID", Did++),  
  43.                         new XElement("Name", items[1]),  
  44.                         new XElement("Code", items[0])));  
  45.                     Console.Write(".");  
  46.                 }  
  47.             }  
  48.             // 添加注释,并保存(可以使用XElement直接保存)  
  49.             XDocument xDoc = new XDocument(new XComment("梦辽软件2012.03生成"),  
  50.                 new XComment("国家统计局截至2011.10.31县及县以上行政区划代码"));  
  51.             xDoc.Add(xe);  
  52.             xDoc.Save("ChineseID.xml");  
  53.             Console.Write("\n\nXML文件成功生成,按任意键退出...");  
  54.             Console.ReadKey();  
  55.         }  
  56.     }  

    以上代码该有的注释都有了,就不多解释了。