浅谈公民身份号码

中华人民共和国国家标准《GB 11643-1999 公民身份号码》的主要内容如下:

  • 范围:本标准规定了公民身份号码的编码对象、号码的结构和表现形式,使每个编码对象获得一个唯一的、不变的法定号码。
  • 编码对象:公民身份号码的编码对象是具有中华人民共和国国籍的公民。
  • 号码的结构:公民身份号码是特征组合码,由十七位数字本体码和一位数字检验码组成。排列顺序从左至右依次为:六位数字地址码,八位数字出生日期码,三位数字顺序码和一位数字校验码。
  • 地址码:表示编码对象常住户口所在县(市、旗、区)的行政区划代码,按 GB/T 2260 的规定执行。
  • 出生日期码:表示编码对象出生的年、月、日,按 GB/T 7408 的规定执行,年、月、日代码之间不用分隔符。
  • 顺序码:表示在同一地址码所标识的区域范围内,对同年、同月、同日出生的人编定的顺序号,顺序码的奇数分配给男性,偶数分配给女性。
  • 校验码:校验码采用 ISO 7064:1983, MOD 11-2 校验系统。

校验码的计算方法为:

  1. 将公民身份号码的第一位到第十七位数字分别乘以以下加权因子:7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2。
  2. 将这十七位数字和加权因子相乘的结果相加。
  3. 用加出来和除以11,得到的余数只可能是 0 1 2 3 4 5 6 7 8 9 10 这十一个数字,
  4. 对应的校验码为:1 0 X 9 8 7 6 5 4 3 2。

公民身份证号码在很多程序中都会用到。所以,我写了一个助手类,如下所示:

using System;
using System.Globalization;
using System.Diagnostics;

namespace Skyiv.Ben.Common
{
  /// <summary>
  /// 中华人民共和国居民身份证
  /// </summary>
  public sealed class Idcard
  {
    [Flags]
    public enum VerifyMode { None = 0, CheckNumber = 1, Date = 2, Full = CheckNumber | Date }
    public VerifyMode Mode { get; private set; }
    public DateTime MaxDate { get; private set; }
    public static readonly Idcard None = new Idcard(VerifyMode.None);
    public static readonly Idcard CheckNumber = new Idcard(VerifyMode.CheckNumber);
    public static readonly Idcard Date = new Idcard(VerifyMode.Date);
    public static readonly Idcard Full = new Idcard(VerifyMode.Full);
    static readonly byte[] weight = { 7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2 };
    static readonly string code = "10X98765432";
    static readonly DateTime MinDate = new DateTime(1800, 1, 1);

    public Idcard(VerifyMode mode) : this(mode, DateTime.MaxValue) { }

    public Idcard(VerifyMode mode, DateTime maxDate)
    {
      Mode = mode;
      MaxDate = maxDate;
    }

    public long String2Number(string s)
    {
      long n;
      string msg;
      if (!TryString2Number(s, out n, out msg)) throw new ArgumentOutOfRangeException(msg, (Exception)null);
      return n;
    }

    public string Number2String(long n)
    {
      return (n == 0) ? "" : (Math.Abs(n).ToString() + ((n < 0) ? "X" : ""));
    }

    public bool TryString2Number(string s, out long n, out string msg)
    {
      if ((msg = TryString2Number(s, out n)) == null) return true;
      msg = "身份证号(" + s + ")" + msg;
      return false;
    }

    public string Convert15to18(string oldId)
    {
      string newId;
      if (!TryConvert15to18(oldId, out newId)) throw new ArgumentOutOfRangeException(newId, (Exception)null);
      return newId;
    }

    public bool TryConvert15to18(string oldId, out string newId)
    {
      if (oldId != null && oldId.Length == 18 && oldId.StartsWith("000")) oldId = oldId.Substring(3);
      long n;
      if (!TryString2Number(oldId, out n, out newId)) return false;
      newId = oldId;
      if (oldId.Length == 18) return true;
      Debug.Assert(oldId.Length == 15);
      newId = oldId.Substring(0, 6) + GetDateStr(oldId) + oldId.Substring(12);
      newId += GetCheckNumber(newId);
      return true;
    }

    string TryString2Number(string s, out long n)
    {
      n = 0;
      if (s == null) return "不能为空";
      string msg;
      if ((msg = VerifyBase(s)) != null) return msg;
      if (Has(VerifyMode.CheckNumber) && (msg = VerifyCheckNumber(s)) != null) return msg;
      if (Has(VerifyMode.Date) && (msg = VerifyDate(s)) != null) return msg;
      Debug.Assert(s.Length == 15 || s.Length == 18);
      if (s.Length == 18 && !char.IsDigit(s, 17)) s = "-" + s.Substring(0, 17);
      n = long.Parse(s);
      return msg;
    }

    string VerifyBase(string s)
    {
      if (s.Length != 15 && s.Length != 18) return "必须是(15)或者(18)位";
      if (s[0] == ‘0‘) return "不能以零开头";
      for (int i = 0; i < s.Length; i++)
        if (i == 17 && !char.IsDigit(s, i) && s[i] != ‘x‘ && s[i] != ‘X‘) return "第(18)位必须是数字或者(x)或者(X)";
        else if (i != 17 && !char.IsDigit(s, i)) return "除第(18)位外必须是数字";
      return null;
    }

    string VerifyCheckNumber(string s)
    {
      Debug.Assert(s.Length == 15 || s.Length == 18);
      if (s.Length != 18 || char.ToUpper(s[17]) == GetCheckNumber(s)) return null;
      return "校验码错";
    }

    string VerifyDate(string s)
    {
      Debug.Assert(s.Length == 15 || s.Length == 18);
      var dateStr = GetDateStr(s);
      DateTime date;
      if (!DateTime.TryParseExact(dateStr, "yyyyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None, out date)) return "日期不合法";
      if (date < MinDate) return "日期太小";
      if (date > MaxDate) return "日期太大";
      return null;
    }

    string GetDateStr(string s)
    {
      var dateStr = (s.Length == 18) ? "" : ((int.Parse(s.Substring(12, 3)) > 995) ? "18" : "19");
      return dateStr + s.Substring(6, (s.Length == 18) ? 8 : 6);
    }

    char GetCheckNumber(string s)
    {
      Debug.Assert(s.Length == 17 || s.Length == 18);
      var sum = 0;
      for (var i = 0; i < weight.Length; i++) sum += weight[i] * (s[i] - ‘0‘);
      return code[sum % 11];
    }

    bool Has(VerifyMode mode)
    {
      return (Mode & mode) == mode;
    }
  }
}

示例程序如下所示:

using System;
using Skyiv.Ben.Common;

namespace Skyiv.Ben
{
  class Test
  {
    static void Main()
    {
      string[] idcards =
      {
        "123456",
        "123456950229002",
        "123456950228002",
        "070843199507012145",
        "37084-199507012145",
        "37084319950701214Y",
        "370843179912312147",
        "370843199507012145",
        "15022319871109212X",
      };
      long n;
      string msg;
      foreach (var idcard in idcards)
      {
        var valid = Idcard.Full.TryString2Number(idcard, out n, out msg);
        Console.WriteLine("{0,-18}: {1}", idcard, valid ? "OK" : msg);
      }
    }
  }
}

这个程序的输出结果是:

123456            : 身份证号(123456)必须是(15)或者(18)位
123456950229002   : 身份证号(123456950229002)日期不合法
123456950228002   : OK
070843199507012145: 身份证号(070843199507012145)不能以零开头
37084-199507012145: 身份证号(37084-199507012145)除第(18)位外必须是数字
37084319950701214Y: 身份证号(37084319950701214Y)第(18)位必须是数字或者(x)或者(X)
370843179912312147: 身份证号(370843179912312147)日期太小
370843199507012145: 身份证号(370843199507012145)校验码错
15022319871109212X: OK

版权声明:本文为博主http://www.zuiniusn.com原创文章,未经博主允许不得转载。

时间: 2024-10-27 03:37:10

浅谈公民身份号码的相关文章

公民身份号码

结构和形式 1.号码的结构 公民身份号码是特征组合码,由十七位数字本体码和一位校验码组成.排列顺序从左至右依次为:六位数字地址码,八位数字出生日期码,三位数字顺序码和一位数字校验码. 2.地址码 表示编码对象常住户口所在县(市.旗.区)的行政区划代码,按GB/T2260的规定执行. 3.出生日期码 表示编码对象出生的年.月.日,按GB/T7408的规定执行,年.月.日代码之间不用分隔符. 4.顺序码 表示在同一地址码所标识的区域范围内,对同年.同月.同日出生的人编定的顺序号,顺序码的奇数分配给男

浅谈IM软件怎样建立安全socket连接、登录

----------------------------------------------------欢迎查看IM软件业务知识<专栏>-------------------------------------------------------------------使用状态机来保持在线状态 [点击]                      拼图算法,将零碎小图,整理到一张大图上[点击]登录导航 [点击]                                          

浅谈CSS优先级机制(一)

初次写随笔,如果有哪个地方不足还望大神指点改正,下面我来谈谈我对于CSS优先级的了解吧. CSS优先级,通俗的理解就是你给元素等一堆属性描述,然后最后到底是哪个描述作为最终显示的效果的规则或机制(个人理解).以下我将分为几个点来谈谈优先级的确定. 1.引入方式: CSS引入的方式,我目前只知道四种:内联式.内嵌式.导入式.链接式(当然网上的说法名称不一,理解就好). 各种引入方式的用法我在这里就不再多说了.以上我所按顺序罗列的四个方式是理论上的优先级顺序,也就是说,我使用内联式引入的css代码作

浅谈安全性攻击人为攻击的主要形式和防御

0x01 安全性攻击主要的两种方式 当前,对信息系统(包括硬件.软件.数据.人.物理环境及其基础设施)的攻击来自多方面,这些攻击我们可以宏观地分为人为攻击(主观因素)和自然灾害攻击(客观因素),这两大类的攻击都会对信息安全构成威胁.造成自然灾害攻击的自然因素包括各种自然灾害:如水.火.雷.电.风暴.烟尘.虫害.鼠害.海啸和地震等:系统的环境和场地条件,如温度.湿度.电源.地线和其他防护设施不良造成的威胁:电磁辐射和电磁干扰的威胁:硬件设备自然老化,可靠性下降的威胁等.因为自然灾害往往不可预知和抗

浅谈协议安全

写下最近在研究的macsec方面的东西.思路不是很清晰现在,我就从wep开始谈吧.我将从几个方面来谈这些事,比如密码算法,认证过程等.各位看官且看. Wep设计的思想是通过使用RC4流密码算法加密来保护数据的机密性,通过问答机制实现对用户身份认证和接入控制(其实就是两元对等的模型.)然后过过CRC32循环冗余校验码来保护数据完整性.其实这里就是一个封包格式的填充 为了让验证段处理固定的报文格式. WEP帧的封装过程 我上个图: 看上图.我解释一下.Wep在传输过程中协议保护了完整性.防止数据被篡

浅谈nginx(一)

此文主要介绍nginx的基础知识及其基本配置,一为巩固,二为记录 知识点: nginx的作用 nginx的基本配置框架 nginx一些常用模块介绍 1.什么是nginx     nginx是一款免费的,开源的,高性能的HTTP服务软件,它不仅能     够支持反向代理服务器,而且也能当作IMPA/POP3代理服务.它稳     定, 配置丰富,设置简单,而且占用系统硬件资源少!这些特性     使得它深受广大用户喜欢. 1.1 Nginx的程序架构 Nginx架构: master/worker

浅谈JS之AJAX

0x00:什么是Ajax? Ajax是Asynchronous Javascript And Xml 的缩写(异步javascript及xml),Ajax是使用javascript在浏览器后台操作HTTP和web服务器进行数据交换(用户不知道也感觉不出来,就跟桌面应用程序似的进行数据交互),它不会导致页面重新加载,这样才有更好的用户体验. Ajax是基于以下开放标准: javascript(DOM) css html xml(json) 通俗的说就是使用了javascript(DOM)的XMLH

浅谈程序员创业(要有一个自己的网站,最好的方式还是自己定位一个产品,用心把这个产品做好)

浅谈程序员创业 ——作者:邓学彬.Jiesoft 1.什么是创业? 关于“创业”二字有必要重新学习一下,找了两个相对权威定义: 创业就是创业者对自己拥有的资源或通过努力能够拥有的资源进行优化整合,从而创造出更大经济或社会价值的过程.——百度百科 创业是一种思考.推理和行为方式,它为机会所驱动,需要在方法上全盘考虑并拥有和谐的领导能力.创业必须要贡献出时间.付出努力,承担相应的财务的.精神的和社会的风险,并获得金钱的回报.个人的满足和独立自主.——互动百科 两者都给出了明确的创业定义,这里想着重想

浅谈图片服务器的架构演进

浅谈图片服务器的架构演进 现在几乎任何一个网站.Web App以及移动APP等应用都需要有图片展示的功能,对于图片功能从下至上都是很重要的.必须要具有前瞻性的规划好图片服务器,图片的上传和下载速度至关重要,当然这并不是说一上来就搞很NB的架构,至少具备一定扩展性和稳定性.虽然各种架构设计都有,在这里我只是谈谈我的一些个人想法. 对于图片服务器来说IO无疑是消耗资源最为严重的,对于web应用来说需要将图片服务器做一定的分离,否则很可能因为图片服务器的IO负载导致应用 崩溃.因此尤其对于大型网站和应