上篇文章我们已经实现了 “ 使用Python批量提取文本中,当某一行存在 “×××××” 时获取这一行文字,并将所有提取的内容按顺序保存到一个新的文件中” 。而后来我发现这个功能已经不能满足我的需求了,所以本次我们将重新对一个更复杂文件的内容进行批量提取到表格的处理。
如果是在虚幻5,直接用UnrealInsights就能进行内存分析,可视化界面也很清晰明了,但还在虚幻4的时候,虚幻4的内存分析报告可通过在运行游戏的时候输入命令MemReport -full来提取,因为提取的文件给人很不直观很难看出来是哪行对应的什么数据,于是就有了此篇。
这次我们将使用Python脚本批量将日志文件按自定义格式或分类分表的方式将想要的内容提取到excel表格中,以提高内容的直观性,使得我们更方便操作日志提供的内容和也更好进行分析优化。
一、安装脚本运行环境
1、安装Python,可以在Python官网下载即可:https://www.python.org,安装包大小在30M左右,直接默认安装就行。
2、安装 openpyxl(Excel库),在确认安装完成Python后,按Win+R键输入cmd打开命令窗口,输入命令 pip install openpyxl 会进行自动安装。
3、安装 re(正则表达式库),同上输入命令 pip install re 安装。
二、创建脚本
在我们安装完了最简运行环境后,找个地方新建文件,将文件格式改为.py,即可被识别为Python文件了,我这里演示的新建文件名为“ PyToExcel.py ”,这个文件可以双击运行里面的代码,前提是里面有写内容,上一篇我们已经做过这样的创建脚本的基本操作了,后面不再赘述。
关于脚本里面要写的,这次我将采用全中文的命名,也就是代码里能使用中文的地方,我会全部使用中文,这或许能让完全不懂代码的小白稍微一点启发,然后在写完代码之后,我会解释代码的一些用法或扩展新功能,方便以后查阅。下面先直接发代码。
三、编写脚本
Python代码如下,复制后记得改一下 “内存日志” ,这个路径应填自己的路径不要填我这个。
import openpyxl #Excel库
import re #正则表达式
内存日志 = r"E:\UE5\TestC\Saved\Profiling\MemReports\UEDPIE_0_TopDownMap-WindowsEditor-10.25-19.28.47\Pid20268_UEDPIE_0_TopDownMap-WindowsEditor-25-19.28.47.memreport"
if 内存日志 == "":
print("内存日志路径为空")
input("显示错误的路径")
exit()
日志文件 = open(内存日志, "r", encoding="utf-8")
if not 日志文件:
print("内存日志文件不存在")
input("显示错误的路径")
exit()
def 写入表格(写入数据, 首页行数):
for 行数, 行 in enumerate(写入数据):
if 行数 == 0:
首页 = 提取内存表.worksheets[0]
首页.title = "首页命令"
分页目录['首页'] = 首页
elif 行数 <= 首页行数:
分页目录['首页'].append(行.strip().split(': ')) # 按冒号:分割并写入到表
elif 行数 > 首页行数:
if 'MemReport: Begin ' in 行:
命令内容 = 开始命令.search(行).group(1)
if 命令内容:
新页 = 提取内存表.create_sheet()
分页目录[命令内容] = 新页
分页目录[命令内容].title = 命令内容[:31] #旧版Excel表名不超过31个字符
continue
elif 'MemReport: End ' in 行:
命令内容 = 结束命令.search(行).group(1)
if 命令内容:
continue
else:
写入的文本 = 行.strip().split(': ')) # 按冒号:分割并写入到表
分页目录[命令内容].append(写入的文本)
def 获取首页总行数(列表):
for i, 行 in enumerate(列表):
if 行.strip() == "" and i != 0:
return i
return len(列表)
def 自适应每列的最大宽度(工作表):
for column in 工作表.columns:
max_length = 0
column = [cell for cell in column]
for cell in column:
try:
if len(str(cell.value)) > max_length:
max_length = len(str(cell.value))
except:
pass
adjusted_width = (max_length + 2)
工作表.column_dimensions[column[0].column_letter].width = adjusted_width
def 调整列宽(目录):
for 工作表 in 目录.values():
自适应每列的最大宽度(工作表)
# 通过正则表达式匹配内容类型
开始命令 = re.compile(r'MemReport: Begin command "(.*?)"')
结束命令 = re.compile(r'MemReport: End command "(.*?)"')
提取数据 = 日志文件.readlines()
提取数据.insert(0, '\n') #在list[0]插入一个空行,方便和日志首行索引的1对齐
数据长度 = len(提取数据)
首页总行数 = 获取首页总行数(提取数据)
提取内存表 = openpyxl.Workbook() #创建一个Excel表
分页目录 = {}
写入表格(提取数据, 首页总行数)
调整列宽(分页目录)
提取内存表.save("提取内存表.xlsx")
print("数据长度为:", 数据长度)
print("首个空行索引为:", 首页总行数)
input(" 数据提取完成,请按回车键关闭 ")
四、代码详解
1、输入、输出路径
内存日志 = r"E:\UE5\TestC\Saved\Profiling\MemReports\UEDPIE_0_TopDownMap-WindowsEditor-10.25-19.28.47\Pid20268_UEDPIE_0_TopDownMap-WindowsEditor-25-19.28.47.memreport"
我们通过UE5命令 MemReport -full 来提取的日志一般存放在项目的...\Saved\Profiling\MemReports\...文件夹里面,所以这里填写的路径即为这个完整路径。如果不是想针对 UE日志文件 提取到表格的需求,也可以填写自己的文件路径,但需要改一些地方,也可能要修改 “写入表格” 这个函数里面的if判断语句以及正则表达式等。
修改完路径后,保存双击脚本执行,若执行成功会得到这样的效果:
在我们执行脚本后,会自动提取日志文件,若提取完成则会把表格存放在脚本所在文件夹。
2、实现原理
UE日志因不同版本命令和项目,日志内容差异化很大,但大体上还是有一点规律可循的,比如日志中的 “MemReport: Begin......” 标志着某个模块类型的内存开始,“MemReport: End” 为结束,如下图这种就是日志常见的分析内容:
那我们就可以通过判断这个 “MemReport: Begin......” 的内容所在行,结合正则表达式去匹配类型,如果匹配上了就在后面新建一张新工作簿,并把匹配结果的标志作为了新表名称,将这一行的下面的内容批量写入该表格中了,直到再次匹配到不同的 “MemReport: Begin......” 。最终提取结果如下:
3、存在不足
目前就上面编写的代码中的逻辑,还存在一些不足的地方,因为我们在使用UE提取内存分析日志时,可能使用了不同的内存分析命令,那么所产生的日志内容当然也有区别,随提取内容格式排版的不同,就会出现一些提取后表格看起来依然不直观的问题。
在我编写的代码中是按一定格式去分割字符串再填写到表格的,为了区分不同单元格,我使用了 openpyxl
库中的 strip( )和 split( ) ,是字符串处理方法,通常用于清理和分割字符串,经过处理再写入 Excel 。split( ) 方法用于根据指定的分隔符将字符串分割成多个子字符串,并返回一个列表。如果不提供分隔符,默认情况下会根据空白字符进行分割。但我们脚本代码不足的地方在于,分割字符串的规则要手动修改才能达到想要的效果,不能完全实现自动化。所以如果以后有机会,我会研究一个小界面,在界面填写规则就更方便了。
4、扩展
目前需要手动修改的地方有两个,一是,日志文件的路径要填你自己的,二是,根据具体情况去修改代码这里的 split(': ') 。改完之后注意需 关闭表格后 再重新执行脚本,在表格打开状态无法修改哦。
else:
写入的文本 = 行.strip().split(': ')) # 按冒号:分割并写入到表
分页目录[命令内容].append(写入的文本)
针对第二种情况,我们需要扩展几种不同命令内容,下面举几个例子,
例1:前面我们在分析的 "ConfigMem" 这个内容提取到表格的时候就需要把 split(': ') 改为 split(' ') ,就是为了按每行内容中的空格数这个规则去匹配,即:
else:
写入的文本 = 行.strip().split(' ')
分页目录[命令内容].append(写入的文本)
例2:像下面这种 "DumpParticleMem" 日志,这种和前面我们在分析的 "ConfigMem" 就不太一样了:
这种情况他的每一行是按逗号“,”进行区别的,并不是像前面我们在分析的 "ConfigMem" 那种按照多个空格进行有序的排版,所以我们也需要改一下 split(': ') 为 split(', ')进行分割字符串,即:
else:
写入的文本 = 行.strip().split(',') # 按逗号,分割字符串后依次写入到表
分页目录[命令内容].append(写入的文本)
我们对比修改前后,会发现如果是按照逗号进行排版则更适配表格的美感,这也正是这次编写脚本想要达到的效果。如下是提取表格的对比:
例3:我们一般提取的虚幻内存分析日志最前面会显示一些基本信息,类似这种:
这种写在日志最前面的内容,经过分析一看,发现是用 冒号加空格“: ” 来区别内容的,所以遇到这种分割规则的时候,我们的代码采用了 split(': ') 来分割字符串。
def 写入表格(写入数据, 首页行数):
for 行数, 行 in enumerate(写入数据):
if 行数 == 0:
首页 = 提取内存表.worksheets[0]
首页.title = "首页命令"
分页目录['首页'] = 首页
elif 行数 <= 首页行数:
分页目录['首页'].append(行.strip().split(': ')) # 按冒号:分割并写入到表
执行脚本后的结果即是:按照冒号加空格来进行分A、B两列
五、提取结果
最后,我们根据提取内容的不同排版规则,自定义了不同格式去提取整个日志文件,然后又根据不同内存分析内容来分出了多个工作簿,方便分别去对比数据。这比直接用记事本打开日志看起来更加直观,分析数据也更具阅览性。后续若再结合Excel进行数据统计或生成图表将更完美了。
六、总结
- 目的:从指定路径读取内存日志文件,并将其中的数据解析后写入到Excel表格中。
- 主要步骤:
- 导入库:使用
openpyxl
处理Excel文件,使用re
进行正则表达式匹配。 - 检查日志文件路径:确保内存日志文件路径不为空且文件存在。
- 定义函数:
写入表格(写入数据, 首页行数)
:根据数据内容将数据写入到不同的工作表中,首页数据按冒号:
分割,其他数据按逗号,
分割。获取首页总行数(列表)
:获取首页数据的总行数,遇到第一个空行停止计数。自适应每列的最大宽度(工作表)
:自动调整每个工作表的列宽以适应最长的内容。调整列宽(目录)
:调用自适应每列的最大宽度
函数,调整所有工作表的列宽。
- 正则表达式:定义正则表达式
开始命令
和结束命令
,用于匹配内存报告中的命令开始和结束标记。 - 读取日志文件:读取内存日志文件的所有行,并在列表开头插入一个空行。
- 处理数据:计算数据长度和首页总行数,创建Excel工作簿,调用
写入表格
函数将数据写入工作表,最后调用调整列宽
函数调整列宽。 - 保存Excel文件:将处理后的数据保存到名为
提取内存表.xlsx
的Excel文件中。 - 输出信息:打印数据长度和首个空行索引,并提示用户数据提取完成。
- 导入库:使用
关键点
- 路径检查:确保日志文件路径有效。
- 数据解析:根据日志文件内容动态创建多个工作表,并按规则写入数据。
- 列宽调整:自动调整Excel表格的列宽,使内容显示更美观。
- 用户交互:通过
input
函数暂停脚本执行,等待用户确认。
标签:提取,表格,Python,写入,Excel,内存,UE5,日志,首页 From: https://blog.csdn.net/Q1370034377/article/details/143330137