单元用途:身份证号码相关处理                                              //
//  单元作者:池龙                                                            //

//  备    注:                                                                //
//      区划代码转换为名称的部分,需要从外部文件中读取区划定义的数据(如随单  //
//  元发布的文件 regioncode.dat )。                                          //
//                                                                            //
//      该文件为纯文本文件,每行是一个代码和名称的对应关系。代码和名称之间以  //
//  逗号作分隔。以#开头的行作为注释行,空行不处理。程序中作了一定的容错处理, //
//  通常情况下,不正确的数据会被略过。但在重新编排时,还是请尽量保证数据的正  //
//  确性。                                                                    //
//                                                                            //
//   另外,为了保证查找的效率,所以要求文件中的记录按区划代码由小到大的顺  //
// 序排列。                                                                 //

unit ascidnum;

interface

uses
  Controls,
  Classes,
  SysUtils;

type
//性别
  TSex = (sexUnknown,   //未知
          sexMale,      //男性
          sexFemale);   //女性

//身份证号码的类型
  TIDNumType = (idnum15,   //15位
                idnum18);  //18位

//校验结果
  TChecksumRst = (csNoChecksum,       //无校验(15位)
                  csChecksumError,    //错误
                  csChecksumCorrect); //正确

//个人信息
  TIDNumInfo = record
    IDNumType   : TIDNumType;   //身份证号码类型
    RegionCode  : string;       //区划代码
    RegionStr   : string;       //区划名称
    BirthDay    : TDate;        //生日
    Sex         : TSex;         //性别
    ChecksumRst : TChecksumRst; //校验结果
  end;

//区划代码对应关系
  TRegionCodeTransRec = record
    RegionCode: string;     //代码
    RegionStr : string;     //名称
  end;

TRegionCodeTransArray = array of TRegionCodeTransRec;

//分离过的区域代码
  TSepRegionCode = record
    ProvinceCode : string; //省级代码
    CityCode     : string; //市级代码
    CountyCode   : string; //区县级代码   
  end;

TStrArray = array of string;

const
//18位身份证号码的校验方式中使用的常量
//从左向右,除了末尾的校验位之外,每位的权值
  cPosPowerArray : array[1..17] of integer = (7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2);

//得到的校验值对应的第18位字符
  cCheckSumValueArray : array[0..10] of char = ('1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2');

//取身份证号码中的信息
//为了加快速度,不将区划代码进行转换
//需要取得确切的区划名称,用GetRegionStr函数
function GetIDNumInfo(IDNum: string; var IDNumInfo: TIDNumInfo): boolean;

//检查身份证号码的有效性
//内部仍然是利用GetIDNumInfo函数
function IsIDNumValid(IDNum: string): boolean;

//将15位身份证号码扩展为18位
function ExpandIDNum(IDNum15: string; var IDNum18: string): boolean;

//根据区划代码取相应的字符串
function GetRegionStr(RegionCode: string; FromFileName: string): string; overload;

function GetRegionStr(RegionCode: string): string; overload;

function GetRegionStr(var IDNumInfo: TIDNumInfo; FromFileName: string): boolean; overload;

function GetRegionStr(var IDNumInfo: TIDNumInfo): boolean; overload;

//从文件中区划代码信息
function LoadRegionDat(FromFileName: string; var RegionCodeArray: TRegionCodeTransArray): boolean; overload;
//下面这个函数的作用是直接将区划代码信息读入到本单元的公用变量里
function LoadRegionDat(FromFileName: string): boolean; overload;

implementation

var
  RegionCodeTransArray : TRegionCodeTransArray;

//判断这个字符是否为数字
function IsDigiChar(TestChar: Char): boolean;
begin
//直接判断文字
  Result := TestChar in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
//当然这里也可以使用判断字符ASCII值所在区间的方法,如:
//  Result := ord(TestChar) in [ord('0')..ord('9')];
end;

//判断一个字符串是否全部由数字组成
function IsDigiStr(TestStr: string): boolean;
var
  Count0 : integer;
begin
  if Length(TestStr) > 0 then
  begin
    Result := true;
    for Count0 := 1 to Length(TestStr) do
      if not IsDigiChar(TestStr[Count0]) then
      begin
        Result := false;
        Break;
      end;
  end else
    Result := false;
end;

//用于生成18位身份证号码中的校验字符
//注意:此函数不检查前17位的有效性以及其是否为数字
function GenIDNumCheckSumChar(IDNum18: string): char;
var
  DataSum : integer;
  Count0 : integer;
begin
  if length(IDNum18) = 18 then
  begin
    DataSum := 0;
//将前17位上的各个数字加权累加
    for Count0 := 1 to 17 do
      Inc(DataSum, StrtoInt(IDNum18[Count0]) * cPosPowerArray[Count0]);
//结果取模,再从字典中得到对应的字符
    Result := cCheckSumValueArray[DataSum mod 11];
  end else
    Result := ' ';
end;

//将15位身份证号码扩展为18位
function ExpandIDNum(IDNum15: string; var IDNum18: string): boolean;
var
  IDNumInfo: TIDNumInfo;
  TmpIDNum : string;
begin
  Result := false;
  IDNum18 := '';
//先检察原有的号码是否正确
  if (length(IDNum15) = 15) and GetIDNumInfo(IDNum15, IDNumInfo) then
  begin
//行政区划代码 + 生日 + 顺序码 + 校验
    TmpIDNum := IDNumInfo.RegionCode + formatdatetime('yyyymmdd', IDNumInfo.BirthDay) + Copy(IDNum15, 13, 3) + ' ';
//生成校验码,补到末位
    TmpIDNum[18] := GenIDNumCheckSumChar(TmpIDNum);
    IDNum18      := TmpIDNum;
    Result       := true;
  end;
end;

function GetIDNumInfo(IDNum: string; var IDNumInfo: TIDNumInfo): boolean;
var
  ToProcess : boolean;
  IDNumLen,
  Year, Month, Day : word;
  SexMark          : char;
begin
  Result := false;
  FillChar(IDNumInfo, sizeof(IDNumInfo), #0);

IDNumLen := length(IDNum);
  if IDNumLen in [15, 18] then
  begin
    ToProcess := false;
//检查字符串是否符合要求
    case IDNumLen of
//15位身份证号码,全部要求为数字
      15  : begin
              ToProcess := IsDigiStr(IDNum);
              IDNumInfo.IDNumType := idnum15;
              IDNumInfo.ChecksumRst := csNoChecksum;
            end;
//18位身份证号码,前17位必须是数字,最后一位除数字外,可能是"X"
      18  : begin
              ToProcess := IsDigiStr(Copy(IDNum, 1, 17)) and (IsDigiChar(IDNum[18]) or (IDNum[18] in ['x', 'X']));
              IDNumInfo.IDNumType := idnum18;
            end;
    end;

if ToProcess then
    begin
//取区划代码
      IDNumInfo.RegionCode := Copy(IDNum, 1, 6);
      
//取生日信息
      case IDNumInfo.IDNumType of
        idnum15 : begin
                    Year  := strtoint('19' + Copy(IDNum, 7, 2));
                    Month := strtoint(Copy(IDNum, 9, 2));
                    Day   := strtoint(Copy(IDNum, 11, 2));
                  end;
        idnum18 : begin
                    Year  := strtoint(Copy(IDNum, 7, 4));
                    Month := strtoint(Copy(IDNum, 11, 2));
                    Day   := strtoint(Copy(IDNum, 13, 2));
                  end;
        else
        begin
          Year  := 0;
          Month := 0;
          Day   := 0;
        end;
      end;
//尝试将生日信息转换成日期
      try
        IDNumInfo.BirthDay := EncodeDate(Year, Month, Day);
      except
        ToProcess := false;
      end;

if ToProcess then
      begin
//取性别
        SexMark := '1';
//取性别标志      
        case IDNumInfo.IDNumType of
          idnum15 : SexMark := IDNum[15];
          idnum18 : SexMark := IDNum[17];
        end;
//判断性别
        case strtoint(SexMark) mod 2 of
          0 : IDNumInfo.Sex := sexFemale;
          1 : IDNumInfo.Sex := sexMale;
        end;

//如果是18位的身份证,判断校验是否正确
        if IDNumInfo.IDNumType = idnum18 then
        begin
          if CompareText(GenIDNumCheckSumChar(IDNum), IDNum[18]) = 0 then
          begin
            IDNumInfo.ChecksumRst := csChecksumCorrect;
            Result := true;
          end;
        end else
          Result := true;
      end;
    end;
  end;
end;

//判断身份证号码是否正确,通过从中取信息是否成功来判定。
function IsIDNumValid(IDNum: string): boolean;
var
  TmpIDNumInfo : TIDNumInfo;
begin
  Result := GetIDNumInfo(IDNum, TmpIDNumInfo);
end;

//将区划代码转换成名称
function RegionCode2Str(RegionCode: string; RegionCodeArray: TRegionCodeTransArray): string;

//在列表中搜索指定的区划代码所对应的名称
//用折半查找,所以要求输入的对应关系必须是有序的
  function SearchRegionStr(SearchRegionCode: string): string;
  var
    SearchFrom,
    SearchTo,
    CheckIdx,
    SearchValue,
    CheckValue   : integer;
  begin
    Result := '';
    
    SearchFrom   := low(RegionCodeArray);
    SearchTo     := high(RegionCodeArray);
    SearchValue  := StrToInt(SearchRegionCode);
    while SearchFrom <= SearchTo do
    begin
      CheckIdx := (SearchFrom + SearchTo) div 2;
      CheckValue := StrToInt(RegionCodeArray[CheckIdx].RegionCode);

if CheckValue = SearchValue then
      begin
        Result := RegionCodeArray[CheckIdx].RegionStr;
        Break;
      end else
        if CheckValue > SearchValue then
        begin
          SearchTo := CheckIdx - 1;
        end else
          if CheckValue < SearchValue then
          begin
            SearchFrom := CheckIdx + 1;
          end;
    end;
  end;

var
  SepRegionCode : TSepRegionCode;
  TmpStr,
  SearchRst : string;
begin
  Result := '';
//区划代码为6位
  if (length(RegionCode) = 6) and IsDigiStr(RegionCode) and (length(RegionCodeArray) > 0) then
  begin
//分解区划代码
    SepRegionCode.ProvinceCode := Copy(RegionCode, 1, 2);
    SepRegionCode.CityCode     := Copy(RegionCode, 3, 2);
    SepRegionCode.CountyCode   := Copy(RegionCode, 5, 2);

//省级的区划代码不包括11以下的组合
    if StrToInt(SepRegionCode.ProvinceCode) > 10 then
    begin
//列表内搜索省的名称
      TmpStr := '';
      SearchRst := SearchRegionStr(SepRegionCode.ProvinceCode + '0000');
      if length(SearchRst) > 0 then
      begin
        TmpStr := SearchRst;
//在列表内搜索市的名称
        SearchRst := SearchRegionStr(SepRegionCode.ProvinceCode + SepRegionCode.CityCode + '00');
        if length(SearchRst) > 0 then
        begin
          if not((SearchRst = '市辖区') or (SearchRst = '县')) then
            TmpStr := TmpStr + SearchRst;
//在列表内搜索县的名称
          SearchRst := SearchRegionStr(SepRegionCode.ProvinceCode + SepRegionCode.CityCode + SepRegionCode.CountyCode);
          if length(SearchRst) > 0 then
          begin
            if not((SearchRst = '市辖区') or (SearchRst = '县')) then
              TmpStr := TmpStr + SearchRst;
          end;
        end;
      end;

Result := TmpStr;
    end;

end;

end;

//取区划名称
function GetRegionStr(RegionCode: string; FromFileName: string): string;
var
  RegionCodeArray: TRegionCodeTransArray;
begin
  if LoadRegionDat(FromFileName, RegionCodeArray) then
    Result := RegionCode2Str(RegionCode, RegionCodeArray)
  else
    Result := '';
end;

//取区划名称
function GetRegionStr(RegionCode: string): string;
begin
  Result := RegionCode2Str(RegionCode, RegionCodeTransArray);
end;

//取区划名称
function GetRegionStr(var IDNumInfo: TIDNumInfo; FromFileName: string): boolean;
var
  RegionStr : string;
begin
  RegionStr := GetRegionStr(IDNumInfo.RegionCode, FromFileName);
  if not (RegionStr = '') then
  begin
    IDNumInfo.RegionStr := RegionStr;
    Result := true;
  end else
    Result := false;
end;

//取区划名称
function GetRegionStr(var IDNumInfo: TIDNumInfo): boolean;
var
  RegionStr : string;
begin
  RegionStr := GetRegionStr(IDNumInfo.RegionCode);
  if not (RegionStr = '') then
  begin
    IDNumInfo.RegionStr := RegionStr;
    Result := true;
  end else
    Result := false;
end;

//根据指定分隔符分解字符串,并存入数组
function  DecodeStr2Array(Source: string; SepChar: char; var Rst: TStrArray): integer;
var
  Count, CutFrom, Len: word;

procedure AddStr(Str: string);
  begin
    SetLength(Rst, length(Rst) + 1);
    Rst[high(Rst)] := Str;
  end;
begin
  Result := 0;
  SetLength(Rst, 0);
  len := length(Source);
  if len > 0 then
  begin
    CutFrom := 1;

for count := 1 to len do //逐字符扫描字符串,遇到分隔符时则切断串获得数据
      if (Source[count] = SepChar) then begin
        AddStr(Copy(Source, CutFrom, count - CutFrom));
        CutFrom := count + 1;
        if Count = Len then
          AddStr('');
      end else
        if count = len then
        begin
          AddStr(Copy(Source, CutFrom, count - CutFrom + 1));
          Break;
        end;

Result := length(Rst);
  end;
end;

//把区划代码和名称的对应关系从文件读到变量中
function LoadRegionDat(FromFileName: string; var RegionCodeArray: TRegionCodeTransArray): boolean;
var
  Count0 : integer;
  RegionDatFile : TStringList;
  StrArray      : TStrArray;
begin
  Result := false;
  SetLength(RegionCodeArray, 0);

if FileExists(FromFileName) then
  begin
    RegionDatFile := TStringList.Create;
//读入文件
    try
      RegionDatFile.LoadFromFile(FromFileName);
    except

end;
//处理数据
    for Count0 := 0 to RegionDatFile.Count - 1 do
//跳过空行
      if length(RegionDatFile[Count0]) > 0 then
      begin
//跳过以#开头的行(注释行)
        if not (RegionDatFile[Count0][1] = '#') then
        begin
//分解数据
          if (DecodeStr2Array(RegionDatFile[Count0], ',', StrArray) = 2) and   //以,分隔的两个字段
            (length(StrArray[0]) = 6) and                                      //代码长度为6个字符
            IsDigiStr(StrArray[0]) and                                         //代码必须全部是数字
            (length(StrArray[1]) > 0) then                                     //名称长度必须大于1
          begin
//增加内容
            SetLength(RegionCodeArray, length(RegionCodeArray) + 1);
            RegionCodeArray[high(RegionCodeArray)].RegionCode := StrArray[0];
            RegionCodeArray[high(RegionCodeArray)].RegionStr  := StrArray[1];
          end;
        end;
      end;

Result := length(RegionCodeArray) > 0;

RegionDatFile.Free;
  end;
end;

//直接将区划代码信息读入到本单元的公用变量里
function LoadRegionDat(FromFileName: string): boolean;
begin
  Result := LoadRegionDat(FromFileName, RegionCodeTransArray);
end;

initialization
  SetLength(RegionCodeTransArray, 0);

end.

delphi 身份证号码相关处理单元相关推荐

  1. 在excel表格中,根据身份证号码就可以自动提取出生年月、性别、年龄。

    在excel表格中,根据身份证号码就可以自动提取出生年月.性别.年龄. 第一,身份证号码的组成. 当今的身份证号码由18位数字组成:前6位为地址码,第7至14位为出生日期码,第15至17位为顺序码,第 ...

  2. 用EXCEL提取身份证号码中的生日

    自动录入出生日期       (1)函数分解 CONCATENATE函数将几个文本字符串合并为一个文本字符串. 语法:CONCATENATE(text1,text2,...) Text1,text2, ...

  3. oracle 导出身份证号_ORACLE对身份证号码处理相关的SQL【收藏】

    /*ORACLE对身份证号码处理相关的SQL汇总 身份证号码算法及应用场景: 工作实践总结,与大家分享快乐,并请高人批评指正,努力改进: 目前我国大量存在着正在有效期的15位身份证,虽然国家在推行二代 ...

  4. Java使用百度AI实现识别身份证照片信息,根据身份证号码,获取相关个人信息

    Java使用百度AI实现识别身份证照片信息 百度智能云-登录 1.登录百度智能云,选择文字识别,创建相关信息 2.获取APP_ID.API_KEY.SECRET_KEY 核心处理代码 import c ...

  5. oracle 导出身份证号_ORACLE对身份证号码处理相关的SQL汇总

    目前我国大量存在着正在有效期的15位身份证,虽然国家在推行二代身份证,但尚未发现强行要求全国人民更换未到期的15位身份证的官方声明或公告. 扯远了:),总之合法的15位身份证号码将在今后一段时间内继续 ...

  6. ORACLE对身份证号码处理相关的SQL汇总

    目前我国大量存在着正在有效期的15位身份证,虽然国家在推行二代身份证,但尚未发现强行要求全国人民更换未到期的15位身份证的官方声明或公告.           扯远了:),总之合法的15位身份证号码将 ...

  7. iOS身份证号码识别

    最近不少简友说git上下载下来的代码报各种问题,因为包含的库都比较大,所以大家在pod的时候耐心等待,另外我已经将代码适配到了iOS10. 一.前言   身份证识别,又称OCR技术.OCR技术是光学字 ...

  8. Excel告诉你身份证号码里藏着de秘密

    1.出生日期 每个人都出生日期即为身份证号码中的8位数字,日期需要利用DATE函数. 这8组Excel函数,帮您解决工作中80%的难题文章中已经详细介绍了字符串截取的三个函数,提取日期用到的就是从中间 ...

  9. mysql验证身份证号正确_通过SQL校验身份证号码是否正确

    根据提供的身份证号码信息验证身份证号码是否符合二代身份证规范,其中区域编码网上可下载. 使用数据库为DB2,但目测可以通用身份证号码第18位验证算法从网上查得,具体验证算法如下: 1.将前面的身份证号 ...

  10. Java 身份证号码识别系统

    最近发现一个有趣的项目. 这个项目是通过学习https://gitee.com/nbsl/idCardCv 后整合 tess4j,不需要经过训练直接使用的,当然,你也可以进行训练后进行使用. 该项目修 ...

最新文章

  1. Python学习教程(Python学习视频_Python学些路线):Day05 总结和练习
  2. 如何彻底解决安装Windows漏洞补丁出现蓝屏或无法启动问题?
  3. Which one is faster: Java heap or native memory?
  4. 两个列表合并去重_把两个pdf合并成一个如何解决?
  5. centos7 mysql 1064_【mysql报错】1064 - You have an error in your SQL syntax;
  6. mysql8.0日期类型_Mysql学习-数据类型(日期时间类型)
  7. 如何在iPhone和iPad上允许“不受信任的快捷方式”
  8. cesium js 路径_[CesiumJS]Cesium入门3 – Cesium目录框架结构
  9. Wireshark实战分析值DNS协议(二)
  10. Java 将PDF转为OFD
  11. linux软件源哪个好,Linux 软件源
  12. Android--使用开源vitamio做万能视频播放器
  13. 【前端】【HTML+CSS+JavaScript(JS)】简易工资计算器的实现
  14. android 铃音制作工具,音乐剪辑铃声制作
  15. python偏最小二乘法公式,python3 偏最小二乘法实现
  16. FlinkWindow和水印
  17. 计算机系统期末考试感想
  18. Jquery实现弹幕效果
  19. 深富策略:锂电光伏崛起 成长赛道再度回归
  20. 索尼机型刷机不用愁 带你玩转一键解锁

热门文章

  1. 如何调整金格电子章服务器印章_【​金格统一电子印章平台V2.0】版本正式发布!...
  2. 业务人员不知道如何提出 BI 需求,老板不重视 BI 项目怎么办?
  3. JMeter接口测试及接口登陆压力测试
  4. PHP多功能自动发卡平台源码带手机版 带多套商户模板
  5. Vue开发环境搭建详解
  6. python快捷键设置,环境设置、输出print、转义字符、标识符
  7. java bi报表工具_7款顶级开源BI(商务智能)软件和报表工具
  8. 从ResNet101到ResNet50
  9. GSCOOLINK GSV2006替CH6002 HDMI2.0接口芯片
  10. 1-junos基本操作