身份证信息批量处理系统:从入门到实战(附exe工具+核心源码)
在日常办公、数据统计或信息管理场景中,身份证信息处理是高频需求。不管是验证身份证有效性、提取出生日期,还是批量计算年龄、区分性别,手动操作不仅效率低,还容易出错。今天分享一款身份证信息批量处理系统,支持 exe 文件直接运行,人人都能轻松上手,同时开放核心源码,方便有需要的朋友学习参考。
身份证信息批量处理系统
一、工具核心功能
这款工具专为身份证信息处理设计,核心能力如下:
-
批量验证身份证有效性:自动检查号码长度、校验码、出生日期、行政区划代码等是否合规,清晰标注错误原因(如 "校验码错误""出生日期不合理")。
-
信息自动提取:从身份证号中批量提取出生日期、年龄、性别、所属省份,无需手动计算或查询。
-
自定义年龄计算:支持设置年龄计算的截止日期(比如按 "2023-12-31" 统计年龄),适配不同统计场景。
-
标准模板生成:自带 Excel 模板,只需填写 "姓名" 和 "身份证号",其他信息一键自动填充。
-
单个验证功能:支持临时查询单个身份证信息,即时返回验证结果和相关信息。
-
即开即用:无需安装 Python 环境,双击 exe 文件即可运行,适配各类电脑使用场景。
二、详细使用教程
1. 获取工具
工具已打包为可直接运行的 exe 程序,同时包含核心源码,下载后解压到电脑任意目录即可使用。
龙言龙论
包含文件:身份证信息批量处理系统.exe(可直接运行)、IDCardPro.py(核心源码)。
2. 操作步骤
步骤 1:创建标准模板
打开身份证信息批量处理系统.exe,会显示主菜单界面:
Plain
┌────────────────────────────────────────────┐
│ 身份证信息批量处理系统 (EXE版) v2.0 │
└────────────────────────────────────────────┘
当前年龄计算截止日期: 2024-05-20
程序目录: D:\IDCardTool
请选择操作:
┌────────────────────────────────────────────┐
│ 1. 创建模板文件 │
│ 2. 批量处理Excel文件 │
│ 3. 设置年龄计算截止日期 │
│ 4. 单个身份证验证 │
│ 5. 退出程序 │
└────────────────────────────────────────────┘
输入1并回车,可创建标准 Excel 模板(默认文件名:身份证批量处理模板.xlsx,也可自定义名称)。模板包含以下列:
-
序号(自动生成)、姓名(手动填写)、身份证号(手动填写)
-
出生日期、年龄、性别、省份(自动填充)
-
是否正确、错误原因(验证结果)
步骤 2:填写待处理数据
用 Excel 或 WPS 打开生成的模板,在 "姓名" 和 "身份证号" 列填写需要处理的数据(身份证号需为 18 位),示例如下:
| 序号 | 姓名 | 身份证号 | ... |
|---|---|---|---|
| 1 | 张三 | 110101199001011234 | ... |
| 2 | 李四 | 310101198505056789 | ... |
| 填写完成后务必关闭 Excel 文件(文件被占用会导致处理失败)。 |
步骤 3:批量处理数据
回到程序主菜单,输入2并回车,程序会自动查找当前目录下的 Excel 文件:
Plain
批量处理Excel文件
────────────────────────────────
正在查找Excel文件...
找到以下Excel文件:
────────────────────────────────
1. D:\IDCardTool\身份证批量处理模板.xlsx 12.5 KB 2024-05-20 10:30
0. 手动输入文件路径
请选择文件编号 (0-1):
输入文件对应的编号(如1),程序会自动处理并更新原文件,处理完成后会显示统计结果:
Plain
✓ 选择的文件: D:\IDCardTool\身份证批量处理模板.xlsx
✓ 找到以下列:
身份证号: C
出生日期: D
年龄: E
性别: F
省份: G
是否正确: H
错误原因: I
✓ 处理完成!
总处理数量: 2
有效身份证: 2
无效身份证: 0
年龄计算截止日期: 2024-05-20
结果已保存到原文件: D:\IDCardTool\身份证批量处理模板.xlsx
打开 Excel 文件,即可看到自动填充的完整结果。
步骤 4:自定义年龄截止日期(可选)
默认按当前日期计算年龄,如需按特定日期统计(如年底结算),在主菜单输入3并回车,按提示输入日期(格式:YYYY-MM-DD)即可,示例:
Plain
当前年龄计算截止日期: 2024-05-20
默认使用当前日期计算年龄,如需按特定日期计算,请输入日期
格式: YYYY-MM-DD (例如: 2023-12-31)
直接回车使用当前日期
请输入截止日期: 2023-12-31
步骤 5:单个身份证验证(临时查询)
如需查询单个身份证信息,主菜单输入4并回车,输入身份证号即可快速获取结果:
Plain
单个身份证验证
────────────────────────────────
请输入身份证号码: 110101199001011234
验证结果:
┌────────────────────────────────────────────┐
│ 身份证号: 110101199001011234 │
│ 是否有效: 是 │
│ 验证信息: 验证通过 │
│ 出生日期: 1990-01-01 │
│ 年龄: 33 │
│ 性别: 男 │
│ 省份: 北京市 │
└────────────────────────────────────────────┘
三、使用注意事项
-
文件占用问题:处理前必须关闭对应的 Excel 文件,否则会提示 "文件被占用",导致处理失败。
-
模板格式要求:建议使用程序生成的模板,不要手动修改列名(如将 "身份证号" 改为 "身份证"),否则程序可能无法识别目标列。
-
身份证格式规范:需填写 18 位身份证号,15 位老号码需先转换为 18 位后再处理。
-
路径字符规范 :程序目录或 Excel 文件名避免包含特殊字符(如
*、?、空格等),可能导致文件读取失败。 -
日志排查问题 :程序会在
logs文件夹生成日志文件,若出现错误可通过日志定位问题原因。 -
环境适配说明 :exe 版本无需安装 Python,但需电脑安装 Excel 或 WPS 以打开文件;若运行源码,需先安装
openpyxl库(执行命令:pip install openpyxl)。
四、核心源码解析
以下展示工具的核心逻辑代码,便于理解身份证验证和 Excel 处理的核心原理:
1. 身份证有效性验证核心函数
python
def validate_id_card(self, id_card):
"""验证身份证号码是否正确"""
# 长度校验
if not isinstance(id_card, str) or len(id_card) != 18:
return False, "长度错误"
# 前17位数字校验
if not id_card[:17].isdigit():
return False, "前17位包含非数字字符"
# 最后一位格式校验
if not (id_card[17].isdigit() or id_card[17].upper() == 'X'):
return False, "最后一位格式错误"
# 行政区划代码校验
province_code = id_card[:2]
if province_code not in self.province_codes:
return False, "行政区划代码错误"
# 出生日期校验
birth_date_str = id_card[6:14]
try:
birth_date = datetime.strptime(birth_date_str, '%Y%m%d').date()
if birth_date > date.today():
return False, "出生日期不合理"
except ValueError:
return False, "出生日期格式错误"
# 校验码计算验证
total = sum(int(id_card[i]) * self.weights[i] for i in range(17))
check_code_index = total % 11
expected_check_code = self.check_codes[check_code_index]
if id_card[17].upper() != expected_check_code:
return False, "校验码错误"
return True, "验证通过"
2. 年龄计算逻辑
python
def calculate_age(self, birth_date):
"""计算年龄,使用设定的截止日期"""
age = self.age_cutoff_date.year - birth_date.year
# 若截止日期在生日之前,年龄减1
if (self.age_cutoff_date.month, self.age_cutoff_date.day) < (birth_date.month, birth_date.day):
age -= 1
return age
3. Excel 批量处理核心逻辑
python
def process_excel_file(self, input_file):
"""处理Excel文件中的身份证号码"""
try:
if not os.path.exists(input_file):
print(f"❌ 文件不存在: {input_file}")
return False
# 读取Excel文件并定位身份证列
wb = openpyxl.load_workbook(input_file)
ws = wb.active
id_column = None
for cell in ws[1]:
if cell.value and '身份证' in str(cell.value):
id_column = cell.column
if not id_column:
print("❌ 未找到包含'身份证'的列名")
return False
# 遍历数据行处理
total_count = 0
valid_count = 0
for row in range(2, ws.max_row + 1):
id_cell = ws.cell(row=row, column=id_column)
if not id_cell.value or not str(id_cell.value).strip():
continue
id_card = str(id_cell.value).strip()
if len(id_card) != 18:
# 标记长度错误
continue
total_count += 1
is_valid, message = self.validate_id_card(id_card)
if is_valid:
# 提取并填充信息
birth_date = self.extract_birth_info(id_card)
age = self.calculate_age(birth_date)
gender = self.get_gender(id_card)
province = self.get_province(id_card)
# 填充到对应列(省略样式设置等细节)
valid_count += 1
else:
# 标记错误信息
pass
# 保存文件并输出统计结果
wb.save(input_file)
print(f"\n✓ 处理完成!总处理数量: {total_count} 有效身份证: {valid_count}")
return True
except Exception as e:
print(f"❌ 处理文件时出错: {e}")
return False
完整源码可通过网盘链接下载,包含路径处理、日志配置、界面交互、Excel 样式设置等全部功能逻辑。
五、总结
这款工具兼顾实用性和学习价值:普通用户可直接通过 exe 程序提升身份证信息处理效率,从事开发或学习的朋友可通过源码学习身份证验证算法、Excel 文件操作、Python 程序打包等技能。
龙言龙论
(包含 exe 可执行程序 + 完整源码,长期有效)
如果在使用过程中遇到问题,欢迎留言交流,共同优化和完善工具功能。
完整源码附上:
python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
身份证信息批量处理系统
ID Card Batch Processing System (Complete Fixed Version)
作者:龙论
时间:2025年11月26日
"""
import os
import sys
import traceback
import logging
from datetime import datetime, date
# ==================== 路径处理优化 ====================
def get_resource_path(relative_path):
"""获取资源文件的绝对路径,兼容开发环境和打包环境"""
try:
# PyInstaller创建临时文件夹,将路径存储在_MEIPASS中
base_path = sys._MEIPASS
except Exception:
base_path = os.path.abspath(".")
return os.path.join(base_path, relative_path)
def get_app_directory():
"""获取应用程序目录"""
if getattr(sys, 'frozen', False):
# 打包后的exe文件所在目录
return os.path.dirname(sys.executable)
else:
# 开发环境下的脚本目录
return os.path.dirname(os.path.abspath(__file__))
# ==================== 日志配置 ====================
def setup_logging():
"""配置日志系统"""
app_dir = get_app_directory()
log_dir = os.path.join(app_dir, "logs")
if not os.path.exists(log_dir):
os.makedirs(log_dir)
log_file = os.path.join(log_dir, f"idcard_system_{datetime.now().strftime('%Y%m%d_%H%M%S')}.log")
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler(log_file, encoding='utf-8'),
logging.StreamHandler()
]
)
return logging.getLogger(__name__)
logger = setup_logging()
# ==================== 依赖检查 ====================
def check_dependencies():
"""检查必要的依赖库"""
required_libraries = {
'openpyxl': 'openpyxl',
}
missing_libraries = []
for lib_name, import_name in required_libraries.items():
try:
__import__(import_name)
logger.info(f"✓ 找到库: {lib_name}")
except ImportError:
missing_libraries.append(lib_name)
logger.error(f"✗ 缺少库: {lib_name}")
if missing_libraries:
logger.error(f"缺少必要的库: {', '.join(missing_libraries)}")
print(f"错误:缺少必要的库: {', '.join(missing_libraries)}")
print("请安装缺失的库:")
for lib in missing_libraries:
print(f" pip install {lib}")
return False
return True
# ==================== 异常处理 ====================
def handle_exception(exc_type, exc_value, exc_traceback):
"""全局异常处理"""
if issubclass(exc_type, KeyboardInterrupt):
sys.__excepthook__(exc_type, exc_value, exc_traceback)
return
logger.critical("未捕获的异常:", exc_info=(exc_type, exc_value, exc_traceback))
print("\n" + "="*50)
print("程序发生错误!")
print("="*50)
print(f"错误类型: {exc_type.__name__}")
print(f"错误信息: {exc_value}")
print("\n请检查:")
print("1. Excel文件是否被其他程序打开")
print("2. 文件路径是否包含特殊字符")
print("3. 磁盘空间是否充足")
print("\n详细信息已保存到日志文件")
print("="*50)
input("\n按回车键退出...")
sys.excepthook = handle_exception
# ==================== 主程序导入 ====================
try:
import openpyxl
from openpyxl import Workbook
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
from openpyxl.comments import Comment
import glob
except ImportError as e:
logger.error(f"导入库失败: {e}")
print(f"导入库失败: {e}")
if not check_dependencies():
input("按回车键退出...")
sys.exit(1)
# ==================== 核心处理类 ====================
class IDCardProcessor:
"""身份证信息处理核心类"""
def __init__(self):
"""初始化"""
self.province_codes = {
'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': '新疆维吾尔自治区'
}
self.weights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
self.check_codes = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2']
self.age_cutoff_date = date.today()
def validate_id_card(self, id_card):
"""验证身份证号码是否正确"""
if not isinstance(id_card, str) or len(id_card) != 18:
return False, "长度错误"
# 检查前17位是否为数字
if not id_card[:17].isdigit():
return False, "前17位包含非数字字符"
# 检查最后一位是否为数字或X
if not (id_card[17].isdigit() or id_card[17].upper() == 'X'):
return False, "最后一位格式错误"
# 检查行政区划代码
province_code = id_card[:2]
if province_code not in self.province_codes:
return False, "行政区划代码错误"
# 检查出生日期
birth_date_str = id_card[6:14]
try:
birth_date = datetime.strptime(birth_date_str, '%Y%m%d').date()
# 检查日期是否合理(不能是未来日期)
if birth_date > date.today():
return False, "出生日期不合理"
except ValueError:
return False, "出生日期格式错误"
# 计算校验码
total = 0
for i in range(17):
total += int(id_card[i]) * self.weights[i]
check_code_index = total % 11
expected_check_code = self.check_codes[check_code_index]
if id_card[17].upper() != expected_check_code:
return False, "校验码错误"
return True, "验证通过"
def extract_birth_info(self, id_card):
"""提取出生日期信息"""
birth_date_str = id_card[6:14]
birth_date = datetime.strptime(birth_date_str, '%Y%m%d').date()
return birth_date
def calculate_age(self, birth_date):
"""计算年龄,使用设定的截止日期"""
age = self.age_cutoff_date.year - birth_date.year
# 如果截止日期在生日之前,年龄减1
if (self.age_cutoff_date.month, self.age_cutoff_date.day) < (birth_date.month, birth_date.day):
age -= 1
return age
def get_gender(self, id_card):
"""获取性别"""
gender_code = int(id_card[16:17])
return "男" if gender_code % 2 == 1 else "女"
def get_province(self, id_card):
"""获取省份信息"""
province_code = id_card[:2]
return self.province_codes.get(province_code, "未知")
def set_age_cutoff_date(self, cutoff_date_str):
"""设置年龄计算截止日期"""
try:
self.age_cutoff_date = datetime.strptime(cutoff_date_str, '%Y-%m-%d').date()
return True
except ValueError:
return False
def find_excel_files(self):
"""查找当前目录下的Excel文件"""
app_dir = get_app_directory()
excel_files = []
# 查找所有Excel文件
for pattern in ['*.xlsx', '*.xls']:
try:
files = glob.glob(os.path.join(app_dir, pattern))
excel_files.extend(files)
except Exception as e:
logger.warning(f"查找文件时出错 {pattern}: {e}")
# 过滤掉系统临时文件
excel_files = [f for f in excel_files if not os.path.basename(f).startswith('~')]
return sorted(excel_files)
def select_excel_file(self):
"""让用户选择Excel文件"""
excel_files = self.find_excel_files()
if not excel_files:
print("❌ 当前目录下未找到Excel文件")
print("请将Excel文件放在程序同一目录下,或使用完整路径")
return None
print("\n找到以下Excel文件:")
print("─" * 50)
for i, file in enumerate(excel_files, 1):
file_size = os.path.getsize(file)
file_time = datetime.fromtimestamp(os.path.getctime(file)).strftime('%Y-%m-%d %H:%M')
print(f" {i}. {file:<30} {file_size/1024:>6.1f} KB {file_time}")
print("─" * 50)
print(" 0. 手动输入文件路径")
while True:
try:
choice = input("\n请选择文件编号 (0-{}): ".format(len(excel_files)))
choice = int(choice)
if choice == 0:
file_path = input("请输入Excel文件完整路径: ").strip()
if os.path.exists(file_path):
return file_path
else:
print("❌ 文件不存在,请重新输入")
elif 1 <= choice <= len(excel_files):
return excel_files[choice-1]
else:
print("❌ 无效选择,请重新输入")
except ValueError:
print("❌ 请输入有效数字")
def create_template(self, filename="身份证批量处理模板.xlsx"):
"""创建模板文件"""
try:
# 使用应用程序目录
app_dir = get_app_directory()
filepath = os.path.join(app_dir, filename)
wb = Workbook()
ws = wb.active
ws.title = "身份证信息处理"
# 设置表头和对应的批注
headers_with_comments = [
('序号', ''),
('姓名', '请在此列填入姓名'),
('身份证号', '请在此列填入身份证号'),
('出生日期', '处理后将自动填充'),
('年龄', '处理后将自动计算'),
('性别', '处理后将自动判断'),
('省份', '处理后将自动识别'),
('是否正确', '处理后将自动验证'),
('错误原因', '如有错误将显示原因')
]
# 设置表头样式
header_font = Font(bold=True, color="FFFFFF", size=12)
header_fill = PatternFill(start_color="366092", end_color="366092", fill_type="solid")
border = Border(
left=Side(style='thin'),
right=Side(style='thin'),
top=Side(style='thin'),
bottom=Side(style='thin')
)
# 创建表头并添加批注
for col, (header, comment_text) in enumerate(headers_with_comments, 1):
cell = ws.cell(row=1, column=col, value=header)
cell.font = header_font
cell.fill = header_fill
cell.border = border
cell.alignment = Alignment(horizontal='center', vertical='center')
if comment_text:
cell.comment = Comment(comment_text, "系统提示")
cell.comment.width = 200
cell.comment.height = 50
# 设置列宽和行高
column_widths = [8, 12, 20, 12, 8, 8, 12, 10, 15]
for col, width in enumerate(column_widths, 1):
ws.column_dimensions[chr(64 + col)].width = width
for row in range(1, 100):
ws.row_dimensions[row].height = 25
# 保存文件
wb.save(filepath)
print(f"✓ 模板文件已创建: {filepath}")
print("请在模板的对应列填入数据,鼠标悬停在表头可查看填写说明。")
return True
except Exception as e:
logger.error(f"创建模板文件时出错: {e}")
print(f"❌ 创建模板文件时出错: {e}")
return False
def process_excel_file(self, input_file):
"""处理Excel文件中的身份证号码"""
try:
if not os.path.exists(input_file):
print(f"❌ 文件不存在: {input_file}")
return False
# 读取Excel文件
wb = openpyxl.load_workbook(input_file)
ws = wb.active
# 查找身份证号列和其他结果列
id_column = None
birth_column = None
age_column = None
gender_column = None
province_column = None
valid_column = None
error_column = None
header_row = 1
for cell in ws[1]:
if cell.value:
cell_value = str(cell.value)
if '身份证' in cell_value:
id_column = cell.column
elif '出生日期' in cell_value:
birth_column = cell.column
elif '年龄' in cell_value:
age_column = cell.column
elif '性别' in cell_value:
gender_column = cell.column
elif '省份' in cell_value:
province_column = cell.column
elif '是否正确' in cell_value:
valid_column = cell.column
elif '错误原因' in cell_value:
error_column = cell.column
if not id_column:
print("❌ 未找到包含'身份证'的列名")
return False
print("✓ 找到以下列:")
if id_column: print(f" 身份证号: {openpyxl.utils.get_column_letter(id_column)}")
if birth_column: print(f" 出生日期: {openpyxl.utils.get_column_letter(birth_column)}")
if age_column: print(f" 年龄: {openpyxl.utils.get_column_letter(age_column)}")
if gender_column: print(f" 性别: {openpyxl.utils.get_column_letter(gender_column)}")
if province_column: print(f" 省份: {openpyxl.utils.get_column_letter(province_column)}")
if valid_column: print(f" 是否正确: {openpyxl.utils.get_column_letter(valid_column)}")
if error_column: print(f" 错误原因: {openpyxl.utils.get_column_letter(error_column)}")
# 处理结果统计
total_count = 0
valid_count = 0
# 定义样式
valid_fill = PatternFill(start_color="C6EFCE", end_color="C6EFCE", fill_type="solid")
invalid_fill = PatternFill(start_color="FFC7CE", end_color="FFC7CE", fill_type="solid")
border = Border(
left=Side(style='thin'),
right=Side(style='thin'),
top=Side(style='thin'),
bottom=Side(style='thin')
)
center_align = Alignment(horizontal='center', vertical='center')
# 处理每一行数据
for row in range(header_row + 1, ws.max_row + 1):
id_cell = ws.cell(row=row, column=id_column)
if not id_cell.value or not str(id_cell.value).strip():
continue
id_card = str(id_cell.value).strip()
if len(id_card) != 18:
# 标记长度错误的身份证
if valid_column:
cell = ws.cell(row=row, column=valid_column, value="否")
cell.fill = invalid_fill
cell.border = border
cell.alignment = center_align
if error_column:
cell = ws.cell(row=row, column=error_column, value="身份证号长度不正确")
cell.border = border
cell.alignment = center_align
continue
total_count += 1
is_valid, message = self.validate_id_card(id_card)
# 填充结果
if is_valid:
birth_date = self.extract_birth_info(id_card)
age = self.calculate_age(birth_date)
gender = self.get_gender(id_card)
province = self.get_province(id_card)
if birth_column:
cell = ws.cell(row=row, column=birth_column, value=birth_date.strftime('%Y-%m-%d'))
cell.border = border
cell.alignment = center_align
if age_column:
cell = ws.cell(row=row, column=age_column, value=age)
cell.border = border
cell.alignment = center_align
if gender_column:
cell = ws.cell(row=row, column=gender_column, value=gender)
cell.border = border
cell.alignment = center_align
if province_column:
cell = ws.cell(row=row, column=province_column, value=province)
cell.border = border
cell.alignment = center_align
if valid_column:
cell = ws.cell(row=row, column=valid_column, value="是")
cell.fill = valid_fill
cell.border = border
cell.alignment = center_align
if error_column:
cell = ws.cell(row=row, column=error_column, value="")
cell.border = border
cell.alignment = center_align
valid_count += 1
else:
if birth_column:
cell = ws.cell(row=row, column=birth_column, value="")
cell.border = border
cell.alignment = center_align
if age_column:
cell = ws.cell(row=row, column=age_column, value="")
cell.border = border
cell.alignment = center_align
if gender_column:
cell = ws.cell(row=row, column=gender_column, value="")
cell.border = border
cell.alignment = center_align
if province_column:
cell = ws.cell(row=row, column=province_column, value="")
cell.border = border
cell.alignment = center_align
if valid_column:
cell = ws.cell(row=row, column=valid_column, value="否")
cell.fill = invalid_fill
cell.border = border
cell.alignment = center_align
if error_column:
cell = ws.cell(row=row, column=error_column, value=message)
cell.border = border
cell.alignment = center_align
# 设置统一的列宽和行高
column_widths = [8, 12, 20, 12, 8, 8, 12, 10, 15]
for col, width in enumerate(column_widths, 1):
if col <= ws.max_column: # 确保不超出实际列数
ws.column_dimensions[openpyxl.utils.get_column_letter(col)].width = width
for row in range(1, ws.max_row + 1):
ws.row_dimensions[row].height = 25
# 保存回原文件
wb.save(input_file)
print(f"\n✓ 处理完成!")
print(f" 总处理数量: {total_count}")
print(f" 有效身份证: {valid_count}")
print(f" 无效身份证: {total_count - valid_count}")
print(f" 年龄计算截止日期: {self.age_cutoff_date.strftime('%Y-%m-%d')}")
print(f" 结果已保存到原文件: {input_file}")
return True
except Exception as e:
logger.error(f"处理Excel文件时出错: {e}")
print(f"❌ 处理文件时出错: {e}")
return False
# ==================== 用户界面函数 ====================
def clear_screen():
"""清屏函数"""
try:
os.system('cls' if os.name == 'nt' else 'clear')
except:
print("\n" * 50)
def print_header():
"""打印程序标题"""
clear_screen()
print("┌" + "─" * 58 + "┐")
print("│" + " " * 58 + "│")
print("│ 身份证信息批量处理系统 (EXE版) v2.0 │")
print("│" + " " * 58 + "│")
print("└" + "─" * 58 + "┘")
print()
def print_menu():
"""打印主菜单"""
print("请选择操作:")
print("┌────────────────────────────────────────────┐")
print("│ 1. 创建模板文件 │")
print("│ 2. 批量处理Excel文件 │")
print("│ 3. 设置年龄计算截止日期 │")
print("│ 4. 单个身份证验证 │")
print("│ 5. 退出程序 │")
print("└────────────────────────────────────────────┘")
# ==================== 主函数 ====================
def main():
"""主函数"""
print("正在初始化程序...")
# 检查依赖
if not check_dependencies():
input("按回车键退出...")
return
# 显示启动信息
app_dir = get_app_directory()
print(f"程序目录: {app_dir}")
print("初始化完成!")
# 初始化处理器
processor = IDCardProcessor()
# 主循环
while True:
print_header()
print(f"当前年龄计算截止日期: {processor.age_cutoff_date.strftime('%Y-%m-%d')}")
print(f"程序目录: {app_dir}")
print()
print_menu()
try:
choice = input("\n请输入选项 (1-5): ").strip()
if choice == '1':
print_header()
print("创建模板文件")
print("─" * 40)
filename = input("请输入模板文件名(默认:身份证批量处理模板.xlsx): ").strip()
if not filename:
filename = "身份证批量处理模板.xlsx"
processor.create_template(filename)
input("\n按回车键继续...")
elif choice == '2':
print_header()
print("批量处理Excel文件")
print("─" * 40)
print("正在查找Excel文件...")
input_file = processor.select_excel_file()
if not input_file:
input("\n按回车键继续...")
continue
print(f"\n✓ 选择的文件: {input_file}")
processor.process_excel_file(input_file)
input("\n按回车键继续...")
elif choice == '3':
print_header()
print("当前年龄计算截止日期:", processor.age_cutoff_date.strftime('%Y-%m-%d'))
print("默认使用当前日期计算年龄,如需按特定日期计算,请输入日期")
print("格式: YYYY-MM-DD (例如: 2023-12-31)")
print("直接回车使用当前日期")
date_input = input("\n请输入截止日期: ").strip()
if date_input:
if processor.set_age_cutoff_date(date_input):
print(f"✓ 年龄计算截止日期已设置为: {processor.age_cutoff_date.strftime('%Y-%m-%d')}")
else:
print("❌ 日期格式错误,请使用 YYYY-MM-DD 格式")
else:
processor.age_cutoff_date = date.today()
print(f"✓ 年龄计算截止日期已重置为: {processor.age_cutoff_date.strftime('%Y-%m-%d')}")
input("\n按回车键继续...")
elif choice == '4':
print_header()
print("单个身份证验证")
print("─" * 40)
id_card = input("请输入身份证号码: ").strip()
is_valid, message = processor.validate_id_card(id_card)
print("\n验证结果:")
print("┌────────────────────────────────────────────┐")
print(f"│ 身份证号: {id_card:<30} │")
print(f"│ 是否有效: {'是' if is_valid else '否':<30} │")
print(f"│ 验证信息: {message:<30} │")
if is_valid:
birth_date = processor.extract_birth_info(id_card)
age = processor.calculate_age(birth_date)
gender = processor.get_gender(id_card)
province = processor.get_province(id_card)
print(f"│ 出生日期: {birth_date.strftime('%Y-%m-%d'):<30} │")
print(f"│ 年龄: {age:<30} │")
print(f"│ 性别: {gender:<30} │")
print(f"│ 省份: {province:<30} │")
print("└────────────────────────────────────────────┘")
input("\n按回车键继续...")
elif choice == '5':
print("\n感谢使用,再见!")
break
else:
print("❌ 无效选择,请重新输入!")
input("\n按回车键继续...")
except KeyboardInterrupt:
print("\n\n检测到中断信号,程序退出...")
break
except Exception as e:
logger.error(f"主循环错误: {e}")
print(f"❌ 发生错误: {e}")
input("\n按回车键继续...")
# ==================== 程序入口 ====================
if __name__ == "__main__":
try:
main()
except Exception as e:
logger.critical(f"程序崩溃: {e}")
print(f"程序发生严重错误: {e}")
print("请查看日志文件获取详细信息")
input("按回车键退出...")