mathematica 发表于 2019-4-19 13:26:29

谁能用mathematica写个校验身份证的子函数?

(*身份证检验码*)
Clear["Global`*"];(*Clear all variables*)
(*没考虑身份证号码最后一位x的情况*)
fun:=Module[{ids,dot},
    jym={7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2};
    ids=IntegerDigits;
    dot=Dot],jym];(*前17位与校验码求内积*)
    dot=Mod;(*求余数*)
    dot=Mod;(*余数n,对应的最后一位编码是12-x*)
    If],Return,Return]
]


我写的这个未能考虑最后一位是X的情况,
最后一位是X,应该如何考虑呢?

markfang2050 发表于 2019-4-19 15:13:56

这个容易,嵌套个特例啊

mathematica 发表于 2019-4-26 11:39:56

markfang2050 发表于 2019-4-19 15:13
这个容易,嵌套个特例啊

我自己用perl搞定了

markfang2050 发表于 2019-4-26 22:05:47

mathematica 发表于 2019-4-26 11:39
我自己用perl搞定了

代码贴出啊,。C就可以。

nyy 发表于 2025-2-28 18:58:17

(*
函数名称:IDCardValidate
函数功能:对输入的18位身份证号码进行合法性校验
输入参数:
    id_String:表示输入的身份证号码,为字符串类型
返回值:
    如果身份证号码合法,返回True;否则返回False
*)
IDCardValidate :=
Module[
{
   idList,   (* 用于存储身份证号码前17位转换后的数字列表 *)
   weights,    (* 身份证号码校验的加权因子列表 *)
   sum,      (* 用于存储前17位数字与加权因子乘积的总和 *)
   checkDigits,(* 对应不同余数的正确校验码列表 *)
   remainder,(* 总和对11取模后的余数 *)
   expectedCheckDigit, (* 计算得出的预期校验码 *)
   inputCheckDigit      (* 输入身份证号码的最后一位校验码 *)
},
(* 检查输入的身份证号码长度是否为18位,如果不是,直接返回False *)
If != 18, Return];
(* 将身份证号码的前17位转换为数字列表 *)
idList = IntegerDigits]];
(* 定义身份证号码校验的加权因子列表 *)
weights = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2};
(* 计算前17位数字与加权因子乘积的总和 *)
sum = Total];
(* 计算总和对11取模后的余数 *)
remainder = Mod;
(* 定义对应不同余数的正确校验码列表 *)
checkDigits = {"1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2"};
(* 根据余数获取预期的校验码 *)
expectedCheckDigit = checkDigits[];
(* 获取输入身份证号码的最后一位校验码 *)
inputCheckDigit = StringTake;
(* 如果输入的校验码是"X"或"x",统一转换为"X" *)
If,
   inputCheckDigit = "X"
];
(* 比较预期校验码和输入校验码是否一致,一致则返回True,否则返回False *)
expectedCheckDigit == inputCheckDigit
]

这是人工智能写的代码
看起来很不错
可读性非常强,还有详细注释与详细说明

nyy 发表于 2025-2-28 21:13:19

nyy 发表于 2025-2-28 18:58
这是人工智能写的代码
看起来很不错
可读性非常强,还有详细注释与详细说明


" 函数功能:此函数用于校验一个18位的身份证号码是否合法。
" 依据中华人民共和国国家标准GB 11643-1999中规定的身份证号码编码规则进行校验。
" 输入参数:
"   a:idcard - 字符串类型,代表需要校验的身份证号码,必须为18位。
" 返回参数:
"   1 - 如果身份证号码合法,符合编码规则。
"   0 - 如果身份证号码不合法,例如长度不为18位、字符不符合要求、校验码不正确等情况。
function! ValidateIDCard(idcard) abort
    " 检查身份证号码的长度是否为18位,如果不是则直接返回0,表示不合法
    if len(a:idcard) != 18
      return 0
    endif
    " 定义加权因子数组,用于计算身份证号码前17位的加权和
    let factor =
    " 定义校验码数组,对应加权和取模11后的正确校验码
    let parity = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2']
    " 初始化加权和为0
    let sum = 0
    " 遍历身份证号码的前17位
    for i in range(0, 16)
      " 获取身份证号码当前位置的字符
      let cur_char = a:idcard
      " 将当前字符转换为数字(如果不是数字则返回-1)
      let digit = str2nr(cur_char)
      " 如果转换失败(不是数字字符),则返回0,表示不合法
      if digit == -1
            return 0
      endif
      " 将当前数字乘以对应的加权因子,并累加到加权和中
      let sum += digit * factor
    endfor
    " 计算加权和对11取模的结果
    let remainder = sum % 11
    " 获取身份证号码的最后一位作为输入的校验码
    let check_bit = a:idcard
    " 将输入的校验码转换为大写(如果是X的情况),以便和parity数组比较
    if check_bit =~# 'x'
      let check_bit = 'X'
    endif
    " 比较输入的校验码和计算得到的正确校验码是否一致
    if check_bit == parity
      return 1
    else
      return 0
    endif
endfunction

nyy 发表于 2025-2-28 21:13:56

nyy 发表于 2025-2-28 21:13


" 测试用例 1:合法的身份证号码,最后一位是数字
let test_idcard1 = "110105194912310021"
let result1 = ValidateIDCard(test_idcard1)
echo "测试用例 1(合法,数字结尾):" . (result1 == 1? "通过" : "未通过")

" 测试用例 2:合法的身份证号码,最后一位是 X
let test_idcard2 = "11010519491231002X"
let result2 = ValidateIDCard(test_idcard2)
echo "测试用例 2(合法,X 结尾):" . (result2 == 1? "通过" : "未通过")

" 测试用例 3:不合法的身份证号码,长度不足 18 位
let test_idcard3 = "11010519491231002"
let result3 = ValidateIDCard(test_idcard3)
echo "测试用例 3(不合法,长度不足):" . (result3 == 0? "通过" : "未通过")

" 测试用例 4:不合法的身份证号码,包含非数字非 X 字符
let test_idcard4 = "11010519491231002A"
let result4 = ValidateIDCard(test_idcard4)
echo "测试用例 4(不合法,含非法字符):" . (result4 == 0? "通过" : "未通过")

" 测试用例 5:不合法的身份证号码,校验码不正确
let test_idcard5 = "110105194912310020"
let result5 = ValidateIDCard(test_idcard5)
echo "测试用例 5(不合法,校验码错误):" . (result5 == 0? "通过" : "未通过")

nyy 发表于 2025-3-3 19:35:01

nyy 发表于 2025-2-28 21:13


(*
函数功能:
该函数用于全面检验中国身份证号码的有效性,包括校验码是否正确、出生年份是否在合理范围内(1900至2100年)、
月份和日期是否符合实际情况。中国身份证号码由17位数字本体码和1位校验码组成。
校验码的计算方法是:将前17位数字分别乘以对应的加权因子(7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2),
然后将乘积相加,再对结果取模11,根据模值得到对应的校验码字符(模值为0对应X,模值为1对应1,以此类推),
最后与输入的身份证号码的最后一位进行比较,判断校验码是否正确。

函数用法:
IsValidIDCard,传入一个字符串类型的身份证号码,函数将返回一个布尔值,
表示该身份证号码是否有效。

输入参数:
- cardNumber:类型为字符串(String),意义是输入的中国身份证号码,长度应为18位。

返回值:
- 类型为布尔值(Boolean),意义是如果输入的身份证号码完全有效,返回True;否则返回False。

示例:
1. IsValidIDCard["11010519491231002X"],返回True,因为这是一个完全有效的身份证号码。
2. IsValidIDCard["11010521011231002X"],返回False,因为出生年份超出合理范围。
3. IsValidIDCard["11010519491331002X"],返回False,因为月份不符合实际。
4. IsValidIDCard["11010519491232002X"],返回False,因为日期不符合实际。
5. IsValidIDCard["110105194912310021"],返回False,因为这是一个校验码错误的身份证号码。
*)
IsValidIDCard :=
Module[{weights = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2},
      checkCodes = {"1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2"},
      sum, remainder, checkDigit, year, month, day, dateValid},
    If != 18,
      Return];
   
    (* 计算前17位数字与加权因子的乘积之和 *)
    sum = 0;
    For[i = 1, i <= 17, i++,
      digit = FromDigits];
      sum = sum + digit * weights[];
    ];
    remainder = Mod;
    checkDigit = StringTake;
   
    (* 提取出生年份、月份和日期 *)
    year = FromDigits];
    month = FromDigits];
    day = FromDigits];
   
    (* 初始化日期有效性为True,然后根据条件修改 *)
    dateValid = True;
    If;
    If;
    If[(month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12) && (day < 1 || day > 31), dateValid = False];
    If[(month == 4 || month == 6 || month == 9 || month == 11) && (day < 1 || day > 30), dateValid = False];
    If[month == 2,
      If[(Mod == 0 && Mod != 0) || Mod == 0,
            If[(day < 1 || day > 29), dateValid = False],
            If[(day < 1 || day > 28), dateValid = False]
      ]
    ];
   
    (* 综合校验码和日期有效性判断身份证是否有效 *)
    dateValid && checkDigit === checkCodes[]
];

(* 单元测试 *)
(* 测试用例1:完全有效的身份证号码 *)
Print["测试用例1:", IsValidIDCard["11010519491231002X"]];
(* 测试用例2:校验码错误的身份证号码 *)
Print["测试用例2:", IsValidIDCard["110105194912310021"]];
(* 测试用例3:出生年份小于1900的身份证号码 *)
Print["测试用例3:", IsValidIDCard["11010518991231002X"]];
(* 测试用例4:出生年份大于2100的身份证号码 *)
Print["测试用例4:", IsValidIDCard["11010521011231002X"]];
(* 测试用例5:月份小于1的身份证号码 *)
Print["测试用例5:", IsValidIDCard["11010519490031002X"]];
(* 测试用例6:月份大于12的身份证号码 *)
Print["测试用例6:", IsValidIDCard["11010519491331002X"]];
(* 测试用例7:31天月份中日期大于31的身份证号码 *)
Print["测试用例7:", IsValidIDCard["11010519490132002X"]];
(* 测试用例8:30天月份中日期大于30的身份证号码 *)
Print["测试用例8:", IsValidIDCard["11010519490431002X"]];
(* 测试用例9:闰年2月日期大于29的身份证号码 *)
Print["测试用例9:", IsValidIDCard["11010520000230002X"]];
(* 测试用例10:非闰年2月日期大于28的身份证号码 *)
Print["测试用例10:", IsValidIDCard["11010519010229002X"]];

nyy 发表于 2025-3-3 19:36:48

代码写的太好了。还生成测试用例。
还有注释有说明。
比人工写的好。

nyy 发表于 2025-3-5 13:31:06

本帖最后由 nyy 于 2025-3-5 13:41 编辑

nyy 发表于 2025-3-3 19:35


代码写的是好,但是有bug。
(*
函数功能:
该函数用于全面检验中国身份证号码的有效性,包括校验码是否正确、出生年份是否在合理范围内(1900至2100年)、
月份和日期是否符合实际情况。中国身份证号码由17位数字本体码和1位校验码组成。
校验码的计算方法是:将前17位数字分别乘以对应的加权因子(7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2),
然后将乘积相加,再对结果取模11,根据模值得到对应的校验码字符(模值为0对应X,模值为1对应1,以此类推),
最后与输入的身份证号码的最后一位进行比较,判断校验码是否正确。
函数用法:
IsValidIDCard,传入一个字符串类型的身份证号码,函数将返回一个布尔值,
表示该身份证号码是否有效。
输入参数:
- cardNumber:类型为字符串(String,或者18位整数),意义是输入的中国身份证号码,长度应为18位。
返回值:
- 类型为布尔值(Boolean),意义是如果输入的身份证号码完全有效,返回True;否则返回False。
示例:
1. IsValidIDCard["11010519491231002X"],返回True,因为这是一个完全有效的身份证号码。
2. IsValidIDCard["11010521011231002X"],返回False,因为出生年份超出合理范围。
3. IsValidIDCard["11010519491331002X"],返回False,因为月份不符合实际。
4. IsValidIDCard["11010519491232002X"],返回False,因为日期不符合实际。
5. IsValidIDCard["110105194912310021"],返回False,因为这是一个校验码错误的身份证号码。
*)
IsValidIDCard :=
Module[{weights = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2},(*前17位权重*)
      checkCodes = {"1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2"},(*0 1 2 3等余数所对应的校验码*)
      sum, remainder, checkDigit, year, month, day, dateValid},
    If,cardNumber=ToString];(*如果身份证号码是18位整数,则转化为字符串*)
    If != 18,
      Return];
    (* 计算前17位数字与加权因子的乘积之和 *)
    sum = 0;
    For[i = 1, i <= 17, i++,
      digit = FromDigits];(*此处有bug,原本是StringTake,表示提取前i个字符,而此处需要是第i个字符*)
      sum = sum + digit * weights[];
    ];
    remainder = Mod;(*求余数*)
    checkDigit = StringTake;(*提取最后一位校验码*)
    (* 提取出生年份、月份和日期 *)
    year = FromDigits];
    month = FromDigits];
    day = FromDigits];
    (* 初始化日期有效性为True,然后根据条件修改 *)
    dateValid = True;
    If;
    If;
    If[(month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12) && (day < 1 || day > 31), dateValid = False];
    If[(month == 4 || month == 6 || month == 9 || month == 11) && (day < 1 || day > 30), dateValid = False];
    If[month == 2,
      If[(Mod == 0 && Mod != 0) || Mod == 0,
            If[(day < 1 || day > 29), dateValid = False],
            If[(day < 1 || day > 28), dateValid = False]
      ]
    ];
    (* 综合校验码和日期有效性判断身份证是否有效 *)
    dateValid && (checkDigit === checkCodes[])
];

(* 单元测试 *)
(* 测试用例1:完全有效的身份证号码 *)
Print["测试用例1:", IsValidIDCard["11010519491231002X"]];
(* 测试用例2:校验码错误的身份证号码 *)
Print["测试用例2:", IsValidIDCard["110105194912310021"]];
(* 测试用例3:出生年份小于1900的身份证号码 *)
Print["测试用例3:", IsValidIDCard["11010518991231002X"]];
(* 测试用例4:出生年份大于2100的身份证号码 *)
Print["测试用例4:", IsValidIDCard["11010521011231002X"]];
(* 测试用例5:月份小于1的身份证号码 *)
Print["测试用例5:", IsValidIDCard["11010519490031002X"]];
(* 测试用例6:月份大于12的身份证号码 *)
Print["测试用例6:", IsValidIDCard["11010519491331002X"]];
(* 测试用例7:31天月份中日期大于31的身份证号码 *)
Print["测试用例7:", IsValidIDCard["11010519490132002X"]];
(* 测试用例8:30天月份中日期大于30的身份证号码 *)
Print["测试用例8:", IsValidIDCard["11010519490431002X"]];
(* 测试用例9:闰年2月日期大于29的身份证号码 *)
Print["测试用例9:", IsValidIDCard["11010520000230002X"]];
(* 测试用例10:非闰年2月日期大于28的身份证号码 *)
Print["测试用例10:", IsValidIDCard["11010519010229002X"]];


bug的原因,我已经写在代码里面了。
digit = FromDigits];
这个表示提取前i个字符
digit = FromDigits];
这个表示提取第i个字符,
看来人工智能真的不智能呀!
看起来是很正确的代码,结果还要我给你根除bug。
页: [1] 2
查看完整版本: 谁能用mathematica写个校验身份证的子函数?