閱讀321 返回首頁    go 阿裏雲 go 技術社區[雲棲]


MaxCompute UDF係列之身份證校驗及15位身份證號碼轉換成18位

為了驗證一些老證件上的身份證號碼到底是不是本人,今天為大家提供一個的UDF,下載地址見附件。


效果如下:


157557965088220a4deeeba63128244ee8d3ddcd


f99feca803e62e158a9cf209f787bd8d3e46dc5e


MaxCompute UDF代碼如下:


/*** 
 * 身份證號碼構成:6位地址編碼+8位生日+3位順序碼+1位校驗碼 
 * 驗證15位,18位證件號碼是否有效;15位號碼將直接轉為18位;
 * 校驗身份證號碼除了校驗位是否為數值,校驗省份、出生日期
 * 校驗位不正確的會被正確的替代
 * 出生日期邏輯有效性,即是否1900年前出生,是否當前日期後出生未校驗
 * 1984年4月6日國務院發布《中華人民共和國居民身份證試行條例》,並且開始頒發第一代居民身份證,按壽命120歲驗證,年齡區間是1864年至今。
 * 15位證件號碼轉換未考慮1900年前出生的人
 ***/
package com.yinlin.udf.dev;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import java.text.SimpleDateFormat;
import java.util.Date;
import com.aliyun.odps.udf.UDF;

public final class IdenNum extends UDF {
  private final static int NEW_CARD_NUMBER_LENGTH = 18;
  private final static int OLD_CARD_NUMBER_LENGTH = 15;
  private final static int YEAR_BEGIN = 1864;
  private final static char[] VERIFY_CODE = { '1', '0', 'X', '9', '8', '7','6', '5', '4', '3', '2' }; // 18位身份證中最後一位校驗碼
  private final static int[] VERIFY_CODE_WEIGHT = { 7, 9, 10, 5, 8, 4, 2, 1,6, 3, 7, 9, 10, 5, 8, 4, 2 };// 18位身份證中,各個數字的生成校驗碼時的權值
  private final static int[] PROVINCE_CODE = { 11, 12, 13, 14, 15, 21, 22,23, 31, 32, 33, 34, 35, 36, 37, 41, 42, 43, 44, 45, 46, 50, 51, 52,53, 54, 61, 62, 63, 64, 65, 71, 81, 82, 91 };// 省份編碼
  private final static Pattern pattern = Pattern.compile("[0-9]*");
  private final static ThreadLocal<SimpleDateFormat> threadLocal = new ThreadLocal<SimpleDateFormat>() {
    protected SimpleDateFormat initialValue() {
      return null;// 直接返回null
    }
  };

  public String evaluate(String cardNumber) {
    if (cardNumber == null) {return null;}// 為NULL
    Date dNow = new Date( );
    SimpleDateFormat ft = new SimpleDateFormat ("yyyy");
    int YEAR_END = Integer.parseInt(ft.format(dNow));
    cardNumber = cardNumber.trim();
    if (cardNumber.length() == 0) {return null;}// 空字符串
    if (NEW_CARD_NUMBER_LENGTH != cardNumber.length() &
        OLD_CARD_NUMBER_LENGTH != cardNumber.length()) {return null;}// 長度不正確
    if (NEW_CARD_NUMBER_LENGTH == cardNumber.length()) {
      if (isNumeric(cardNumber.substring(0, 17)) == false) {return null;} // 前17位為數值
      if (IdenNum.calculateVerifyCode(cardNumber) != cardNumber.charAt(17)) {
         cardNumber = contertToNewCardNumber(cardNumber,NEW_CARD_NUMBER_LENGTH);
      }// 校驗位修正
      if (isProvince(cardNumber.substring(0, 2)) == false) {return null;}// 省份編碼在已知集合中
      if (isDate(cardNumber.substring(6, 14)) == false) {return null;}// 非法日期轉換前後不一致
      int birthdayYear = Integer.parseInt(cardNumber.substring(6, 10));
      if (birthdayYear < YEAR_BEGIN) {return null;}// 為NULL
      if (birthdayYear > YEAR_END) {return null;}// 為NULL
    }
    if (OLD_CARD_NUMBER_LENGTH == cardNumber.length()) {
      if (isNumeric(cardNumber) == false) {return null;} // 前15位為數值
      if (isProvince(cardNumber.substring(0, 2)) == false) {return null;}// 省份編碼在已知集合中
      if (isDate("19" + cardNumber.substring(6, 12)) == false) {return null;}// 非法日期轉換前後不一致
      cardNumber = contertToNewCardNumber(cardNumber,OLD_CARD_NUMBER_LENGTH);
    }
    return cardNumber;
  }

  /**
   * 校驗碼(第十八位數):
   * 
   * 十七位數字本體碼加權求和公式 S = Sum(Ai * Wi), i = 0...16 ,先對前17位數字的權求和;
   * Ai:表示第i位置上的身份證號碼數字值 Wi:表示第i位置上的加權因子 Wi: 7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4
   * 2; 計算模 Y = mod(S, 11) 通過模得到對應的校驗碼 Y: 0 1 2 3 4 5 6 7 8 9 10 校驗碼: 1 0 X 9
   * 8 7 6 5 4 3 2
   * 
   * @param cardNumber
   * @return
   */
  private static char calculateVerifyCode(CharSequence cardNumber) {
    int sum = 0;
    for (int i = 0; i < NEW_CARD_NUMBER_LENGTH - 1; i++) {
      char ch = cardNumber.charAt(i);
      sum += ((int) (ch - '0')) * VERIFY_CODE_WEIGHT[i];
    }
    return VERIFY_CODE[sum % 11];
  }

  /**
   * A-把15位身份證號碼轉換到18位身份證號碼 15位身份證號碼與18位身份證號碼的區別為:
   * 1、15位身份證號碼中,"出生年份"字段是2位,轉換時需要補入"19",表示20世紀
   * 2、15位身份證無最後一位校驗碼。18位身份證中,校驗碼根據根據前17位生成
   * B-18位身份證號碼校驗位修複
   * @param cardNumber
   * @return
   */
  private static String contertToNewCardNumber(String CardNumber,int cardNumberLength) {
    StringBuilder buf = new StringBuilder(NEW_CARD_NUMBER_LENGTH);
    if       (cardNumberLength == NEW_CARD_NUMBER_LENGTH){
      buf.append(CardNumber.substring(0, 17));
      buf.append(IdenNum.calculateVerifyCode(buf));
    }else if (cardNumberLength == OLD_CARD_NUMBER_LENGTH){
      buf.append(CardNumber.substring(0, 6));
      buf.append("19");
      buf.append(CardNumber.substring(6));
      buf.append(IdenNum.calculateVerifyCode(buf));
    }
    return buf.toString();
  }

  /**
   * 功能:判斷字符串是否為數字
   * 
   * @param str
   * @return
   */
  private static boolean isNumeric(String str) {
    Matcher isNum = pattern.matcher(str);
    if (isNum.matches()) {
      return true;
    } else {
      return false;
    }
  }

  /**
   * 功能:判斷前兩位是否正確
   * 
   * @param Province
   * @return
   */
  private static boolean isProvince(String Province) {
    int prov = Integer.parseInt(Province);
    for (int i = 0; i < PROVINCE_CODE.length; i++) {
      if (PROVINCE_CODE[i] == prov) {
        return true;
      }
    }
    return false;
  }

  public static SimpleDateFormat getDateFormat() {
    SimpleDateFormat df = (SimpleDateFormat) threadLocal.get();
    if (df == null) {
      df = new SimpleDateFormat("yyyyMMdd");
      df.setLenient(false);
      threadLocal.set(df);
    }
    return df;
  }

  /**
   * 功能:判斷出生日期字段是否為日期
   * 
   * @param str
   * @return
   */
  private static boolean isDate(String birthDate) {
    try {
      SimpleDateFormat sdf = getDateFormat();
      Date cacheBirthDate = sdf.parse(birthDate);
      if (sdf.format(cacheBirthDate).equals(birthDate) == false) {return false;}// 非法日期轉換前後不一致
    } catch (Exception e) {
      return false;
    }
    return true;
  }

}


使用方法:

1、通過Eclipse將IdenNum.java編譯成Jar包,命名為IdenNum_udf.jar。


2、通過大數據開發套件,上傳資源IdenNum_udf.jar;

05c21f05512ad89ba68680a521ef075bce4f6da5



3、通過大數據開發套件,引用第二步上傳的IdenNum_udf.jar資源,注冊idennum函數;

386fa1c44ffc501c64d0633a0dc35380cd322396

4、通過大數據開發套件新建SQL腳本,輸入SQL函數進行測試。


--創建虛擬表
CREATE TABLE IF NOT EXISTS dual (id STRING);
insert into table dual select '1' from (select count(1) from dual) t;

---idennum身份證有效性識別
select idennum('1101057109235829XX') from yinlin_demo.dual limit 1;

---idennum身份證15位轉換18位
select idennum('110105710923582') from yinlin_demo.dual limit 1;


到此為止,實驗完成。


常見問題

Q:無結果?

我們在創建虛擬dual表的時候,一定要保證表中有數據。


Q:無法找到函數?

利用大數據開發套件注冊函數的時候需要包名+類名,否則會找不到類。



阿裏巴巴大數據-玩家社區 https://yq.aliyun.com/teams/6/

---阿裏大數據博文,問答,社群,實踐,有朋自遠方來,不亦說乎……

bba01b493e1c5d904e882b1c380673c6ebe49a98

最後更新:2017-08-13 22:22:26

  上一篇:go  智迪科技攜手海通安恒,啟動SAP實施項目
  下一篇:go  雲服務器 ECS 數據恢複:誤刪文件後如何恢複數據