在使用dll钩子注入目标程序获取数据后,得到存放数据的文本文件
文章目录
- 1. python调用exe
- 1.1 直接使用os.system( )来调用
- 1.2 使用subprocess.call()
- 1.3 编码问题
- 2.判断是否获取到了数据文件以及数据处理
- 2.1 判断文件是否存在
- 2.2 含有中文的list类型进行转置
- 2.3 字符串自动判断是转为整型还是浮点型
- 3. excel的问题
- 3.1 excel精确匹配值的问题
- 3.1.1 使用LookAt参数
- 3.1.2 奇怪的现象LookAt参数生效问题
- 3.1.3 关闭excel的问题
- 3.1.3.1 Workbook.Close method (Excel)
- 3.1.3.2 Application.Quit method (Excel)
- 3.1.4 上述打开excel方式不同带来的相关衍生错误
- 3.2 excel改变值的颜色
- 3.2.1 使用RGB值
- 3.2.2 使用预定义的一些颜色常量值
- 3.3 关于nan值
- 3.5 合并单元格值的问题
- 3.7 搜索结果range最后一行是65536
- 3.8 excel显示打印线的问题
- 3.9 pywin32给cell加边框border问题
- 3.9.1 参考文档/属性说明
- 3.9.2 代码
- 3.9.3 线型问题
- 3.10 合并单元格并写入值
- 3.11 合并值相同的单元格
- 3.11.1 说明/相关参考
- 3.11.2 代码
- 3.12 excel列号字母转数字
- 4. pyinstaller打包问题
- 尝试1
- 尝试2 成功解决
- 5.交互过程中的问题
1. python调用exe
1.1 直接使用os.system( )来调用
参考:在python中实现调用可执行文件.exe的3种方法
直接搬运过来好了
import os
main = "project1.exe"
r_v = os.system(main)
print (r_v )
1.2 使用subprocess.call()
虽然这个会有返回值,但是是这个函数的返回值,用来判断call的内容执行是否正确,也不是获取call的内容的返回值,不是我要的,算了
1.3 编码问题
出现的问题,由于我的.exe
程序是C++写得,使用的编码默认是 UNICODE字符集
,调试时,.exe
输出在控制台上的内容是可以正确显示为中文的,查看了控制台的编码为:当前代码页 936(ANSI/OEM-简体中文GBK)
搜索资料,根据以下:
- 中文字符集编码Unicode ,gb2312 , cp936 ,GBK,GB18030
- python爬虫解决gbk乱码问题
- Visual Studio中C++关于Unicode字符集和多字节字符集
可知:反正我还是没解决,头大的编码,哈哈哈
2.判断是否获取到了数据文件以及数据处理
2.1 判断文件是否存在
参考 Python判断文件是否存在的三种方法
import os
filepath="D:/haha.txt"
if os.path.exists(filepath) and os.path.isfile(filepath):
print("存在这个目录,且是文件 不是文件夹")
使用os.path.exists()
方法,判断文件和文件夹是一样,所以后面还需要使用os.path.isfile()
来判断是一个文件,而不是一个文件夹
2.2 含有中文的list类型进行转置
由于每行数据是 行号,列号,该行列对应的cell的值
,所以需要把这个进行处理(已知12列,所以要把读取的这个大列表进行转置,变成一个59*12的二维列表)
由于numpy的reshape只针对于矩阵,而矩阵中的元素必须都是数字类型,我这个数据含有字符,所以只能手写一个转换,不能调用现成的函数了。(现成的也有,但是看起来很麻烦的样子)
dflist = df['value'].tolist()
cvList = []
for index in range(2, len(dflist) // 12):
singelList = []
for i in range(3, 12):
singelList.append(dflist[index * 12 + i])
cvList.append(singelList)
# 为了方便后续搜索key 某些数据类型要改变 所以把二维列表变成一个类似实验1的json dict列表形式
dictList = []
for i in cvList:
rowDict = {}
rowDict['LC'] = i[0]
rowDict['BCFH'] = eval(i[1])
rowDict['YGBC'] = eval(i[2])
rowDict['YGSJ'] = float(i[3]) # 对nan使用eval会报错
rowDict['WGBC'] = eval(i[4])
rowDict['WGSJ'] = float(i[5])
rowDict['JZD'] = eval(i[6])
rowDict['EDEC'] = i[7]
rowDict['GLYS'] = float(i[8])
dictList.append(rowDict)
2.3 字符串自动判断是转为整型还是浮点型
使用 eval
函数
则不难明白:当传入一个字符串的时候,eval函数作用就是把这个字符串还原成它本身或者是能够转化成的数据类型。因此会自动转为对应的int或者float
3. excel的问题
3.1 excel精确匹配值的问题
3.1.1 使用LookAt参数
这里涉及到一个 搜索问题,Excel里直接的find其实是类似正则表达式匹配的。
比如这里,我搜索5
,搜索范围中的数据都是常规
类型,返回的其实更像是字符串包含的内容。
因此,搜索 excel 精准匹配 VBA
根据:[求助] 如何精确匹配查找
转到 find函数的参数说明及表格非空最大行号实例-中文
锁定 LookAt
参数,去MSDN官网关于Find函数的说明
进一步,点击详情页(python调用win32识别不了关键字,只能使用对应的数值)
# 代码添加 LookAt=1 参数即可
BCFHRange = ws.Range(ws.Cells(34, 2), ws.Cells(82, 2)).Find(BCFH, LookAt=1)
3.1.2 奇怪的现象LookAt参数生效问题
-
以
win32.gencache.EnsureDispatch('Excel.Application')
这种方式打开excel,会提前运行gen_py来产生一些相关的文档,使得程序知道这些对象有什么方法和属性,在这种方式下,LookAt参数有效,确实是精确查找。但是单纯使用这个方式,没法达到每次运行,重启一个excel实例的目的,会把用户当前打开的excel文件关掉。 -
以
win32.DispatchEx("excel.Application")
这种方式打开,则可以重启一个新的excel实例。但是LookAt参数无效 -
故,需要使用方法1中的方式来产生一些文件缓存(让程序知道对象的方法和属性),然后再使用2中的方式来进行真正的excel调用。 这里有一个地方需要注意:
这里的
Dispatch
只是构建了一个excel对象,但是并没有打开表格,打开表格的操作是创建excel对象之后使用excel.Workbooks.Open(template_excel)
引起的。所以即便先调用win32.gencache.EnsureDispatch('Excel.Application')
,只要不是这个excel对象执行excel.Workbooks.Open(template_excel)
操作,就不会关闭用户的excel表格。 故执行1之后执行2创建excel的方式,再执行excel.Workbooks.Open(template_excel)
就不仅可以使LookAt属性生效,还可以以新建一个excel实例的方式运行excel。疑惑的地方:一开始总觉得,需要先关闭excel=
win32.gencache.EnsureDispatch('Excel.Application')
这句创建的excel对象,然后再创建excel = win32.DispatchEx("excel.Application")
,虽然同名可以覆盖。搜索后,发现只有
Application.Quit method (Excel)
和Workbook.Close method (Excel)
这两个相关的内容。查看3.1.3关于这两个函数的说明,如果在方式1后面加上excel.Quit(),则用户的工作表界面也会被关闭,所以就算啦,不关就好了。操作完也不关了,因为不清楚用户是不是在使用excel。(反正是后台,应该没什么大问题)
def open_excel():
try:
# 以这种方式打开excel,会提前运行gen_py来产生一些相关的文档,使得程序知道这些对象有什么方法和属性,在这种方式下,LookAt参数有效,确实是精确查找
try:
excel = win32.gencache.EnsureDispatch('Excel.Application')
except AttributeError:
import shutil
import sys
MODULE_LIST = [m.__name__ for m in sys.modules.values()]
for module in MODULE_LIST:
if re.match(r'win32com\.gen_py\..+', module):
del sys.modules[module]
shutil.rmtree(os.path.join(os.environ.get('LOCALAPPDATA'), 'Temp', 'gen_py'))
excel = win32.gencache.EnsureDispatch('Excel.Application')
# 这种方式打开
excel = win32.DispatchEx("excel.Application")
excel.Visible = False # True 打开excel界面
excel.DisplayAlerts = 0 # 不弹窗显示警告信息 覆盖同名文件时不弹出确认框(但是无法打开同名文件时会出错 这个不属于弹窗警告信息)
except Exception as e:
print('ERROR 05: excel打开错误:', e)
else:
return excel
3.1.3 关闭excel的问题
3.1.3.1 Workbook.Close method (Excel)
MSDN-Workbook.Close method (Excel)
代码demo
wb = excel.Workbooks.Open(template_excel)
wb.SaveAs(save_path) # 另存为 路径是绝对路径
wb.Close(False) # 关闭该文件,并保存。
函数说明
用于关闭工作表Workbook对象
expression.Close (SaveChanges, FileName, RouteWorkbook)
参数说明
"SaveChanges": 如果没有对工作表进行修改,这个参数会被忽略。如果对这个工作簿进行了修改但是这个工作簿显示在其他窗口,这个参数也会被忽略。如果工作簿被修改且没有呈现在其他窗口,则这个参数表明是否要保存修改。True就是保存对这个工作表的修改。False就是虽然我改了,但是我不保存这个修改。
比如:我打开了一个模板文件,修改后,另存为一个文件,但是模板本身我不希望改变,则使用False。
此外,如果这个工作表暂时没有名称(也就是说这个工作表没有被保存过,还是默认的 类似"工作簿1")这样的名称,则使用"FileName"参数,如果这个参数被省略了,则需要要求用户提供一个文件名
"FileName":使用这个文件名来保存文件
"RouteWorkbook":用不到,不管了
3.1.3.2 Application.Quit method (Excel)
MSDN-Application.Quit method (Excel)
函数说明
:退出excel程序(这条语句相当于excel右上角的关闭)
A.Quit
A变量表示一个Application对象
如果使用这个方法的时候有未保存的工作表,则Excel会显示一个对话框询问是否要保存修改,如果不想让Excel显示这个对话框,则最好在使用Quit
方法之前保存所有的工作表,或者设置DisplayAlerts
属性为False
,这样Excel就不会展示那些提示框了(无论什么情况下),但是当你没有保存修改就离开程序时,这些修改就丢失了。。。
如果之前设置过某个工作表的Saved
属性是True
(已保存),则不会将工作簿保存到硬盘,Excel会直接退出,而不会再询问是否要保存
3.1.4 上述打开excel方式不同带来的相关衍生错误
错误信息,This COM object can not automate the makepy process - please run makepy manually for this object
有时如果打开了 代码中要访问的excel文件,就会报这个错误。
解决方案,参考python 报错 This COM object can not automate the makepy process - please run makepy manually for this
其实就是不要用 win32.gencache.EnsureDispatch('Excel.Application')
这个方式打开,改成下面那种就好win32.DispatchEx("excel.Application")
说白了,还是因为自己提前打开了程序要访问的excelw文件。
3.2 excel改变值的颜色
使用的是pywin32这个包,所以和VBA直接搜索的结果有一定出入,
根据:Excel如何用VBA更改字体颜色
3.2.1 使用RGB值
考虑到python,根据:Setting a cell’s fill RGB color with pywin32 in excel?
采用这一方式来改变某个单元格中字体的颜色(值的颜色,区别于填充色)
def rgbToInt(rgb):
colorInt = rgb[0] + (rgb[1] * 256) + (rgb[2] * 256 * 256)
return colorInt
ws.Cells(row,col).Font.Color = rgbToInt((255,255,128))
3.2.2 使用预定义的一些颜色常量值
还是想找更简单的方法,比如直接有颜色值可以使用的那种,根据MSDN-Font.Color property (Excel) →→ RGB function→→Color constants
在颜色常量中看到了常见颜色的 Value
这样,对于我的需求,把某些字体变红,就可以直接使用这个颜色对应的Value
来表示了
ws.Range('H' + str(SJZRow)).Font.Color = 0xFF # 红色
# 效果和调用上面函数 传递 RGB值(255,0,0)是一样的
3.3 关于nan值
从软件中获取的数据,有些没有值就会返回nan
,但是发现excel会将nan
值转为65535
,搜索后发现,根据 微软社区问答-Microsoft.Office.Interop.Excel Range.set_value converts NaN to 65535:大意就是这个问题从Excel2007就有了,但是没法解决,所以要自己写个方法处理下,把那些nan变得65535变成空的。
ws.Range('F' + str(SJZRow)).Value = '/' if math.isnan(YGSJ) else YGSJ
# 最简单的方式就是使用这种 类似三元表达式的 if else 然后使用math.isnan来判断
3.5 合并单元格值的问题
合并单元格的英语应该是MergeCells
,搜索对应的 MSDN官方文档-Range.MergeCells property (Excel)
参考:
不难得出:如果要获取下图的这个合并单元格里的值
# 先判断是不是合并单元格
if(ws.Range("B11").MergeCells):
# 再获取值/修改值
ws.Range("B11").MergeArea.Cells(1,1).Value = "额定电流:" + item["EDEC"]
3.7 搜索结果range最后一行是65536
很奇怪,虽然不影响代码,也可以根据项目的具体情况直接取代掉。
返回结果:
对应的excel表
代码:
BCFHRange = ws.Range(ws.Cells(34, 2), ws.Cells(85, 2)).Find(BCFH, LookAt=1)
print(BCFHRange.Row, BCFHRange.End(-4121).Row-1, BCFH)
3.8 excel显示打印线的问题
不小心手贱(项目甲方对excel报告打印效果有一定的效果),改模板的时候不小心点了打印,出现了打印线。参考:百度经验https://jingyan.baidu/article/a501d80c49d471ec620f5e45.html
主要就是 选项
->高级
,然后找找下面选项,去掉就好了
3.9 pywin32给cell加边框border问题
3.9.1 参考文档/属性说明
参考:stack-overflow_python win32com excel border formatting
这里要注意一个问题,你是要给
- 一个范围Range内的每个cell都加边框(上下左右)
- excel里的实现是选中范围,然后使用
所有框线
下图左
- excel里的实现是选中范围,然后使用
- 还是要给某个范围Range最外层加边框
- excel里的实现是选中范围,然后使用
外侧框线
下图右
- excel里的实现是选中范围,然后使用
区别如下:
参考官方:
-
MSDN-Borders object (Excel)
MSDN给出的VB的例子 Worksheets(1).Range("A1").Borders.LineStyle = xlDouble Worksheets("Sheet1").Range("A1:G1"). _ Borders(xlEdgeBottom).Color = RGB(255, 0, 0)
- 这里注意区别,如果只有一个单元格,Range里只有一个单元格,调用的其实是 Range对象的Borders属性,参见:MSDN-Range.Borders property (Excel)
- 如果是一个Range范围,而不是单个的单元格,则需要使用Border object来进行使用,也就是代码上面那个链接的部分。
- 这里注意一下 Borders对象里的参数,是
XlBordersIndex
常量,其可以为以下值xlDiagonalDown。xlDiagonalUp, xlEdgeBottom, xlEdgeLeft,xlEdgeRight,xlEdgeTop,xlInsideHorizontal,xlInsideVertical。 在python里使用需要找到对应的数值,XlBordersIndex enumeration (Excel)
xlDiagnoalDown 5
从左上角到右下角范围内的所有单元格。其实是斜线xlDiagonalUp
这个也是遍历范围内的所有单元格,`xlEdgeBottom
,xlEdgeLeft
,xlEdgeRight
,xlEdgeTop
这四个加一起就是外侧框线
(序号刚好是 7 8 9 10)xlInsideHorizontal
、xlInsideVertical
这两个加一起就是内部框线
- 7 8 9 10 11 12 加起来就是一个Rang中的
所有框线
- 这里注意一下 Borders对象里的参数,是
- 印象里直接选择边框后就会有个默认样式(如果需要调整的话,根据下图, 关于border设置相关的属性就是
Color
LineStyle
Weight
这三个了,可以看一下)
3.9.2 代码
我的需求是右边(给范围加外侧框线 最直接的选择 外侧框线 之后的效果)
# 左侧外边框
ws.Range("B13:I" + str(LineFlag-1)).Borders(7).Color = 0x0
ws.Range("B13:I" + str(LineFlag-1)).Borders(7).LineStyle = 1
ws.Range("B13:I" + str(LineFlag-1)).Borders(7).Weight = 2
# 下侧外边框
ws.Range("B13:I" + str(LineFlag - 1)).Borders(9).Color = 0x0
ws.Range("B13:I" + str(LineFlag - 1)).Borders(9).LineStyle = 1
ws.Range("B13:I" + str(LineFlag - 1)).Borders(9).Weight = 2
# 右侧外边框
ws.Range("B13:I" + str(LineFlag - 1)).Borders(10).Color = 0x0
ws.Range("B13:I" + str(LineFlag - 1)).Borders(10).LineStyle = 1
ws.Range("B13:I" + str(LineFlag - 1)).Borders(10).Weight = 2
- 关于颜色
- 参考本文 3.2部分,3.2.2 预定义的颜色常量值
- 关于线宽
3.9.3 线型问题
ws.Range("B12:I" + str(LineFlag-1)).Borders(7).LineStyle = 1
# .Weight= 1 # 1 Hairline (thinnest border).
按照文档 应该是 LineStyle = 1 Continuous line
应该就是连续实线,但是得到的却实点线??(结合代码实际效果,初步判断,使用 实线 但是出现的却是 点 的原因是 weight值为1 最细的线,改为 2 Thin
. 就正常了 验证正确)
LineStyle = -4115 Dashed line.
出来的效果也是下面这样LineStyle =4 Alternating dashes and dots.
同上LineStyle =5 Dash followed by two dots.
同上LineStyle =-4118 Dotted line.
LineStyle =-4119 Double line.
LineStyle =13 Slanted dashes.
注意:当weight=1时,所有的线型设置都会表现为如下,基本失效,当weight-2时,就正常了,其他值还没验证过,但是应该没问题
3.10 合并单元格并写入值
实验2电压部分的内容修改了,没有明确的excel模板了,有一部分要自己代码写入。
类似:
把一个范围内的单元格合并后,写入一些文字(还有个合并后居中的要求)
ws.Range("A" + str(LineFlag)+":I" + str(LineFlag)).Merge() #合并
ws.Range("A" + str(LineFlag)+":I" + str(LineFlag)).Value = "校准内容结束" #写入内容
ws.Range("A" + str(LineFlag)+":I" + str(LineFlag)).HorizontalAlignment=-4108
- 合并单元格,主要使用了 MSDN-Range.Merge method (Excel)
- 写入值,直接就是Value(也是Range对象的一个属性)
- 居中,根据MSDN官方论坛-Excel automation - merge and center cells:
锁定搜索对象是:Range对象的 HorizonAlignment属性
(或者VerticalAlignment
属性,我用不到),相关的链接是:Range.HorizontalAlignment property (Excel):
3.11 合并值相同的单元格
3.11.1 说明/相关参考
和上面的情况不太一样,这次的是要把某几列中上下相同的值(列方向上)进行合并
似乎没有找到可以直接实现这个功能的函数,参考相关的VBA实现内容:
- 使用VBA 合并相同单元格
- [已解决] 如何用VBA实现自动把相同的数值的单元格合并?
- 360图书馆-Excel VBA-批量合并相同值单元格
- 百度经验-excel VBA快速合并相同内容的单元格
- 主要参考-How To Quickly Merge Adjacent Rows With Same Data In Excel?
自己的代码(不一定是最好的,性能最高的,但是一定满足需求。。)
上述网页中都提到了一个问题,就是合并单元格的时候会提示
虽然之前设置过这个,但是不确定面对上述问题会是怎么样
excel.DisplayAlerts = 0
# 不弹窗显示警告信息 覆盖同名文件时不弹出确认框(但是无法打开同名文件时会出错 这个不属于弹窗警告信息)
3.11.2 代码
下面代码以一列为例(因为实际的需求就是几列需要进行合并单元格操作,不是某个连续范围的Range,而是分离的几个Column,写成函数形式好了 PS:不是对一个表里所有列操作,只对传入列号的列进行合并操作)
# 其实是一个二重循环,一个指向要基准值的cell,一个不断向下搜索新的值并判断是否相等,进而考虑是否要合并
def mergeColumn(self, worksheet, index, stopLine):
"""
合并某列中数值相同的单元格(列方向)
:param worksheet: 工作表对象
:param index: 列的序号 A B C D 这样的
:return: None(直接对excel表的操作)
"""
colRng = worksheet.Range(index + "13:" + index + str(stopLine))
# print("区域范围",colRng.Address) # 范围正确
xRows = colRng.Rows.Count # 获取行数 为循环做准备
# print("行数",xRows)
# 注意 已经使用Rng.Cells来获取数字了,
# 里面的row和col就是在当前Rng范围里的序号了(不按照整张工作簿的了 按照相对于当前Rng的序号)
i=0
j=0
# i的范围是colRng的第一到 倒数第二 最后一个值是j比较的 i不用取到
# (正常i需要的值是 1到xRows 因为最后一个数不用取 但是range(a,b)取[a,b-1],所以i的取值和j就是下面这样了 )
for i in range(1,xRows+1):
for j in range(i+1,xRows+2):
# 判断值相等且不为空
if (colRng.Cells(i,1).Value!=colRng.Cells(j,1).Value) :
# print("!= i:{},j:{}".format(colRng.Cells(i, 1).Value, colRng.Cells(j, 1).Value))
break
# 相等且不为空
elif not(colRng.Cells(i,1).Value is None):
# print("== 非空 i:{},j:{}".format(colRng.Cells(i,1).Value,colRng.Cells(j,1).Value))
pass
else:
# print("不等且空 i:{},j:{}".format(colRng.Cells(i, 1).Value, colRng.Cells(j, 1).Value))
break
# 上面只是为了递推最后一个相等的j的序号,合并操作在外面进行
colRng.Parent.Range(colRng.Cells(i, 1), colRng.Cells(j - 1, 1)).Merge()
i = j - 1
参考:
- 利用 pywin32 操作 excel:比网上直接查到的一些博客要详细一点,不属于伸手党,有自己的内容??
- pywin32模块操作Excel 这个也写得很好呀,像我一样默默无名但是又认真干活的小白码农,还有好多呀,(* ̄︶ ̄)。
- win32com: how to tell if an excel cell is empty
3.12 excel列号字母转数字
在处理 3.11的时候,遇到的问题,
clRng=ws.Range("B11:C20")
# 定义Range的时候使用的列号是 字母
clRng.Cells(11,2) 11行 2列
# 但是在使用Cells时,其使用的却是 Cells(row,col)
# 所以,当已知列号字母的时候,需要多加一个步骤把字母变成数字序号,才能使用Cells,其实字母转数字很方便,数字转字母略不方便
colNum=clRng.Column
- MSDN-Range.Cells property (Excel):excel没有直接从列号字母形式转为数字形式的函数,需要开发者自己利用某些已有函数造一个
- VBA Code To Convert Column Number to Letter Or Letter To Number
- [分享] VBA列标字母与数字的转化办法汇总
- Range.Column property (Excel)
4. pyinstaller打包问题
使用如下语句打包,报错RecursionError:maximum recursion depth exceeded
pyinstaller main.py -F
尝试1
搜索后,根据
pyinstaller 打包错误:RecursionError: maximum recursion depth exceeded
尝试2 成功解决
无效,我的应该是一些报错信息,命令行看不全,重定向到文件
根据:Windows 重定向命令行输出可知:
pyinstaller main.py -F>a.txt //只会输出正确的信息到目标文件,错误信息还是会输出到控制台
pyinstaller main.py -F>a.txt 2>&1 //可以把错误信息和正确信息都输出到一个流
pyinstaller main.py -F>suc.txt 2>fail.txt //可以将正确和错误信息分开输出到不同的文件
查看错误文件发现:
搜索后,根据:github-pyinstaller-issue——RecursionError: maximum recursion depth exceeded - python 3.6 issue #2919:要将python3.6降级到python3.5.3 好像问题就解决了,发布这个问题的人贴出来的信息基本和我一样。
conda create -n py35 python=3.5
//产出信息表明安装的是 3.5.6 不是上述链接里的3.5.3 希望可以ok吧
pip install pyinstaller
pyinstaller main.py -F
确实显示 打包成功了,但是在执行生成的exe时,闪退
以管理员身份运行cmd,转入该exe目录下运行,
这说明,python3.5环境也需要安装那些 程序中所使用的包,这样才可能打包成功。。。继续安装包
pip install pywin32
pip install pandas
再重新生成一次,ok
5.交互过程中的问题
报错 Expression:("Buffer too small",0)
参考:
- 求助:c++,报错:Expression:(L"Buffer is too small" && 0)
- 关于sprintf_s,弹出Expression:(“Buffer too small”,0)
可知:错误的原因主要是sprintf_s
造成的,大概率就是所要输出的内容与所分配的buffer大小不匹配,所以报错了(直接在dll中注释掉这行代码就好了)
更多推荐
pywin32+excel(四)使用pywin32操作excel进阶
发布评论