涉及格式化输出、字符串、序列、字典、文件、函数等知识

1.获取性别

身份证上包含了公民身份号码、姓名、性别、住址等信息,公民身份号码里面还隐含出生日期、出生地、性别等信息。

  1. id_number = input()
  2. if id_number[16] in '02468': # 字符串更简洁,也避免转整数的运算
  3. # if int(id_number[16]) % 2 == 0: # 也可转整数再用取模运算判定奇偶
  4. gender = '女'
  5. else:
  6. gender = '男'
  7. print(f'你的性别为{gender}')
  1. id_number = input()
  2. # 利用条件表达式实现更简洁
  3. gender = '女' if id_number[16] in '02468' else '男'
  4. print(f'你的性别为{gender}')

输入
530126193303150409
输出
你的性别为女

  1. def judge_gender(id_number):
  2. if id_number[16] in '02468': # 字符串更简洁,也避免转整数的运算
  3. # if int(id_number[16]) % 2 == 0: # 也可转整数再用取模运算判定奇偶
  4. gender = '女'
  5. else:
  6. gender = '男'
  7. return f'你的性别为{gender}'
  8. if __name__ == '__main__':
  9. id_num = input()
  10. print(judge_gender(id_num))

2.获取出生日期

  1. id_number = input()
  2. year = id_number[6:10]
  3. month = id_number[10:12]
  4. date = id_number[12:14]
  5. print(f'你出生于{year}年{month}月{date}日')
  6. print(f'你出生于{year}年{int(month)}月{int(date)}日')

输入
530126193303150409
输出
你出生于1933年03月15日
你出生于1933年3月15日

  1. def get_date(id_number):
  2. year = id_number[6:10]
  3. month = id_number[10:12]
  4. date = id_number[12:14]
  5. return f'你出生于{year}年{month}月{date}日'
  6. # return f'你出生于{year}年{int(month)}月{int(date)}日'
  7. if __name__ == '__main__':
  8. id_num = input()
  9. print(get_date(id_num))

输出日期格式化
年月日改由用户输入可放到第1章教学中

  1. def get_date(id_number):
  2. year = id_number[6:10]
  3. month = id_number[10:12]
  4. date = id_number[12:14]
  5. print(year, month, date) # 年 月 日,2020 09 16
  6. print(year, month, date, sep='-') # 年-月-日,2020-09-16
  7. print(year, month, date, sep='/') # 年/月/日,2020/09/16
  8. print(month, date, year, sep=',') # 月,日,年,09,16,2020
  9. print('{}年{}月{}日'.format(year, month, date)) # 2020年09月16日
  10. print(year + '年' + month + '月' + date + '日') # 2020年09月16日
  11. print(f'你出生于{year}年{int(month)}月{int(date)}日')
  12. if __name__ == '__main__':
  13. id_num = input()
  14. get_date(id_num)

3.获取年龄

  1. import datetime
  2. id_number = input()
  3. age = datetime.datetime.now().year - int(id_number[6:10])
  4. print(f'你今年{age}周岁')

输入
530126193303150409
输出
你今年89周岁

  1. import datetime
  2. def get_age(id_number):
  3. age = datetime.datetime.now().year - int(id_number[6:10])
  4. return age
  5. if __name__ == '__main__':
  6. id_num = input()
  7. print(get_age(id_num))

4.身份证号升位

从1999年10月1日起,全国实行公民身份证号码制度,居民身份证编号由原15位升至18位。 居民身份证前6位为地址码;第七位至14位为出生日期码,此码由6位数改为8位数,其中年份用4位数表示;第15位至17位为顺序码,取消了顺序码中对百岁老人使用的特定编号。 第十八位为校验码,主要是为了校验计算机输入公民身份证号码的前17位数字是否正确,其取值范围是0至10,当值等于10时,用罗马数字符χ表示。
升位是1999年,所以这里假定所有人都是1900年-1999年之间出生
身份证号校验位的计算方法:
1、将前面的身份证号码17位数分别乘以不同的系数。第一位到第十七位的系数分别为:7、9、10、5、8、4、2、1、6、3、7、9、10、5、8、4、2 ;
2、将这17位数字和系数相乘的结果相加;
3、用加出来和除以11,看余数是多少;
4、余数只可能有0、1、2、3、4、5、6、7、8、9、10这11个数字。
其分别对应的最后一位身份证的号码为1、0、X、9、8、7、6、5、4、3、2,其中的X是罗马数字10;
5、通过上面得知如果余数是2,就会在身份证的第18位数字上出现罗马数字的Ⅹ;如果余数是10,身份证的最后一位号码就是2

  1. def id_upgrade(id_old):
  2. wi = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
  3. ecc = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2']
  4. id_seventeen = id_old[0:6] + '19' + id_old[6:]
  5. sum_ai_wi = 0 # 初值为0
  6. for i in range(len(id_seventeen)): # 遍历17位的身份证号
  7. sum_ai_wi = sum_ai_wi + int(id_seventeen[i]) * wi[i] # 身份证前十七位数分别乘以不同系数并求和
  8. id_new = id_seventeen + ecc[sum_ai_wi % 11] # 根据余数查ecc列表对应序号取得最后一位的字符,拼接到一起
  9. return id_new
  10. if __name__ == '__main__':
  11. id_fifteen = input() # 输入15位身份证号,都是19**年出生
  12. print(id_upgrade(id_fifteen))

输入
530126330315040
输出
530126193303150409

5.合法身份证号判定

  1. import datetime # 导入datetime模块用于获取当年年份
  2. def leap(year):
  3. return True if (year % 400 == 0) or (year % 4 == 0 and year % 100 != 0) else False
  4. # 校验身证号中的年月日及校验码
  5. def check_date(id_num):
  6. """校验身证号中的年月日,年月日值均要合法"""
  7. # 年份超过当前年,或月份小于1或大于12,或日期小于1或大于31时非法
  8. if int(id_num[6:10]) > datetime.datetime.now().year or int(id_num[10:12]) < 1 or int(id_num[10:12]) > 12 or int(
  9. id_num[12:14]) < 1 or int(id_num[12:14]) > 31:
  10. return False
  11. if int(id_num[10:12]) in [4, 6, 9, 11] and int(id_num[12:14]) > 30: # 当月份为4,6,9,11时,日期超过30即非法
  12. return False
  13. if int(id_num[10:12]) == 2 and int(id_num[12:14]) > 29: # 月份为2时,日期大于29便非法
  14. return False
  15. if int(id_num[10:12]) == 2 and leap(int(id_num[6:10])) == False and int(
  16. id_num[12:14]) > 28: # 月份为2时,如果不是闰年,日期大于28便非法
  17. return False
  18. else:
  19. return True
  20. def check_sum(id_num):
  21. """计算检验位"""
  22. ls = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2, 1]
  23. id_num_sum = sum([ls[i] * int(id_num[i]) for i in range(17)]) # 计算校验和
  24. if id_num[17] == 'X':
  25. return True if id_num_sum % 11 == 2 else False
  26. elif (id_num_sum % 11 + int(id_num[17])) % 11 == 1:
  27. return True
  28. else:
  29. return False
  30. def print_info(id_num):
  31. """输出信息"""
  32. year = int(id_num[6:10])
  33. month = id_num[10:12]
  34. day = id_num[12:14]
  35. if len(id_num) == 18 and check_date(id_num) and check_sum(id_num): # 先判断长度是否是18位,再判断校验和
  36. gender = '女' if int(id_num[16]) % 2 == 0 else '男'
  37. print('身份证号码校验为合法号码')
  38. print('出生:{}年{}月{}日'.format(year, month, day))
  39. print('年龄:{}'.format(datetime.datetime.now().year - year))
  40. print('性别:{}'.format(gender))
  41. else:
  42. print('身份证校验错误')
  43. if __name__ == '__main__':
  44. id_number = input()
  45. print_info(id_number)

输入
450221201012230741
输出
身份证号码校验为合法号码
出生:2010年12月23日
年龄:12
性别:女

输入
220221200513120034
输出
身份证校验错误

6.字符串切分为列表

  1. title = '姓名,性别,出生,民族,公民身份号码,住址,出生省市,出生地区,出生县区,出生区码,居住省市,居住地区,居住县区'
  2. print(title.split(','))
  3. # ['姓名', '性别', '出生', '民族', '公民身份号码', '住址', '出生省市', '出生地区', '出生县区', '出生区码', '居住省市', '居住地区', '居住县区']
  4. data = '吉嘉,男,20070505,佤,150202200705052293,白城市市聚福园26栋4单元2601室,内蒙古自治区,包头市,东河区,150202,吉林省,白城市,大安市'
  5. print(data.split(','))
  6. # ['吉嘉', '男', '20070505', '佤', '150202200705052293', '白城市市聚福园26栋4单元2601室', '内蒙古自治区', '包头市', '东河区', '150202', '吉林省', '白城市', '大安市']

7.模拟生成身份证号

AreaCode.txtfamily names.txtIDcode.txtnation.txtpopularNameF.txtpopularNameM.txtvillageName.txt
area_code.csv

  1. import random
  2. import datetime
  3. def generate_id_number():
  4. """随机生成并返回一个18位身份证号,并将性别,出生区码,出生省市区和身份证号存入字典"""
  5. id_info_dict = {}
  6. id_info_dict = get_gender(id_info_dict) # 为字典增加性别项
  7. order_num = order_number(id_info_dict['性别']) # 产生出生序号,3位数字字符串
  8. id_info_dict = get_birthdate(id_info_dict) # 为字典增加出生日期项,8位数字字符串
  9. area_code_dict = get_area(id_info_dict) # 为字典增加出生省、市、区项,返回出生区码
  10. id_number_17 = area_code_dict['出生区码'] + id_info_dict['出生'] + order_num # 17位身份证号
  11. ls = (7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2)
  12. ecc = ('1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2')
  13. s = sum([int(id_number_17[i]) * ls[i] for i in range(17)]) # 将数字与该位上的权值相乘放入列表并求和
  14. id_number = id_number_17 + ecc[s % 11] # 以位权和对11取余为索引获得校验位上的字符
  15. id_info_dict['公民身份号码'] = id_number
  16. return id_info_dict # 函数保存身份号码的字典
  17. def get_gender(id_info_dict):
  18. """随机获取一个性别并返回"""
  19. gender = random.choice('男女') # 随机生成男或女
  20. id_info_dict['性别'] = gender
  21. return id_info_dict
  22. def get_birthdate(id_info_dict):
  23. """在1900-2020间随机抽取一个数字作为出生年份,再随机生成一个合法的包含月和日的日期。需
  24. 要注意月份范围为1-12,1、3、5、7、8、10、12月的日期范围为1-31,4、6、9、11的日期范围为1-30,闰年2月
  25. 的日期范围为1-29,平年2月的日期范围为1-28。年为4位字符串,月和日均为2位字符串,依序构成长
  26. 度为8的字符串作为返回值,例如19840509 """
  27. year_of_birth = random.choice(range(1900, 2020))
  28. days_of_rand = datetime.timedelta(days=random.randint(1, 366))
  29. date_of_birth = datetime.datetime.strptime(str(year_of_birth) + '0101', "%Y%m%d") + days_of_rand # 月份和日期项
  30. birthdate = date_of_birth.strftime("%Y%m%d") # 19840509
  31. id_info_dict['出生'] = birthdate
  32. return id_info_dict
  33. def get_area(id_info_dict):
  34. """ 以地区编码为键,地区名为值构建字典作为返回值。"""
  35. area_code = {}
  36. area_file = '../data/csv/area_code.csv' # 地区码
  37. with open(area_file, 'r', encoding='utf-8') as data:
  38. for x in data:
  39. ls = x.strip().split(',')
  40. if len(ls) != 2: # 略过省级编码的行
  41. area_code[ls[0]] = ls[1] # 得到保存地区编码的字典
  42. area_id = random.choice(list(area_code.keys())) # 避免抽到省市的编码
  43. id_info_dict['出生区码'] = area_id
  44. return id_info_dict
  45. def order_number(gender):
  46. """接收性别为参数,随机抽取1-99之间的整数作为顺序号,根据性别随机抽取性别序号数字,男偶女奇"""
  47. num = random.choice(range(1, 100))
  48. # gender_num = random.choice('13579') if gender == '男' else random.choice('02468')
  49. if gender == '男':
  50. gender_num = random.choice('13579')
  51. else:
  52. gender_num = random.choice('02468')
  53. order = '{:02}'.format(num) + str(gender_num)
  54. return order
  55. def faker_id(n):
  56. """生成n个虚拟身份信息,写入到csv文件中"""
  57. filename = '模拟身份证.csv'
  58. for i in range(n):
  59. id_detail = generate_id_number() # 生成一个身份证号存入字典
  60. print(id_detail['公民身份号码'])
  61. if __name__ == '__main__':
  62. id_detail = generate_id_number() # 生成一个身份证号存入字典
  63. n = int(input())
  64. faker_id(n)

输入
10
输出
32021319040722635X
710619201104289648
210112196012191463
140826192309081310
45122820120707844X
441702193006150593
632701193809214650
71141419281001981X
450502191412269873
110114190405023710

模拟生成一个身份证

# ------------      -------    --------    -----------    -----------
# @File       : 7.4.3 模拟生成身份信息并查验身份实验模板.py
# @Contact    : vasp@qq.com
# @Copyright  : 2018-2025, Wuhan University of Technology
# @Modify Time: 2021/4/27 16:56
# @Author     : 赵广辉
# @Version    : 1.0
# @License    : 仅限用于Python程序设计基础实践教程(赵广辉,高等教育出版社)配套实验
# ------------      -------    --------    -----------    -----------

import random
import datetime


def generate_id_number():
    """随机生成并返回一个18位身份证号,并将性别,出生区码,出生省市区和身份证号存入字典"""
    id_info_dict = {}
    id_info_dict = get_gender(id_info_dict)  # 为字典增加性别项
    order_num = order_number(id_info_dict['性别'])  # 产生出生序号,3位数字字符串
    id_info_dict = get_birthdate(id_info_dict)  # 为字典增加出生日期项,8位数字字符串
    area_code_dict = area_code_dic()  # 生成地区编码字典
    area_code_dict = get_area(area_code_dict, id_info_dict)  # 为字典增加出生省、市、区项,返回出生区码
    id_number_17 = area_code_dict['出生区码'] + id_info_dict['出生'] + order_num  # 17位身份证号
    ls = (7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2)
    ecc = ('1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2')
    s = sum([int(id_number_17[i]) * ls[i] for i in range(17)])  # 将数字与该位上的权值相乘放入列表并求和
    id_number = id_number_17 + ecc[s % 11]  # 以位权和对11取余为索引获得校验位上的字符
    id_info_dict['公民身份号码'] = id_number
    return id_info_dict  # 函数保存身份号码的字典


def generate_information(id_info_dict):
    """模拟产生身份信息,返回字典"""
    gender = id_info_dict['性别']
    id_info_dict = person_name(gender, id_info_dict)  # 为字典增加姓名项
    id_info_dict = get_nation(id_info_dict)  # 为字典增加民族项
    area_code_dict = area_code_dic()  # 生成地区编码字典
    id_info_dict = get_address(id_info_dict)
    return id_info_dict


def get_gender(id_info_dict):
    """随机获取一个性别并返回"""
    gender = random.choice('男女')  # 随机生成男或女
    id_info_dict['性别'] = gender
    return id_info_dict


def get_last_name():
    """读百家姓文件,随机选选一个姓并返回"""
    last_name_file = '../data/txt/family names.txt'  # 百家姓
    with open(last_name_file, 'r', encoding='utf-8') as data:
        last = [line.strip().replace(',', '').replace('。', '')
                for line in data]
    last1 = ''.join(last[:51])
    last2 = ''.join(last[51:])
    last = list(last1) + [last2[i * 2: i * 2 + 2] for i in range(len(last2) // 2)]  # 得到姓的列表
    last_name = random.choice(last)  # 随机一个姓
    return last_name


def get_first_name(gender):
    """读男女姓名文件,根据性别随机选取一个名字,返回字符串"""
    male_name_file = '../data/txt/popularNameM.txt'  # 男性名来源文件
    female_name_file = '../data/txt/popularNameF.txt'  # 女性名来源文件
    name_file = male_name_file if gender == '男' else female_name_file
    with open(name_file, 'r', encoding='utf-8') as data:
        male_name = data.readline().split()
    first_name = random.choice(male_name)
    return first_name


def person_name(gender, id_info_dict):
    """last_name:姓,first_name:名字, 返回表示姓名的字符串。 """
    last_name = get_last_name()
    first_name = get_first_name(gender)
    full_name = last_name + first_name
    id_info_dict['姓名'] = full_name
    return id_info_dict


def get_birthdate(id_info_dict):
    """在1900-2020间随机抽取一个数字作为出生年份,再随机生成一个合法的包含月和日的日期。需
    要注意月份范围为1-12,1、3、5、7、8、10、12月的日期范围为1-31,4、6、9、11的日期范围为1-30,闰年2月
    的日期范围为1-29,平年2月的日期范围为1-28。年为4位字符串,月和日均为2位字符串,依序构成长
    度为8的字符串作为返回值,例如19840509 """
    year_of_birth = random.choice(range(1900, 2020))
    days_of_rand = datetime.timedelta(days=random.randint(1, 366))
    date_of_birth = datetime.datetime.strptime(str(year_of_birth) + '0101', "%Y%m%d") + days_of_rand  # 月份和日期项
    birthdate = date_of_birth.strftime("%Y%m%d")  # 19840509
    id_info_dict['出生'] = birthdate
    return id_info_dict


def get_nation(id_info_dict):
    """读包含民族的文件,从中随机抽取一个民族为返回值。
    需要注意的是,返回值里不包含'族'字,例如抽取'蒙古族',返回值为'蒙古'。
    """
    nation_file = '../data/txt/nation.txt'  # 民族
    with open(nation_file, 'r', encoding='utf-8') as data:
        nation_name = data.readline().split()
    nation = random.choice(nation_name)
    id_info_dict['民族'] = nation[:-1]
    return id_info_dict


def area_code_dic():
    """ 以地区编码为键,地区名为值构建字典作为返回值。"""
    area_code = {}
    area_file = '../data/txt/IDcode.txt'  # 地区码
    with open(area_file, 'r', encoding='utf-8') as data:
        for x in data:
            ls = x.strip().split(',')
            area_code[ls[0]] = ls[1]  # 得到保存地区编码的字典
    return area_code


def get_area(area_code, id_info_dict):
    """
    @参数 area_code:地区码字典
    接收地区码字典,出生日期和出生顺序号,随机抽取一个地区码,为身份信息字典增加出生地区信息项。
    需要注意的是,抽取地区码时,要避免抽取到省或地级市的编码(最后2位编码为0)。
    """
    area_no_city = [x for x in area_code.keys() if x[-2:] != '00']
    area_id = random.choice(area_no_city)  # 避免抽到省市的编码
    id_info_dict['出生省市'] = area_code[area_id[:2] + '0000']
    id_info_dict['出生地区'] = area_code[area_id[:-2] + '00']
    id_info_dict['出生县区'] = area_code[area_id]
    id_info_dict['出生区码'] = area_id
    return id_info_dict


def get_address(id_info_dict):
    """
    @ 参数 area_of_code:地区编码,字典类型
    返回居住地址,字符串
    从village_file中随机选择一个小区名,从area_of_code中随机选择一个地区编码,并从中获取省、市、
    县(区)名。楼栋号限制[1-30]中随机,单元号限制[1-7]中随机,楼层号限制[1-35]中随机,
    房间号限制[1-4]中随机。
    """
    area_code = area_code_dic()  # 生成地区编码字典
    village_file = '../data/txt/villageName.txt'  # 常用小区名
    with open(village_file, 'r', encoding='utf-8') as data:
        village_live = data.readline().split()
    village = random.choice(village_live)
    building = random.choice(range(1, 30))
    door = random.choice(range(1, 7))
    floor = random.choice(range(1, 35))
    room = random.choice(range(1, 4))
    area_id = random.choice([x for x in list(area_code.keys()) if x[-2:] != '00'])  # 避免抽到省市的编码
    province = area_code.get(area_id[:2] + '0000', '')
    city = area_code.get(area_id[:4] + '00', '')
    area = area_code[area_id]
    if area_id[:2] in ['11', '12', '31', '50']:  # 北京市,上海市,天津市,重庆市
        address_of_live = province + f'{village}{building}栋{door}单元{floor:02}{room:02}室'
        city = ''
    else:
        address_of_live = city + f'{village}{building}栋{door}单元{floor:02}{room:02}室'
    id_info_dict['住址'] = address_of_live
    id_info_dict['居住省市'] = province
    id_info_dict['居住地区'] = city
    id_info_dict['居住县区'] = area
    return id_info_dict


def order_number(gender):
    """接收性别为参数,随机抽取1-99之间的整数作为顺序号,根据性别随机抽取性别序号数字,男偶女奇"""
    num = random.choice(range(1, 100))
    # gender_num = random.choice('13579') if gender == '男' else random.choice('02468')
    if gender == '男':
        gender_num = random.choice('13579')
    else:
        gender_num = random.choice('02468')
    order = '{:02}'.format(num) + str(gender_num)
    return order


def print_id(id_info_dict):
    """
    按身份证正面的格式输出完整的身份信息,包括:
    姓名
    性别   民族
    出生年月日
    住址
    公民身份号码"""
    date = id_info_dict["出生"]
    print('-------------------------------------------------')
    print()
    print(f'  姓  名  {id_info_dict["姓名"]}')
    print(f'  性  别  {id_info_dict["性别"]}   民族  {id_info_dict["民族"]}')
    print(f'  出  生  {date[:4]} 年 {int(date[4:6])} 月 {int(date[6:])} 日')
    print(f'  住  址  {id_info_dict["住址"]}')
    print()
    print(f'  公民身份号码 {id_info_dict["公民身份号码"]}')
    print('-------------------------------------------------')



if __name__ == '__main__':
    # random.seed()  # 随机数种子,不用于自动评测时注释掉此行
    id_detail = generate_id_number()  # 生成一个身份证号存入字典
    id_detail = generate_information(id_detail) # 为字典补充信息
    print_id(id_detail)  # 打印身份证

image.png

8.写入文件

模拟身份证.csv

def id_detail_csv(file, id_info_dict):
    """将身份证信息按指定的顺序排序后写入到文件中"""
    order = ['姓名', '性别', '出生', '民族', '公民身份号码', '住址', '出生省市', '出生地区', '出生县区', '出生区码', '居住省市', '居住地区', '居住县区']
    title = ','.join(order) + '\n'
    data_ls = [id_info_dict[key] for key in order]
    data = ','.join(data_ls) + '\n'
    with open(file, 'a+', encoding="utf-8") as f:
        f.seek(0)  # 指针移动文件开头,判断文件是否为空
        if f.readline()[:2] == '姓名':  # 读第一行,若开头文字是'姓名',则有数据,略过标题行
            f.seek(2)  # 指针移动文件末尾
            f.write(data)
        else:
            f.write(title)
            f.write(data)

image.png

9.遍历输出文件

def read_file():
    file = '../data/csv/模拟身份证.csv'
    with open(file,'r',encoding='utf-8') as fr:
        for line in fr:
            print(line.strip())


if __name__ == '__main__':
    read_file()

输出

姓名,性别,出生,民族,公民身份号码,住址,出生省市,出生地区,出生县区,出生区码,居住省市,居住地区,居住县区
吉嘉,男,20070505,佤,150202200705052293,白城市市聚福园26栋4单元2601室,内蒙古自治区,包头市,东河区,150202,吉林省,白城市,大安市
西门晓晴,女,19330315,佤,530126193303150409,大理州翠岛花城24栋5单元3201室,云南省,昆明市,石林彝族自治县,530126,云南省,大理州,祥云县
洪俊,男,19311021,高山,152501193110216470,汉中市金帝花园2栋5单元3303室,内蒙古自治区,锡林郭勒盟市,二连浩特市,152501,陕西省,汉中市,镇巴县
......
明寄莲,女,19521227,普米,341204195212278409,平顶山北京青年城21栋2单元2602室,安徽省,阜阳市,颍泉区,341204,河南省,平顶山,宝丰县
孙冷筠,女,19320316,黎,411481193203164787,呼伦贝尔市蓝山国际18栋1单元0403室,河南省,商丘市,永城市,411481,内蒙古自治区,呼伦贝尔市,阿荣旗
晁鸿煊,男,19340617,赫哲,431028193406170538,济宁市独墅逸致16栋3单元0101室,湖南省,郴州市,安仁县,431028,山东省,济宁市,金乡县

10.读文件转列表

def read_file():
    file = '../data/csv/模拟身份证.csv'
    with open(file,'r',encoding='utf-8') as fr:
        info_lst = [line.strip().split(',') for line in fr]
    return info_lst


if __name__ == '__main__':
    print(read_file())
def read_file():
    file = '../data/csv/模拟身份证.csv'
    with open(file,'r',encoding='utf-8') as fr:
        info_lst = []
        for line in fr:
            info_lst.append(line.strip().split(','))

    return info_lst


if __name__ == '__main__':
    print(read_file())

输出

[['姓名', '性别', '出生', '民族', '公民身份号码', '住址', '出生省市', '出生地区', '出生县区', '出生区码', '居住省市', '居住地区', '居住县区'], 
 ['吉嘉', '男', '20070505', '佤', '150202200705052293', '白城市市聚福园26栋4单元2601室', '内蒙古自治区', '包头市', '东河区', '150202', '吉林省', '白城市', '大安市'], 
 ['西门晓晴', '女', '19330315', '佤', '530126193303150409', '大理州翠岛花城24栋5单元3201室', '云南省', '昆明市', '石林彝族自治县', '530126', '云南省', '大理州', '祥云县'],
 ......
 ['晁鸿煊', '男', '19340617', '赫哲', '431028193406170538', '济宁市独墅逸致16栋3单元0101室', '湖南省', '郴州市', '安仁县', '431028', '山东省', '济宁市', '金乡县']]

11.读文件转元素为字典的列表

def read_file():
    """读文件,返回元素是字典的列表"""
    file = '../data/csv/模拟身份证.csv'
    with open(file,'r',encoding='utf-8') as fr:
        info_lst = []
        for line in fr:
            info_lst.append(line.strip().split(','))
    info_dic = []  # 空列表,向其中加入字典元素
    for i in range(1, len(info_lst[1:]) + 1):
        info_dic.append(dict(zip(info_lst[0], info_lst[i])))
    return info_dic


if __name__ == '__main__':
    print(read_file())

输出

[{'姓名': '吉嘉', '性别': '男', '出生': '20070505', '民族': '佤', '公民身份号码': '150202200705052293', '住址': '白城市市聚福园26栋4单元2601室', '出生省市': '内蒙古自治区', '出生地区': '包头市', '出生县区': '东河区', '出生区码': '150202', '居住省市': '吉林省', '居住地区': '白城市', '居住县区': '大安市'}, 
 {'姓名': '西门晓晴', '性别': '女', '出生': '19330315', '民族': '佤', '公民身份号码': '530126193303150409', '住址': '大理州翠岛花城24栋5单元3201室', '出生省市': '云南省', '出生地区': '昆明市', '出生县区': '石林彝族自治县', '出生区码': '530126', '居住省市': '云南省', '居住地区': '大理州', '居住县区': '祥云县'}, 
 {'姓名': '洪俊', '性别': '男', '出生': '19311021', '民族': '高山', '公民身份号码': '152501193110216470', '住址': '汉中市金帝花园2栋5单元3303室', '出生省市': '内蒙古自治区', '出生地区': '锡林郭勒盟市', '出生县区': '二连浩特市', '出生区码': '152501', '居住省市': '陕西省', '居住地区': '汉中市', '居住县区': '镇巴县'}, 
 ......
 {'姓名': '晁鸿煊', '性别': '男', '出生': '19340617', '民族': '赫哲', '公民身份号码': '431028193406170538', '住址': '济宁市独墅逸致16栋3单元0101室', '出生省市': '湖南省', '出生地区': '郴州市', '出生县区': '安仁县', '出生区码': '431028', '居住省市': '山东省', '居住地区': '济宁市', '居住县区': '金乡县'}]

12.分离字典的键值为二维列表

def read_file():
    """读文件,返回元素是字典的列表"""
    file = '../data/csv/模拟身份证.csv'
    with open(file,'r',encoding='utf-8') as fr:
        info_lst = []
        for line in fr:
            info_lst.append(line.strip().split(','))
    info_dic = []  # 空列表,向其中加入字典元素
    for i in range(1, len(info_lst[1:]) + 1):
        info_dic.append(dict(zip(info_lst[0], info_lst[i])))
    return info_dic


def get_value(info_dic):
    """参数为元素是字典的列表,将字典中的键值分离,返回二维列表"""
    info_ls = []
    title = list(info_dic[0].keys())
    info_ls.append(title)
    for x in info_dic:
        info_ls.append(list(x.values()))
    return info_ls


if __name__ == '__main__':
    info_dict = read_file()
    print(get_value(info_dict))

输出

[['姓名', '性别', '出生', '民族', '公民身份号码', '住址', '出生省市', '出生地区', '出生县区', '出生区码', '居住省市', '居住地区', '居住县区'], 
 ['吉嘉', '男', '20070505', '佤', '150202200705052293', '白城市市聚福园26栋4单元2601室', '内蒙古自治区', '包头市', '东河区', '150202', '吉林省', '白城市', '大安市'], 
 ['西门晓晴', '女', '19330315', '佤', '530126193303150409', '大理州翠岛花城24栋5单元3201室', '云南省', '昆明市', '石林彝族自治县', '530126', '云南省', '大理州', '祥云县'], 
......
 ['晁鸿煊', '男', '19340617', '赫哲', '431028193406170538', '济宁市独墅逸致16栋3单元0101室', '湖南省', '郴州市', '安仁县', '431028', '山东省', '济宁市', '金乡县']]

13.二维列表写入文件

def read_file():
    file = '../data/csv/模拟身份证.csv'
    with open(file,'r',encoding='utf-8') as fr:
        info_lst = []
        for line in fr:
            info_lst.append(line.strip().split(','))
    info_dic = []  # 空列表,向其中加入字典元素
    for i in range(1, len(info_lst[1:]) + 1):
        info_dic.append(dict(zip(info_lst[0], info_lst[i])))
    return info_dic


def get_value(info_dic):
    info_ls = []
    title = list(info_dic[0].keys())
    info_ls.append(title)
    for x in info_dic:
        info_ls.append(list(x.values()))
    return info_ls


def ls_to_csv(info_ls):
    """将身份证信息按指定的顺序排序后写入到文件中"""
    file = '../data/csv/虚拟身份信息.csv'
    with open(file, 'w', encoding="utf-8") as f:
        for line in info_ls:
            line_str = ','.join(line) + '\n'
            f.write(line_str)


if __name__ == '__main__':
    info_dict = read_file()
    info_lst = get_value(info_dict)
    ls_to_csv(info_lst)

文件内容
虚拟身份信息.csv

姓名,性别,出生,民族,公民身份号码,住址,出生省市,出生地区,出生县区,出生区码,居住省市,居住地区,居住县区
吉嘉,男,20070505,佤,150202200705052293,白城市市聚福园26栋4单元2601室,内蒙古自治区,包头市,东河区,150202,吉林省,白城市,大安市
西门晓晴,女,19330315,佤,530126193303150409,大理州翠岛花城24栋5单元3201室,云南省,昆明市,石林彝族自治县,530126,云南省,大理州,祥云县
洪俊,男,19311021,高山,152501193110216470,汉中市金帝花园2栋5单元3303室,内蒙古自治区,锡林郭勒盟市,二连浩特市,152501,陕西省,汉中市,镇巴县
......
明寄莲,女,19521227,普米,341204195212278409,平顶山北京青年城21栋2单元2602室,安徽省,阜阳市,颍泉区,341204,河南省,平顶山,宝丰县
孙冷筠,女,19320316,黎,411481193203164787,呼伦贝尔市蓝山国际18栋1单元0403室,河南省,商丘市,永城市,411481,内蒙古自治区,呼伦贝尔市,阿荣旗
晁鸿煊,男,19340617,赫哲,431028193406170538,济宁市独墅逸致16栋3单元0101室,湖南省,郴州市,安仁县,431028,山东省,济宁市,金乡县

14.赋健康码

def add_color(file):
    """读文件中的数据,为数据赋健康码,转为元素为字典的列表返回"""
    with open(file, 'r', encoding='utf-8') as fr:
        id_info = [line.strip().split(',') for line in fr]
    id_info[0].append('健康码')
    for x in id_info[1:]:
        color = random.choice(['red', 'green', 'yellow'])
        x.append(color)
    for i in range(1, len(id_info[1:])+1):
        id_info[i] = dict(zip(id_info[0], id_info[i]))
    print(id_info)
    return id_info[1:]

15.批量生成健康码图片文件

def make_qrcode(id_info):
    """为每一个人生成一个二维码"""
    path = './二维码/'
    for x in id_info:
        qr = qrcode.QRCode(
            version=None,  # 控制二维码的大小,程序自动确定
            error_correction=qrcode.constants.ERROR_CORRECT_M,  # 大约15%或更少的错误能被纠正。
            box_size=10,  # 控制二维码中每个小格子包含的像素数
            border=4,     # 控制边框(二维码与图片边界的距离)包含的格子数
        )
        qr.add_data(str(x))  # 给二维码添加数据
        qr.make(fit=True)  # 生成二维码
        # 设置二维码颜色,第一个是填充颜色 第二个是背景颜色
        img = qr.make_image(fill_color=x['健康码'], back_color="white")
        print(f"正在生成{x['姓名']}的二维码...")
        # img.show()  # 二维码展示
        img.save(path + x['姓名'] + '.png')  # 二维码保存

image.png

16.查看健康码

def show_qrcode(id_info):
    """查看一个人二维码"""
    id_number = input()
    qr = qrcode.QRCode()
    content = [x for x in id_info if x['公民身份号码'] == id_number]
    qr.add_data(str(content[0]))  # 给二维码添加数据
    qr.make(fit=True)  # 生成二维码
    # 设置二维码颜色,第一个是填充颜色 第二个是背景颜色
    img = qr.make_image(fill_color=content[0]['健康码'], back_color="white")
    img.show()  # 二维码展示

输入
33022519480430736X
image.png

17.查询出生或居住地

def check_area(id_info_dict):
    """输入一个省市,返回出生或居住在该省的人的数据"""
    city = input()
    id_info_city = [x for x in id_info_dict if city in [x['出生省市'],x['居住省市']]]
    return id_info_city

输入
河北省
输出

[{'姓名': '段立诚', '性别': '男', '出生': '19400211', '民族': '佤', '公民身份号码': '341502194002118596', '住址': '沧州市恒美嘉园24栋4单元0301室', '出生省市': '安徽省', '出生地区': '六安市', '出生县区': '金安区', '出生区码': '341502', '居住省市': '河北省', '居住地区': '沧州市', '居住县区': '新华区', '健康码': 'green'}, 
 {'姓名': '欧涛', '性别': '男', '出生': '19421203', '民族': '景颇', '公民身份号码': '131128194212038415', '住址': '庆阳市顺驰林溪别墅3栋4单元0201室', '出生省市': '河北省', '出生地区': '衡水市', '出生县区': '阜城县', '出生区码': '131128', '居住省市': '甘肃省', '居住地区': '庆阳市', '居住县区': '合水县', '健康码': 'red'}, 
 {'姓名': '上官屋山', '性别': '男', '出生': '19230625', '民族': '拉祜', '公民身份号码': '510682192306251635', '住址': '石家庄市燕沙·后(东润枫景)10栋3单元3103室', '出生省市': '四川省', '出生地区': '德阳市', '出生县区': '什邡市', '出生区码': '510682', '居住省市': '河北省', '居住地区': '石家庄市', '居住县区': '桥西区', '健康码': 'red'}, 
 {'姓名': '蒲明达', '性别': '男', '出生': '20101014', '民族': '门巴', '公民身份号码': '130728201010141734', '住址': '广州市建东苑26栋1单元2301室', '出生省市': '河北省', '出生地区': '张家口', '出生县区': '怀安县', '出生区码': '130728', '居住省市': '广东省', '居住地区': '广州市', '居住县区': '花都区', '健康码': 'red'}, 
 {'姓名': '麻慧语', '性别': '男', '出生': '19160320', '民族': '裕固', '公民身份号码': '530122191603209295', '住址': '保定市城市花园24栋5单元0403室', '出生省市': '云南省', '出生地区': '昆明市', '出生县区': '晋宁县', '出生区码': '530122', '居住省市': '河北省', '居住地区': '保定市', '居住县区': '清苑区', '健康码': 'green'}, 
 {'姓名': '娄正豪', '性别': '男', '出生': '19901204', '民族': '壮', '公民身份号码': '220602199012047576', '住址': '保定市融城20栋3单元1001室', '出生省市': '吉林省', '出生地区': '白山市', '出生县区': '浑江区', '出生区码': '220602', '居住省市': '河北省', '居住地区': '保定市', '居住县区': '涿州市', '健康码': 'green'}, 
 {'姓名': '狄宇寰', '性别': '男', '出生': '19110317', '民族': '蒙古', '公民身份号码': '530821191103175553', '住址': '张家口阳光地带11栋6单元1703室', '出生省市': '云南省', '出生地区': '普洱市', '出生县区': '宁洱县', '出生区码': '530821', '居住省市': '河北省', '居住地区': '张家口', '居住县区': '涿鹿县', '健康码': 'green'}, 
 {'姓名': '段干景同', '性别': '男', '出生': '19850708', '民族': '彝', '公民身份号码': '350721198507088336', '住址': '邢台市鉴湖景园13栋6单元1103室', '出生省市': '福建省', '出生地区': '南平市', '出生县区': '顺昌县', '出生区码': '350721', '居住省市': '河北省', '居住地区': '邢台市', '居住县区': '临西县', '健康码': 'green'}, 
 {'姓名': '叶小易', '性别': '女', '出生': '20000504', '民族': '撒拉', '公民身份号码': '320903200005048046', '住址': '沧州市爱建园25栋4单元0603室', '出生省市': '江苏省', '出生地区': '盐城市', '出生县区': '盐都区', '出生区码': '320903', '居住省市': '河北省', '居住地区': '沧州市', '居住县区': '肃宁县', '健康码': 'yellow'}, 
 {'姓名': '汲懿美', '性别': '男', '出生': '19381122', '民族': '回', '公民身份号码': '131028193811226338', '住址': '阿坝自治州天和人家12栋6单元1003室', '出生省市': '河北省', '出生地区': '廊坊市', '出生县区': '大厂回族自治县', '出生区码': '131028', '居住省市': '四川省', '居住地区': '阿坝自治州', '居住县区': '马尔康县', '健康码': 'red'}, 
 {'姓名': '伏千旋', '性别': '女', '出生': '20120804', '民族': '纳西', '公民身份号码': '130706201208041626', '住址': '资阳市远洋山水20栋6单元2101室', '出生省市': '河北省', '出生地区': '张家口', '出生县区': '下花园区', '出生区码': '130706', '居住省市': '四川省', '居住地区': '资阳市', '居住县区': '安岳县', '健康码': 'yellow'}, 
 {'姓名': '逄从蝶', '性别': '女', '出生': '19400830', '民族': '朝鲜', '公民身份号码': '530402194008305885', '住址': '衡水市欧园·北欧印象9栋4单元2701室', '出生省市': '云南省', '出生地区': '玉溪市', '出生县区': '红塔区', '出生区码': '530402', '居住省市': '河北省', '居住地区': '衡水市', '居住县区': '武强县', '健康码': 'green'}, 
 {'姓名': '苗夜梅', '性别': '女', '出生': '19430407', '民族': '布朗', '公民身份号码': '130207194304078660', '住址': '德宏州绣江18栋3单元2201室', '出生省市': '河北省', '出生地区': '唐山市', '出生县区': '丰南区', '出生区码': '130207', '居住省市': '云南省', '居住地区': '德宏州', '居住县区': '芒市', '健康码': 'green'}, 
 {'姓名': '牛容曰', '性别': '男', '出生': '19000227', '民族': '纳西', '公民身份号码': '421003190002278056', '住址': '保定市新贵都20栋1单元1703室', '出生省市': '湖北省', '出生地区': '荆州市', '出生县区': '荆州区', '出生区码': '421003', '居住省市': '河北省', '居住地区': '保定市', '居住县区': '涿州市', '健康码': 'yellow'}]

18.统计离开籍贯所在地的人数

def migrate():
    """读文件中的数据,统计并返回离开籍贯所在地的人数"""
    file = '模拟身份证.csv'
    native_place = input()  # 籍贯
    migrate_info = []
    with open(file, 'r', encoding='utf-8') as fr:
        for line in fr:
            x = line.strip().split(',')
            if x[6] == native_place and x[10] != native_place:
                migrate_info.append(x)
    print(migrate_info)
    return native_place, len(migrate_info)


if __name__ == '__main__':
    result = migrate()
    print(f'离开{result[0]}的人有{result[1]}人')

输入
吉林省
输出

[['杭煜祺', '男', '19931021', '黎', '220402199310212217', '保定市城市复兴·华城19栋6单元2203室', '吉林省', '辽源市', '龙山区', '220402', '河北省', '保定市', '高阳县'], 
 ['盖子轩', '男', '19201030', '京', '220524192010307291', '榆林市欧园·北欧印象7栋5单元0302室', '吉林省', '通化市', '柳河县', '220524', '陕西省', '榆林市', '定边县'], 
 ['仲孙旭尧', '男', '19251113', '普米', '22240519251113623X', '石家庄市蓝山国际12栋4单元0603室', '吉林省', '延边州', '龙井市', '222405', '河北省', '石家庄市', '长安区'], 
 ['敖曼蕾', '女', '20050309', '塔塔尔', '220182200503096161', '西宁市天和人家9栋5单元1601室', '吉林省', '长春市', '榆树市', '220182', '青海自治区', '西宁市', '城中区'], 
......
 ['单伟泽', '男', '19680212', '瑶', '220102196802120757', '张家口市聚福园6栋4单元1301室', '吉林省', '长春市', '南关区', '220102', '河北省', '张家口', '崇礼县']]
离开吉林省的人有447人

19.拍照识别身份证上的文字信息

利用百度AI可以准确的识别身份证照片上的信息,在控制台-免费资源领取页 可领取所需接口的免费测试资源。未实名认证 用户可领取 200 次/月,个人认证 1,000 次/月,企业认证 2,000 次/月。
免费测试资源用尽后按照如下价格进行计费。如需付费使用,可 购买次数包 或 开通按量后付费,如需扩充 QPS,可购买 QPS 叠加包

预付费次数包

规格(次) 价格(元)
1万 200
5万 950
10万 1700
20万 3000
50万 6000
100万 10000
500万 40000

首先在百度AI中注册,并开通身份证识别应用。
https://ai.baidu.com/tech/ocr_cards/idcard
安装Python SDK:

pip install baidu-aip
from aip import AipOcr


def baidu_ocr(picfile):  #
    """利用百度AI提供的算API识别身份证上的信息。
    参数picfile为身份证图片文件名。
    """
    APP_ID = '25****89'                              # 用户自己创建的应用的appid
    API_KEY = 'FR2V************XFuII1MW'             # 用户自己创建的应用的appkey
    SECRET_KEY = 'DRO9********************IgkGpkqx'  # 用户自己创建的应用的secretkey
    client = AipOcr(APP_ID, API_KEY, SECRET_KEY)
    with open(picfile, 'rb') as id_info:
        img = id_info.read()
        """ 调用通用文字识别(高精度版) """
        message = client.basicAccurate(img)

    id_info = []  # 识别出的文本内容存入列表
    for text in message.get('words_result'):  # 识别的内容
        # print(text)  # 字典类型,键为word,值为识别的信息
        id_info.append(text.get('words'))  # 取值加入列表
    return id_info


if __name__ == '__main__':
    print(baidu_ocr('zghid.jpg'))
# ['姓名', '赵广辉', '性别男', '民族汉', '出生19**年*月**日', '住址', '武汉市洪山区***', '**-012-4号', '公民身份号码220221***********4']

根据获取的身份证号可以到前面字典中取得人名,从而得到其二维码

20. 批量处理检测报告

批量识别检测报告中的文本,提取必要信息写入文件
核酸副本.png

import os
from aip import AipOcr


def baidu_ocr(picfile):  #
    """利用百度AI提供的算API识别检验报告上的信息 """
    APP_ID = '25****89'                              # 用户自己创建的应用的appid 
    API_KEY = 'FR2V*****I1MW'             # 用户自己创建的应用的appkey
    SECRET_KEY = 'DRO9******Gpkqx'  # 用户自己创建的应用的secretkey
    client = AipOcr(APP_ID, API_KEY, SECRET_KEY)
    with open(picfile, 'rb') as id_info:
        img = id_info.read()
        message = client.basicAccurate(img)  # 调用通用文字识别(高精度版) 
    id_info = []  # 识别出的文本内容存入列表
    for text in message.get('words_result'):  # 识别的内容
        id_info.append(text.get('words'))  # 取值加入列表
    return id_info

def filename_list(path):
    """接收路径字符串为参数,获取该路径下所有文件名,以列表形式返回,批量识别多文件。
    os.listdir(path)以列表形式返回path路径下的所有文件名,不包括子路径中的文件名"""
    name_list = os.listdir(path)
    return name_list

def write_to_file(info):
    with open('nucleic_acid_report.csv','a',encoding='utf-8') as fw:
        info_str = ','.join(info)+'\n'  # 拼接为字符串,行末加换行符
        fw.write(info_str)              # 字符串追加写入文件

def result_to_file(file_list):
    for file in file_list:
        data = baidu_ocr(filepath + file)  # 调用函数识别文字
        info = [data[i] for i in (6,7,10,11,12,15,17,24,43)]  # 提取必要信息
        write_to_file(info)  # 调用函数写文件

if __name__ == '__main__':
    filepath = './Nucleic Acid Test/'
    file_list = filename_list(filepath)
    result_to_file(file_list)

文件’nucleic_acid_report.csv’内容如下所示(仅列出一行数据)

条码号:WH203300238790B,姓名:赵*辉,样本类型:咽拭子,性别:男,病人电话:132****6288,年龄:49岁,证件号:220221********6534,阴性,采样时间:2022-04-0617:31接收时间:2022-04-0620:56检验时间:2022-04-0620:56报告时间:2022-04-0703:11

21.完整代码

# ------------      -------    --------    -----------    -----------
# @File       : 7.4.3 模拟生成身份信息并查验身份实验模板.py
# @Contact    : vasp@qq.com
# @Copyright  : 2018-2025, Wuhan University of Technology
# @Modify Time: 2021/4/27 16:56
# @Author     : 赵广辉
# @Version    : 1.0
# @License    : 仅限用于Python程序设计基础实践教程(赵广辉,高等教育出版社)配套实验
# ------------      -------    --------    -----------    -----------
# 通过查验居民身份证可以掌握持证人的姓名、性别、出生日期、住址和公民身份证号码等信息,还可以获得居住后和出生地信息。
# 疫情期间,需要通过查验身份证来实现对一些出生或居住在敏感地区的人进行监控,现在需要你开发这样一个系统,具有以下功能:
# 1.为测试以上功能,模拟产生一个身份证上的全部信息。具体方法为
# 1.1 模拟姓名,从百家姓中抽取一个姓,注意百家姓文件中前51行为单姓,51行后面为双字复姓。根据性别从男或女性常用名中取机抽取一个名字。
# 1.2 模拟出生日期(限制1900-2020),性别随机男女、民族从56个民族中随机取一个
# 1.3 模拟住址,省、市、县区随机,随机一个小区、100以内整数楼号、房间号格式为 a - b0c,a 为 1-8, b为 0-35,c 为 1-4
# 1.4  模拟生成身份证号,出生序号随机。
# 1.5 按身份证格式打印输出模拟产生的身份证
# 2.输出年龄和性别
# 3.获取和输出身份证注册地的省、市、县(区)
# 4.获取持证人居住地的省、市、县(区)
# 5.根据输入设置敏感地区,判定持证人是否为敏感地区常住人或是敏感地区出生者。

import random
import datetime
import qrcode
from aip import AipOcr


# {'性别': '男',
#  '姓名': '缪心怡',
#  '出生': '1910 年 7 月 9 日',
#  '民族': '仫佬',
#  '出生省市': '广西自治区', '出生地区': '桂林市', '出生县区': '七星区',
#  '公民身份号码': '450305191007098298',
#  '住址': '安徽省阜阳市阜南县北京财富中心27栋4单元2502室',
#  '居住省市': '安徽省', '居住地区': '阜阳市', '居住县区': '阜南县'
#  }


def generate_id_number():
    """随机生成并返回一个18位身份证号,并将性别,出生区码,出生省市区和身份证号存入字典"""
    id_info_dict = {}
    id_info_dict = get_gender(id_info_dict)  # 为字典增加性别项
    order_num = order_number(id_info_dict['性别'])  # 产生出生序号,3位数字字符串
    id_info_dict = get_birthdate(id_info_dict)  # 为字典增加出生日期项,8位数字字符串
    area_code_dict = area_code_dic()  # 生成地区编码字典
    area_code_dict = get_area(area_code_dict, id_info_dict)  # 为字典增加出生省、市、区项,返回出生区码
    id_number_17 = area_code_dict['出生区码'] + id_info_dict['出生'] + order_num  # 17位身份证号
    ls = (7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2)
    ecc = ('1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2')
    s = sum([int(id_number_17[i]) * ls[i] for i in range(17)])  # 将数字与该位上的权值相乘放入列表并求和
    id_number = id_number_17 + ecc[s % 11]  # 以位权和对11取余为索引获得校验位上的字符
    id_info_dict['公民身份号码'] = id_number
    return id_info_dict  # 函数保存身份号码的字典


def generate_information(id_info_dict):
    """模拟产生身份信息,返回字典"""
    gender = id_info_dict['性别']
    id_info_dict = person_name(gender, id_info_dict)  # 为字典增加姓名项
    id_info_dict = get_nation(id_info_dict)  # 为字典增加民族项
    area_code_dict = area_code_dic()  # 生成地区编码字典
    id_info_dict = get_address(id_info_dict)
    return id_info_dict


def get_gender(id_info_dict):
    """随机获取一个性别并返回"""
    gender = random.choice('男女')  # 随机生成男或女
    id_info_dict['性别'] = gender
    return id_info_dict


def get_last_name():
    """读百家姓文件,随机选选一个姓并返回"""
    last_name_file = '../data/family names.txt'  # 百家姓
    with open(last_name_file, 'r', encoding='utf-8') as data:
        last = [line.strip().replace(',', '').replace('。', '')
                for line in data]
    last1 = ''.join(last[:51])
    last2 = ''.join(last[51:])
    last = list(last1) + [last2[i * 2: i * 2 + 2] for i in range(len(last2) // 2)]  # 得到姓的列表
    last_name = random.choice(last)  # 随机一个姓
    return last_name


def get_first_name(gender):
    """读男女姓名文件,根据性别随机选取一个名字,返回字符串"""
    male_name_file = '../data/popularNameM.txt'  # 男性名来源文件
    female_name_file = '../data/popularNameF.txt'  # 女性名来源文件
    name_file = male_name_file if gender == '男' else female_name_file
    with open(name_file, 'r', encoding='utf-8') as data:
        male_name = data.readline().split()
    first_name = random.choice(male_name)
    return first_name


def person_name(gender, id_info_dict):
    """last_name:姓,first_name:名字, 返回表示姓名的字符串。 """
    last_name = get_last_name()
    first_name = get_first_name(gender)
    full_name = last_name + first_name
    id_info_dict['姓名'] = full_name
    return id_info_dict


def get_birthdate(id_info_dict):
    """在1900-2020间随机抽取一个数字作为出生年份,再随机生成一个合法的包含月和日的日期。需
    要注意月份范围为1-12,1、3、5、7、8、10、12月的日期范围为1-31,4、6、9、11的日期范围为1-30,闰年2月
    的日期范围为1-29,平年2月的日期范围为1-28。年为4位字符串,月和日均为2位字符串,依序构成长
    度为8的字符串作为返回值,例如19840509 """
    year_of_birth = random.choice(range(1900, 2020))
    days_of_rand = datetime.timedelta(days=random.randint(1, 366))
    date_of_birth = datetime.datetime.strptime(str(year_of_birth) + '0101', "%Y%m%d") + days_of_rand  # 月份和日期项
    birthdate = date_of_birth.strftime("%Y%m%d")  # 19840509
    id_info_dict['出生'] = birthdate
    return id_info_dict


def get_nation(id_info_dict):
    """读包含民族的文件,从中随机抽取一个民族为返回值。
    需要注意的是,返回值里不包含'族'字,例如抽取'蒙古族',返回值为'蒙古'。
    """
    nation_file = '../data/nation.txt'  # 民族
    with open(nation_file, 'r', encoding='utf-8') as data:
        nation_name = data.readline().split()
    nation = random.choice(nation_name)
    id_info_dict['民族'] = nation[:-1]
    return id_info_dict


def area_code_dic():
    """ 以地区编码为键,地区名为值构建字典作为返回值。"""
    area_code = {}
    area_file = '../data/IDcode.txt'  # 地区码
    with open(area_file, 'r', encoding='utf-8') as data:
        for x in data:
            ls = x.strip().split(',')
            area_code[ls[0]] = ls[1]  # 得到保存地区编码的字典
    return area_code

def get_area(area_code, id_info_dict):
    """
    @参数 area_code:地区码字典
    接收地区码字典,出生日期和出生顺序号,随机抽取一个地区码,为身份信息字典增加出生地区信息项。
    需要注意的是,抽取地区码时,要避免抽取到省或地级市的编码(最后2位编码为0)。
    """
    area_no_city = [x for x in area_code.keys() if x[-2:] != '00']
    area_id = random.choice(area_no_city)  # 避免抽到省市的编码
    id_info_dict['出生省市'] = area_code[area_id[:2] + '0000']
    id_info_dict['出生地区'] = area_code.get(area_id[:-2] + '00','')
    id_info_dict['出生县区'] = area_code[area_id]
    id_info_dict['出生区码'] = area_id
    return id_info_dict


def get_address(id_info_dict):
    """
    @ 参数 area_of_code:地区编码,字典类型
    返回居住地址,字符串
    从village_file中随机选择一个小区名,从area_of_code中随机选择一个地区编码,并从中获取省、市、
    县(区)名。楼栋号限制[1-30]中随机,单元号限制[1-7]中随机,楼层号限制[1-35]中随机,
    房间号限制[1-4]中随机。
    """
    area_code = area_code_dic()  # 生成地区编码字典
    village_file = '../data/villageName.txt'  # 常用小区名
    with open(village_file, 'r', encoding='utf-8') as data:
        village_live = data.readline().split()
    village = random.choice(village_live)
    building = random.choice(range(1, 30))
    door = random.choice(range(1, 7))
    floor = random.choice(range(1, 35))
    room = random.choice(range(1, 4))
    area_id = random.choice([x for x in list(area_code.keys()) if x[-2:] != '00'])  # 避免抽到省市的编码
    province = area_code.get(area_id[:2] + '0000', '')
    city = area_code.get(area_id[:4] + '00', '')
    area = area_code[area_id]
    if area_id[:2] in ['11', '12', '31', '50'] or not city:  # 北京市,上海市,天津市,重庆市
        address_of_live = province + f'{village}{building}栋{door}单元{floor:02}{room:02}室'
    else:
        address_of_live = city + f'{village}{building}栋{door}单元{floor:02}{room:02}室'
    id_info_dict['住址'] = address_of_live
    id_info_dict['居住省市'] = province
    id_info_dict['居住地区'] = city
    id_info_dict['居住县区'] = area
    return id_info_dict


def order_number(gender):
    """接收性别为参数,随机抽取1-99之间的整数作为顺序号,根据性别随机抽取性别序号数字,男偶女奇"""
    num = random.choice(range(1, 100))
    # gender_num = random.choice('13579') if gender == '男' else random.choice('02468')
    if gender == '男':
        gender_num = random.choice('13579')
    else:
        gender_num = random.choice('02468')
    order = '{:02}'.format(num) + str(gender_num)
    return order


def print_id(id_info_dict):
    """
    按身份证正面的格式输出完整的身份信息,包括:
    姓名
    性别   民族
    出生年月日
    住址
    公民身份号码"""
    date = id_info_dict["出生"]
    print('-------------------------------------------------')
    print()
    print(f'  姓  名  {id_info_dict["姓名"]}')
    print(f'  性  别  {id_info_dict["性别"]}   民族  {id_info_dict["民族"]}')
    print(f'  出  生  {date[:4]} 年 {int(date[4:6])} 月 {int(date[6:])} 日')
    print(f'  住  址  {id_info_dict["住址"]}')
    print()
    print(f'  公民身份号码 {id_info_dict["公民身份号码"]}')
    print('-------------------------------------------------')


def area_of_registration(id_info_dict):
    """返回持证人出生的省市县(区)
    需要注意的是,若持证人注册地为直辖市,则住址中无地级市,直接输出市和区,例如:北京市朝阳区
    其他地区格式例如:湖北省武汉市洪山区。
    """
    return f'持证人出生于{id_info_dict["出生省市"]}{id_info_dict["出生地区"]}{id_info_dict["出生县区"]}'


def area_of_live(id_info_dict):
    """返回持证人居住地址的省市县(区) """
    return f'持证人住址为{id_info_dict["居住省市"]}{id_info_dict["住址"]}'


def check_city(city_name, id_info_dict):
    """
    @ 参数 city_name:查验城市名,字符串类型
    接收查验城市名,查验持证人是否与指定的城市相关
    若居住地与查验城市名相同,返回持证人居住于city_name市
    否则若出生地与查验城市相同,返回持证人出生于city_name市
    其他情况返回持证人与city_name无关联。
    """
    if city_name in id_info_dict['居住地区']:
        return f'持证人居住于{city_name}'
    elif city_name in id_info_dict['出生地区']:
        return f'持证人出生于{city_name}'
    else:
        return f'持证人与{city_name}无关联'


def judge(id_info_dict):
    """输入一个字符串为参数,如果参数值为“姓名”,输出当前模拟身证上的姓名;
    如果参数是身份证号,输出当前模拟的身份证号的号码。如果参数值是“住址”,输出当前身份证号上的住址。
    如果参数值为“性别”,输出当前模拟身证上的性别;
    如果参数值为“姓名”,输出当前模拟身证上的姓名;
    如果参数值为“住址”,输出当前模拟身证上的住址;
    如果参数值为“身份证”,按身份证格式输出当前模拟身证上的全部信息;
    如果参数值为“查询”,,要求用户输入一个要查询的人名,再输入一个单词做为匹配词,
    根据输入设置敏感地区,判定持证人是否为敏感地区常住人或是敏感地区出生者。。"""
    while txt := input():
        if txt in ['姓名', '性别', '公民身份号码', '住址']:
            print(id_info_dict[txt])
        elif txt == '身份证':
            print_id(id_info_dict)
        elif txt == '查询':
            city_name = input()  # 输入敏感城市名
            relation = check_city(city_name, id_info_dict)
            print(relation)
        else:
            print('输入错误')


def id_detail_csv(file, id_info_dict):
    """将身份证信息按指定的顺序排序后写入到文件中"""
    order = ['姓名', '性别', '出生', '民族', '公民身份号码', '住址', '出生省市', '出生地区', '出生县区', '出生区码', '居住省市', '居住地区', '居住县区']
    title = ','.join(order) + '\n'
    data_ls = [id_info_dict[key] for key in order]
    data = ','.join(data_ls) + '\n'
    with open(file, 'a+', encoding="utf-8") as f:
        f.seek(0)  # 指针移动文件开头,判断文件是否为空
        if f.readline()[:2] == '姓名':  # 读第一行,若开头文字是'姓名',则有数据,略过标题行
            f.seek(2)  # 指针移动文件末尾
            f.write(data)
        else:
            f.write(title)
            f.write(data)


def faker_id(n):
    """生成n个虚拟身份信息,写入到csv文件中"""
    filename = '模拟身份证.csv'
    for i in range(n):
        id_detail = generate_id_number()  # 生成一个身份证号存入字典
        id_detail = generate_information(id_detail)  # 为字典补充信息
        id_detail_csv(filename, id_detail)


def add_color(file):
    """读文件中的数据,为数据赋健康码,转为元素为字典的列表返回"""
    with open(file, 'r', encoding='utf-8') as fr:
        id_info = [line.strip().split(',') for line in fr]
    id_info[0].append('健康码')
    for x in id_info[1:]:
        color = random.choice(['red', 'green', 'yellow'])
        x.append(color)
    for i in range(1, len(id_info[1:]) + 1):
        id_info[i] = dict(zip(id_info[0], id_info[i]))
    print(id_info)
    return id_info[1:]


def make_qrcode(id_info):
    """为每一个人生成一个二维码"""
    path = './二维码/'
    for x in id_info:
        qr = qrcode.QRCode(
            version=None,  # 控制二维码的大小,程序自动确定
            error_correction=qrcode.constants.ERROR_CORRECT_M,  # 大约15%或更少的错误能被纠正。
            box_size=10,  # 控制二维码中每个小格子包含的像素数
            border=4,  # 控制边框(二维码与图片边界的距离)包含的格子数
        )
        qr.add_data(str(x))  # 给二维码添加数据
        qr.make(fit=True)  # 生成二维码
        # 设置二维码颜色,第一个是填充颜色 第二个是背景颜色
        img = qr.make_image(fill_color=x['健康码'], back_color="white")
        print(f"正在生成{x['姓名']}的二维码...")
        # img.show()  # 二维码展示
        img.save(path + x['姓名'] + '.png')  # 二维码保存


def show_qrcode(id_info):
    """查看一个人二维码"""
    id_number = input()
    qr = qrcode.QRCode()
    content = [x for x in id_info if x['公民身份号码'] == id_number]
    qr.add_data(str(content[0]))  # 给二维码添加数据
    qr.make(fit=True)  # 生成二维码
    # 设置二维码颜色,第一个是填充颜色 第二个是背景颜色
    img = qr.make_image(fill_color=content[0]['健康码'], back_color="white")
    img.show()  # 二维码展示


def check_area(id_info_dict):
    """输入一个省市,返回出生或居住在该省的人的数据"""
    city = input()
    id_info_city = [x for x in id_info_dict if city in [x['出生省市'], x['居住省市']]]
    return id_info_city

def migrate():
    """读文件中的数据,统计返回离开籍贯所在地的人数"""
    file = '模拟身份证.csv'
    native_place = input()  # 籍贯
    migrate_info = []
    with open(file, 'r', encoding='utf-8') as fr:
        for line in fr:
            x = line.strip().split(',')
            if x[6] == native_place and x[10] != native_place:
                migrate_info.append(x)
    print(migrate_info)
    return len(migrate_info)


def baidu_ocr(picfile):  #
    """利用百度AI提供的算API识别身份证上的信息。
    参数picfile为身份证图片文件名。
    """
    APP_ID = '25884589'                              # 用户自己创建的应用的appid
    API_KEY = 'FR2V5RuGw8q40SYUXFuII1MW'             # 用户自己创建的应用的appkey
    SECRET_KEY = 'DRO9c9Kiw31AuhFrPHtbMrd0IgkGpkqx'  # 用户自己创建的应用的secretkey
    client = AipOcr(APP_ID, API_KEY, SECRET_KEY)
    with open(picfile, 'rb') as id_info:
        img = id_info.read()
        """ 调用通用文字识别(高精度版) """
        message = client.basicAccurate(img)

    id_info = []  # 识别出的文本内容存入列表
    for text in message.get('words_result'):  # 识别的内容
        # print(text)  # 字典类型,键为word,值为识别的信息
        id_info.append(text.get('words'))  # 取值加入列表
    return id_info


import os
from aip import AipOcr


def baidu_ocr(picfile):  #
    """利用百度AI提供的算API识别身份证上的信息。
    参数picfile为身份证图片文件名。
    """
    APP_ID = '25884589'  # 用户自己创建的应用的appid id_info(ID:25884589)
    API_KEY = 'FR2V5RuGw8q40SYUXFuII1MW'  # 用户自己创建的应用的appkey=FR2V5RuGw8q40SYUXFuII1MW
    SECRET_KEY = 'DRO9c9Kiw31AuhFrPHtbMrd0IgkGpkqx'  # 用户自己创建的应用的secretkey=DRO9c9Kiw31AuhFrPHtbMrd0IgkGpkqx
    client = AipOcr(APP_ID, API_KEY, SECRET_KEY)
    with open(picfile, 'rb') as id_info:
        img = id_info.read()
        """ 调用通用文字识别(高精度版) """
        message = client.basicAccurate(img)
    # print(message)
    id_info = []  # 识别出的文本内容存入列表
    for text in message.get('words_result'):  # 识别的内容
        # print(text)  # 字典类型,键为word,值为识别的信息
        id_info.append(text.get('words'))  # 取值加入列表
    # print(id_info)
    return id_info


def filename_list(path):
    """接收路径字符串为参数,获取该路径下所有文件名,以列表形式返回
    os.listdir(path)以列表形式返回path路径下的所有文件名,不包括子路径中的文件名"""
    name_list = os.listdir(path)
    # print(name_list)
    return name_list


def write_to_file(info):
    with open('nucleic_acid_report.csv', 'a', encoding='utf-8') as fw:
        info_str = ','.join(info) + '\n'
        fw.write(info_str)


def result_to_file(file_list):
    for file in file_list:
        data = baidu_ocr(filepath + file)
        info = [data[i] for i in (6, 7, 10, 11, 12, 15, 17, 24, 43)]
        write_to_file(info)




if __name__ == '__main__':
    # random.seed()  # 随机数种子,不用于自动评测时注释掉此行
    id_detail = generate_id_number()  # 生成一个身份证号存入字典
    id_detail = generate_information(id_detail) # 为字典补充信息
    # print_id(id_detail)  # 打印身份证
    # judge(id_detail)
    # print(id_detail)
    # n = int(input())
    # faker_id(n)
    # filename = '模拟身份证.csv'
    # id_with_color = add_color(filename)
    # make_qrcode(id_with_color)
    # show_qrcode(id_with_color)
    # print(check_area(id_with_color))
    print(baidu_ocr('zghid.jpg'))
    print(f'离开籍贯地的人有{migrate()}人')
    filepath = './Nucleic Acid Test/'
    file_list = filename_list(filepath)
    result_to_file(file_list)