身份证信息批量处理系统:从入门到实战(附exe工具+核心源码)

身份证信息批量处理系统:从入门到实战(附exe工具+核心源码)

在日常办公、数据统计或信息管理场景中,身份证信息处理是高频需求。不管是验证身份证有效性、提取出生日期,还是批量计算年龄、区分性别,手动操作不仅效率低,还容易出错。今天分享一款身份证信息批量处理系统,支持 exe 文件直接运行,人人都能轻松上手,同时开放核心源码,方便有需要的朋友学习参考。

身份证信息批量处理系统

一、工具核心功能

这款工具专为身份证信息处理设计,核心能力如下:

  1. 批量验证身份证有效性:自动检查号码长度、校验码、出生日期、行政区划代码等是否合规,清晰标注错误原因(如 "校验码错误""出生日期不合理")。

  2. 信息自动提取:从身份证号中批量提取出生日期、年龄、性别、所属省份,无需手动计算或查询。

  3. 自定义年龄计算:支持设置年龄计算的截止日期(比如按 "2023-12-31" 统计年龄),适配不同统计场景。

  4. 标准模板生成:自带 Excel 模板,只需填写 "姓名" 和 "身份证号",其他信息一键自动填充。

  5. 单个验证功能:支持临时查询单个身份证信息,即时返回验证结果和相关信息。

  6. 即开即用:无需安装 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                                  │
│  性别: 男                                  │
│  省份: 北京市                              │
└────────────────────────────────────────────┘

三、使用注意事项

  1. 文件占用问题:处理前必须关闭对应的 Excel 文件,否则会提示 "文件被占用",导致处理失败。

  2. 模板格式要求:建议使用程序生成的模板,不要手动修改列名(如将 "身份证号" 改为 "身份证"),否则程序可能无法识别目标列。

  3. 身份证格式规范:需填写 18 位身份证号,15 位老号码需先转换为 18 位后再处理。

  4. 路径字符规范 :程序目录或 Excel 文件名避免包含特殊字符(如*?、空格等),可能导致文件读取失败。

  5. 日志排查问题 :程序会在logs文件夹生成日志文件,若出现错误可通过日志定位问题原因。

  6. 环境适配说明 :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("按回车键退出...")
相关推荐
m0_626535201 小时前
代码分析 长音频分割为短音频
javascript·python·音视频
Wpa.wk1 小时前
自动化测试环境配置-java+python
java·开发语言·python·测试工具·自动化
带刺的坐椅2 小时前
AI 应用工作流:LangGraph 和 Solon AI Flow,我该选谁?
java·python·ai·solon·flow·langgraph
北郭guo2 小时前
MyBatis框架讲解,工作原理、核心内容、如何实现【从浅入深】让你看完这篇文档对于MyBatis的理解更加深入
java·数据库·mybatis
工业互联网专业2 小时前
图片推荐系统_django+spider
python·django·毕业设计·源码·课程设计·spider·图片推荐系统
小满、2 小时前
MySQL :锁机制、InnoDB 架构与 MVCC 解析
数据库·mysql·innodb·mvcc·锁机制
Lwcah2 小时前
Python | LGBM+SHAP可解释性分析回归预测及可视化算法
python·算法·回归
@一辈子爱你2 小时前
归来九十余日:在时代的夹缝中,与你共筑一道光
python
AI2中文网3 小时前
AppInventor2 使用 SQLite(三)带条件过滤查询表数据
数据库·sql·sqlite·select·app inventor 2·appinventor·tableview