用python读取身份证信息的功能分析与实现,兼述python调用dll的方法

背景

有这样一个需求,要求能自动读取用户的身份证信息。如果是一代身份证,这个功能恐怕只能通过图像识别的办法来解决了。不过现在二代身份证已经很普及。客户要求能读二代身份证就可以了。
现在二代身份证阅读器很常见,本文中使用的是新中新的身份证阅读器。下图是其外观,如果有朋友也是用的这个型号,则此处提供的代码不加修改就可以运行了。
用python读取身份证信息的功能分析与实现,兼述python调用dll的方法_第1张图片
厂家提供了sdk。由于要在windows上使用,就下载了最新的dll。如此一来,就变成了一个用python调用dll的问题。由于要传结构体给dll,所以在调用时还是有些点要注意到。

具体实现

身份证阅读器所需的dll不只一个,都放在一个目录下,如下图所示:
用python读取身份证信息的功能分析与实现,兼述python调用dll的方法_第2张图片
其中python需要调用的入口dll是SynIDCardAPI.dll。而这个dll又调用了其它的dll。这样就带来了一个问题,由于多个dll之间有依赖关系,不能简单地通过绝对路径加载入口dll。现在的解决办法,在python中临时切换当前工作目录。dll加载完成后再切换回所需的目录。相关代码如下所示:

#当前工作目录
oldCwd = os.getcwd()
#不要将本程序放置于中文目录下,可能会导致os.getcwd()异常

#print('oldCwd:',oldCwd)
#将工作目录切换到当前文件所在的目录
os.chdir(os.path.dirname(os.path.abspath(__file__))) 
newCwd = os.getcwd()
#print('newCwd:',newCwd)

#为了加载dll,临时切换到dll所在目录,
#由于多个dll之间有依赖关系,不能简单地通过路径加载其中一个文件
dllPath = newCwd +'\\dll'
os.chdir(dllPath)
dll = windll.LoadLibrary("SynIDCardAPI.dll")

#dll加载完成后将工作目录切换回原来的位置
os.chdir(oldCwd)

这其中的一个技巧就是,事先是知道正在执行的python文件与dll的相对路径关系的。通过os.path.dirname(os.path.abspath(file))可以获取正在执行的python文件所在的目录。然后可以调用os.chdir改变当前工作目录。

要加载dll其实很好办,网上很多文献都说到了,就是用dll = windll.LoadLibrary(“SynIDCardAPI.dll”)。
这里比较麻烦的一点是如何将结构体数据传给dll中的方法。现在采用的办法是用python中的class来模拟struct的功能,相关代码如下:

class IDCardData(Structure):  
    _fields_ = [ 
    		("Name", c_char * 32),  
            ("Sex", c_char * 6),
            ("Nation", c_char * 20),
            ("Born", c_char * 18),
            ("Address", c_char * 72),
            ("IDCardNo", c_char * 38),
            ("GrantDept", c_char * 32),
            ("UserLifeBegin", c_char * 18),
            ("UserLifeEnd", c_char * 18),
            ("reserved", c_char * 38),
            ("PhotoFileName", c_char * 255)] 

要做好这一步,需要仔细分析阅读器的开发文档与示例代码,如果是不同厂家的阅读器,这里可能要做相应的调整。

完整的代码如下,补充说明一下,为了与其它程序配合,代码中还设计了mock功能,也就是说在不实际连接身份证阅读器时,可以产生模拟数据。实际证明,这个办法在测试及与其它功能模块联调时是个好主意。

完整代码

#coding=utf-8
#读取身份证信息
import time
import random
import ConfigParser
import os
from ctypes import * 
import sys
sys.path.append('../')

#当前工作目录
oldCwd = os.getcwd()
#不要将本程序放置于中文目录下,可能会导致os.getcwd()异常

#print('oldCwd:',oldCwd)
#将工作目录切换到当前文件所在的目录
os.chdir(os.path.dirname(os.path.abspath(__file__))) 
newCwd = os.getcwd()
#print('newCwd:',newCwd)

#为了加载dll,临时切换到dll所在目录,
#由于多个dll之间有依赖关系,不能简单地通过路径加载其中一个文件
dllPath = newCwd +'\\dll'
os.chdir(dllPath)
dll = windll.LoadLibrary("SynIDCardAPI.dll")

#dll加载完成后将工作目录切换回原来的位置
os.chdir(oldCwd)

#idCard存放数据的结构体定义,python中用类来实现
class IDCardData(Structure):  
    _fields_ = [ 
    		("Name", c_char * 32),  
            ("Sex", c_char * 6),
            ("Nation", c_char * 20),
            ("Born", c_char * 18),
            ("Address", c_char * 72),
            ("IDCardNo", c_char * 38),
            ("GrantDept", c_char * 32),
            ("UserLifeBegin", c_char * 18),
            ("UserLifeEnd", c_char * 18),
            ("reserved", c_char * 38),
            ("PhotoFileName", c_char * 255)] 

def read(idCardMockdata):
	if idCardMockdata == 'yes':
		return readMockData()
	else:
		return readRealData()

#从身份证读卡器读取数据
def readRealData():
	cardData = IDCardData()  
	nPort= dll.Syn_FindReader()
	dll.Syn_SetPhotoType(4)
	dll.Syn_SetSexType(1)
	pucIIN = create_string_buffer('/0'*4)
	pucSN = create_string_buffer('/0'*8)
	if dll.Syn_OpenPort(nPort) == 0:
		if dll.Syn_SetMaxRFByte(nPort,80,0) == 0:
			nRet = dll.Syn_StartFindIDCard(nPort, byref(pucIIN), 0)
			nRet = dll.Syn_SelectIDCard(nPort, byref(pucSN), 0)
			nRet = dll.Syn_ReadMsg(nPort, 0,byref(cardData))
			if nRet == 0:
				return cardData.IDCardNo
	return ''

#读取模拟数据,返回400以内的一个随机整数
def readMockData():	
	cardData = IDCardData()  
	n =random.random()
	if(n>0.5):
		n =int(120*n)		
		print('idCard random index:',n)
		print('customer',customer)
		if customer: 
			cardData.IDCardNo = customer['idCard']
		else:
			time.sleep(1)
			cardData.IDCardNo =''
	else:
		time.sleep(1)
		cardData.IDCardNo =''
	return cardData.IDCardNo	

		

你可能感兴趣的:(python,编程语言)