用Dart写的身份证号校验代码(可用于flutter项目)

贴一份用dart写的身份证号校验代码,直接复制使用。需要手动引入国际化库intl

import 'package:intl/intl.dart';
import 'age_util.dart' as ageUtil;

///@author: wangzhanhong
///纯数字正则匹配式
RegExp _pureDigital = RegExp('[0-9]*', multiLine: false);

var _formatter = DateFormat("yyMMdd");

///初步匹配年月日字段
RegExp _dateRegExp = RegExp(
    '^((\d{2}(([02468][048])|([13579][26]))[\-\/\s]?((((0?[13578])|(1[02]))[\-\/\s]?((0?[1-9])|([1-2][0-9])|(3[01])))|(((0?[469])|(11))[\-\/\s]?((0?[1-9])|([1-2][0-9])|(30)))|(0?2[\-\/\s]?((0?[1-9])|([1-2][0-9])))))|(\d{2}(([02468][1235679])|([13579][01345789]))[\-\/\s]?((((0?[13578])|(1[02]))[\-\/\s]?((0?[1-9])|([1-2][0-9])|(3[01])))|(((0?[469])|(11))[\-\/\s]?((0?[1-9])|([1-2][0-9])|(30)))|(0?2[\-\/\s]?((0?[1-9])|(1[0-9])|(2[0-8]))))))(\s(((0?[0-9])|([1-2][0-3]))\:([0-5]?[0-9])((\s)|(\:([0-5]?[0-9])))))?\$',
    multiLine: false);

const List<int> _power = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];

int _currentYear = int.parse(DateFormat("yyyy").format(DateTime.now()));

Map<String, String> _areaCode;

const List<String> _ValCodeArr = [
  "1",
  "0",
  "x",
  "9",
  "8",
  "7",
  "6",
  "5",
  "4",
  "3",
  "2"
];
const List<String> _Wi = [
  "7",
  "9",
  "10",
  "5",
  "8",
  "4",
  "2",
  "1",
  "6",
  "3",
  "7",
  "9",
  "10",
  "5",
  "8",
  "4",
  "2"
];

const int _YEAR_DELTA = 150;

bool isIdCard(String idCard) {
  if (idCard == null || idCard.isEmpty) {
    print('身份证不能为空');
    return false;
  } else if (!(idCard.length == 15 || idCard.length == 18)) {
    print('身份证必须是15位或者18位');
    return false;
  }
  if (idCard.length == 15 && ((idCard = wrap15To18(idCard)) == null)) {
    return false;
  }
  String ai = "";
  String aiCache = ""; //大写X
  if (idCard.length == 18) {
    ai = idCard.substring(0, 17);
  } else if (idCard.length == 15) {
    ai = idCard.substring(0, 6) + "19" + idCard.substring(6, 15);
  }
  if (!_isDigital(ai)) {
    print('身份证处最后一位必须是纯数字');
    return false;
  }
  String strYear = ai.substring(6, 10); // 年份
  String strMonth = ai.substring(10, 12); // 月份
  String strDay = ai.substring(12, 14); // 月份
  if (_dateRegExp.hasMatch(strYear + "-" + strMonth + "-" + strDay)) {
    print('出生年月日无效');
    return false;
  }
  int yearDelta = int.parse(strYear) - _currentYear;
  //身份证上的年份与当前系统年份差值太大
  //根据需要自定义修改该值
  if (yearDelta.abs() > _YEAR_DELTA) {
    print('年份不对,距离当前年份差量过大');
    return false;
  }
  if (int.parse(strMonth) > 12 || int.parse(strMonth) <= 0) {
    print('月份不对');
    return false;
  }
  if (int.parse(strDay) > 31 || int.parse(strDay) <= 0) {
    print('日期不对');
    return false;
  }
  if (!_getAreaCode().containsKey(ai.substring(0, 2))) {
    print('地区编码错误');
    return false;
  }
  int totalMulAiWi = 0;
  for (int i = 0; i < 17; i++) {
    totalMulAiWi =
        totalMulAiWi + int.parse(ai.substring(i, i + 1)) * int.parse(_Wi[i]);
  }
  int modValue = totalMulAiWi % 11;
  String strVerifyCode = _ValCodeArr[modValue];
  //兼容大写字母X
  if ("x" == strVerifyCode) {
    aiCache = ai + "X";
  }
  ai = ai + strVerifyCode;
  if (idCard.length == 18) {
    if (!(ai == idCard) && !(aiCache == idCard)) {
      return false;
    }
  } else {
    return true;
  }
  return true;
}

///返回'男'或者'女',请验证身份证号合法之后再使用该函数,
///该函数不会额外去验证输入是否正确
String getGender(String idCard) {
  if (idCard.length == 15 && (idCard = wrap15To18(idCard)) == null) {
    return null;
  }
  String genderStr = idCard.substring(16, 17);
  return (int.parse(genderStr) % 2 != 0) ? '男' : '女';
}

///返回出生年月日,请验证身份证号合法之后再使用该函数,
///该函数不会额外去验证输入是否正确
String getBirthday(String idCard) {
  if (idCard.length == 15 && (idCard = wrap15To18(idCard)) == null) {
    return null;
  }
  return idCard.substring(6, 17);
}

///返回输入身份证的用户周岁,请验证身份证号合法之后再使用该函数,
///该函数不会额外去验证输入是否正确
///计算有误返回-1
int getAge(String idCard) {
  String dob = getBirthday(idCard);
  if (dob == null) {
    return -1;
  }
  return ageUtil.getAgeFromBirthday(dob);
}

Map<String, String> _getAreaCode() {
  if (_areaCode == null) {
    _areaCode = Map();
    _areaCode['11'] = '北京';
    _areaCode['12'] = '天津';
    _areaCode['13'] = '河北';
    _areaCode['14'] = '山西';
    _areaCode['15'] = '内蒙古';
    _areaCode['21'] = '辽宁';
    _areaCode['22'] = '吉林';
    _areaCode['23'] = '黑龙江';
    _areaCode['31'] = '上海';
    _areaCode['32'] = '江苏';
    _areaCode['33'] = '浙江';
    _areaCode['34'] = '安徽';
    _areaCode['35'] = '福建';
    _areaCode['36'] = '江西';
    _areaCode['37'] = '山东';
    _areaCode['42'] = '湖北';
    _areaCode['43'] = '湖南';
    _areaCode['45'] = '广西';
    _areaCode['44'] = '广东';
    _areaCode['46'] = '海南';
    _areaCode['50'] = '重庆';
    _areaCode['51'] = '四川';
    _areaCode['52'] = '贵州';
    _areaCode['53'] = '云南';
    _areaCode['54'] = '西藏';
    _areaCode['61'] = '陕西';
    _areaCode['62'] = '甘肃';
    _areaCode['63'] = '青海';
    _areaCode['64'] = '宁夏';
    _areaCode['65'] = '新疆';
    _areaCode['71'] = '台湾';
    _areaCode['81'] = '香港';
    _areaCode['82'] = '澳门';
    _areaCode['91'] = '国外';
  }
  return _areaCode;
}

String wrap15To18(String idCard) {
  String idCard17;
  if (idCard == null || idCard.length != 15) {
    return null;
  }
  if (_isDigital(idCard)) {
    String birthday = idCard.substring(6, 12);
    DateTime birthDate;
    try {
      birthDate = _formatter.parse(birthday);
    } on Exception catch (e) {
      print(e.toString());
      return null;
    }
    idCard17 =
        "${idCard.substring(0, 6)}${birthDate.year}${idCard17.substring(8)}";
    var bit = _coverCharToInt(idCard.split(''));
    int sum17 = _getPowerSum(bit);
    String checkCode = _getCheckCodeBySum(sum17);
    // 获取不到校验位
    if (null == checkCode) {
      return null;
    }
    // 将前17位与第18位校验码拼接
    idCard17 += checkCode;
  } else {
    return null;
  }
  return idCard17;
}

int _getPowerSum(List<int> bit) {
  int sum = 0;
  if (_power.length != bit.length) {
    return sum;
  }
  for (int i = 0; i < bit.length; i++) {
    for (int j = 0; j < _power.length; j++) {
      if (i == j) {
        sum = sum + bit[i] * _power[j];
      }
    }
  }
  return sum;
}

List<int> _coverCharToInt(List<String> c) {
  var a = List();
  int k = 0;
  c.forEach((str) {
    a[k++] = str as int;
  });
  return a;
}

bool _isDigital(String idCard) {
  return _pureDigital.hasMatch(idCard);
}

String _getCheckCodeBySum(int sum17) {
  String checkCode;
  switch (sum17 % 11) {
    case 10:
      checkCode = "2";
      break;
    case 9:
      checkCode = "3";
      break;
    case 8:
      checkCode = "4";
      break;
    case 7:
      checkCode = "5";
      break;
    case 6:
      checkCode = "6";
      break;
    case 5:
      checkCode = "7";
      break;
    case 4:
      checkCode = "8";
      break;
    case 3:
      checkCode = "9";
      break;
    case 2:
      checkCode = "x";
      break;
    case 1:
      checkCode = "0";
      break;
    case 0:
      checkCode = "1";
      break;
  }
  return checkCode;
}

age_util代码如下

//该文件提供根据年月日计算岁数的功能函数,计算得出的函数是周岁,不满一周岁的年月会被忽略
//@author: wangzhanhong

///输入格式19990131
int getAgeFromBirthday(String dob) {
  int selectYear = int.parse(dob.substring(0, 4));
  int selectMonth = int.parse(dob.substring(4, 6));
  int selectDay = int.parse(dob.substring(6, 8));
  DateTime now = DateTime.now();
  int yearNow = now.year;
  int monthNow = now.month;
  int dayNow = now.day;
  // 用当前年减去出生年,例如2019-1-22 ,出生年日月是2011年-6-1
  //当前值是8
  int yearMinus = yearNow - selectYear;
  //当前值是-5,monthMinus大于0的情况下,证明满8周岁。等于0需要判断dayMinus,小于0的时候age-1
  int monthMinus = monthNow - selectMonth;
  //当前值是21,monthMinus大于0的情况下,配合monthMinus等于0的情况下,age等于原值。否则age等于age-1
  int dayMinus = dayNow - selectDay;
  // 先大致赋值
  int age = yearMinus;
  if (age <= 0) {
    return 0;
  }
  if (monthMinus == 0) {
    if (dayMinus <= 0) {
      age = age - 1;
    }
  } else if (monthMinus < 0) {
    age = age - 1;
  }
  return age;
}

///输入格式1999-03-05
int getAgeFromBirthdayWithSep(String dob) {
  List<String> list = dob.split("-");
  return getAgeFromBirthday("${list[0]}${list[1]}${list[2]}");
}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,039评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,426评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,417评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,868评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,892评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,692评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,416评论 3 419
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,326评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,782评论 1 316
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,957评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,102评论 1 350
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,790评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,442评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,996评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,113评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,332评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,044评论 2 355

推荐阅读更多精彩内容