AI摘要:
这篇博客介绍了如何使用 Python 的正则表达式从日志中提取各种信息,包括时间戳、机器人名称列表、坐标点等。通过多个示例,展示了如何使用正则表达式匹配特定格式的数据,如日期时间、浮动数值、列表等,并结合 Python 内置的函数(如 eval()
和 re.search()
)进行解析和处理。博客强调了正则表达式在日志数据提取中的实用性,帮助开发者快速从复杂的文本中提取所需的信息。
示例1 —— 提取时间戳
[INFO] [1744205701.805933, 388.642000]: [2025-04-09 21:35:01] [log] This iteration begins
从如上所示的一行内容(line
)中提取格式为 YYYY-MM-DD HH:MM:SS
的时间戳(不包括方括号),时间戳中的年份、月份、日期、小时、分钟和秒都由数字组成,并且这些数字由特定的分隔符(-
和 :
)分隔,可以使用如下所示例的正则表达式
time_str = re.search(r"\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\]", line).group(1)
下面对正则表达式的逐部分进行分析:
正则表达式:r"\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\]"
-
\[
:- 反斜杠
\
用于转义字符[
, 因为在正则表达式中,[
是一个元字符,表示字符类的开始。所以\[
表示匹配字面上的左中括号[
。
- 反斜杠
-
(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})
:(
和)
:表示捕获组,即在匹配过程中将匹配到的内容分组存储,可以通过.group(1)
来提取这个分组的内容。\d{4}
:表示匹配 4 个数字(即年份部分)。\d
匹配一个数字,{4}
表示数字的数量为 4。-
:表示字面上的连字符-
,用来分隔年、月、日。\d{2}
:表示匹配 2 个数字,用于匹配月、日、小时、分钟和秒。:
:表示字面上的冒号:
, 用来分隔小时、分钟和秒。- 该部分整体表示匹配日期时间的格式:
YYYY-MM-DD HH:MM:SS
。
-
\]
:- 反斜杠
\
用于转义字符]
,因为在正则表达式中,]
是字符类的结束标志。所以\]
表示字面上的右中括号]
。
- 反斜杠
re.search()
方法:
re.search(r"\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\]", line)
:re.search()
是 Python 正则表达式模块re
中的一个函数,用于在指定的字符串line
中查找与给定模式匹配的第一个位置。- 它返回一个匹配对象(如果匹配成功),否则返回
None
。 - 在这个例子中,
r"\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\]"
是匹配模式,它会查找一个形如[YYYY-MM-DD HH:MM:SS]
的时间戳。
.group(1)
方法:
.group(1)
表示提取第一个捕获组的内容,即(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})
中匹配到的时间字符串。- 由于正则表达式中有一个捕获组(
()
),因此.group(1)
会返回匹配到的日期时间部分,不包括外部的方括号。
- 由于正则表达式中有一个捕获组(
所以,上述正则表达式可以从一行日志中提取出一个格式为 YYYY-MM-DD HH:MM:SS
的时间戳(不包括方括号)。
示例2 —— 提取字符串列表
[WARN] [1744206318.523503, 467.530000]: [2025-04-09 21:45:18] [log] available robots: ['tb3_1', 'tb3_2']
从如上所示的一行内容(line
)中提取出数量不确定的机器人名字列表,并存储到 current_group[“available_robots”]中,可以使用如下所示例的正则表达式
robots = re.search(r"\[(.*?)\]", line.split(":")[-1])
if robots:
current_group["available_robots"] = eval(robots.group(0))
下面对正则表达式的逐部分进行分析:
1. line.split(":")[-1]
:
line.split(":")
:将line
按照冒号:
分割成一个列表,分割的依据是:
字符。这个操作将日志行从:
分隔成多个部分。[-1]
:这个表示取列表中的最后一部分,通常是日志行中冒号:
后面的内容。
2. re.search(r"\[(.*?)\]", line.split(":")[-1])
:
-
这是一个正则表达式匹配操作,它用于在上一步提取的字符串(
line.split(":")[-1]
)中查找机器人列表。 -
正则表达式
r"\[(.*?)\]"
的含义:\[
:匹配字面上的左方括号[
,因为方括号是正则表达式中的特殊字符,需要使用反斜杠转义。(.*?)
:这是一个捕获组,.*?
是一个非贪婪匹配,表示匹配任何字符,直到遇到右方括号]
。非贪婪意味着它会尽可能少地匹配字符,以便尽早匹配到右方括号。\]
:匹配字面上的右方括号]
。
-
re.search()
会返回一个匹配对象,如果在提取的字符串中找到符合[...]
格式的部分,就会将该部分提取出来。如果没有找到,返回None
。
3. robots.group(0)
:
.group(0)
是re.search()
返回的匹配对象的方法,它返回匹配到的整个字符串。由于正则表达式是用来提取方括号包围的内容,因此robots.group(0)
将会返回一个包含方括号的字符串,例如"['tb3_1', 'tb3_2']"
。
4. eval(robots.group(0))
:
eval()
是一个 Python 内置函数,它将字符串作为 Python 表达式进行求值。- 在这个例子中,
eval(robots.group(0))
会将字符串"['tb3_1', 'tb3_2']"
解析成一个实际的 Python 列表['tb3_1', 'tb3_2']
。 - 这意味着原始日志中的字符串
['tb3_1', 'tb3_2']
被转换为一个 Python 列表,可以直接操作。
- 在这个例子中,
5. current_group["available_robots"] = eval(robots.group(0))
:
- 这一行将解析出来的机器人列表赋值给
current_group["available_robots"]
6、安全性注意:
使用 eval()
时需要小心,因为它会执行字符串中的任何 Python 表达式。如果输入的字符串不受信任(例如来自用户的输入),它可能会导致安全漏洞。在这个特定场景下,如果 line
来自可靠的日志文件,那么使用 eval()
是可以接受的,但在处理外部输入时应该避免使用。
示例3 —— 提取坐标点,并将它们转换为浮点数的列表
[INFO] [1744206321.989578, 467.911000]: [2025-04-09 21:45:21] [log] centroid record: ['[-3.0867193932760326, -2.0956288642773826]', '[-4.219862393320849, 3.255906439231088]', ...]
从如上所示的一行内容(line
)中提取出数量不确定的二维位置坐标,并将它们转换为浮点数的列表,存储到 current_group[“centroids”]中,可以使用如下所示例的正则表达式
current_group["centroids"] = [list(map(float, p.strip("[]").split(',')))
for p in eval(line.split(": ")[-1])]
以下是详细解释:
1. line.split(": ")[-1]
:
- 这段代码首先用
split(": ")
将line
分割成多个部分,分割的依据是:
(冒号后面有一个空格)。 [-1]
表示取分割后的最后一个部分,这通常是一个包含坐标列表的字符串,例如:['[-3.0867193932760326, -2.0956288642773826]', '[-4.219862393320849, 3.255906439231088]', ...]
2. eval(line.split(": ")[-1])
:
eval()
函数会将字符串中的表达式求值。在这个例子中,line.split(": ")[-1]
的值是一个字符串表示的列表(例如,['[-3.0867193932760326, -2.0956288642773826]', ...]
)。eval()
会将其转换成 Python 对象,也就是一个列表,其中每个元素都是一个字符串表示的坐标对。例如:['[-3.0867193932760326, -2.0956288642773826]', ...]
3. list(map(float, p.strip("[]").split(',')))
:
- 这部分代码用于将每个坐标字符串转换为一个浮点数列表。
p.strip("[]")
:去掉每个坐标字符串两侧的方括号([]
),例如将[-3.0867193932760326, -2.0956288642773826]
转换为-3.0867193932760326, -2.0956288642773826
。split(',')
:用逗号分割字符串,得到一个包含两个数字的字符串列表,例如:['-3.0867193932760326', '-2.0956288642773826']
。map(float, ...)
:将每个字符串转换为浮动数值。list(...)
:将map
对象转换为列表,得到坐标点[ -3.0867193932760326, -2.0956288642773826 ]
。
4. current_group["centroids"] = ...
:
current_group["centroids"]
会被赋值为上述生成的坐标列表。- 这意味着
centroids
将是一个包含多个坐标点的列表,每个坐标点是一个浮动数值对。
示例4 —— r’[,\s]+’
[,\s]:这是一个字符集,表示匹配逗号(,)或任何空白字符(\s)。[\s] 可以匹配空格、制表符、换行符等所有的空白字符。+:表示前面的字符集可以重复一次或多次。这意味着它会匹配连续的逗号或空白字符。所以,这个正则表达式 r’[,\s]+’ 匹配的是一个或多个逗号或者空白字符。
示例5 —— 可能存在多处随机空格的坐标提取
[WARN] [1744205712.201956, 389.939000]: [2025-04-09 21:35:12] [log] Robot tb3_1 previous assigned point: [-2.37719159 -0.51813818], Info Gain: 2.1900000652670863
从如上所示的一行内容(line
)中提取出可能存在多处随机空格的坐标,并存储到 coord_match中,可以使用如下所示例的正则表达式
coord_match = re.search(
r"previous assigned point:\s*\[" # 匹配 "previous assigned point: [",包含可能的空格
r"([-+]?\d*\.\d+|\d+)" # 匹配 x 坐标
r"\s*" # 匹配空格
r"([-+]?\d*\.\d+|\d+)" # 匹配 y 坐标
r"\s*\]", line # 匹配 "]",并允许空格
)
这个正则表达式的目标是匹配日志行中 "previous assigned point:"
后面的坐标对 [-2.37719159 -0.51813818]
,并将其中的 x 和 y 坐标提取出来。
正则表达式分析:
-
previous assigned point:\s*\[
:previous assigned point:
:匹配字面字符串"previous assigned point:"
。\s*
:匹配零个或多个空格字符。\[
:匹配字面上的左方括号[
,因为方括号在正则表达式中是特殊字符,所以需要转义。
-
([-+]?\d*\.\d+|\d+)
:([-+]?\d*\.\d+|\d+)
:这是一个捕获组,用来匹配数字(包括可能的正负号),可以是带小数点的数字(例如-2.37719159
),也可以是整数(例如3
)。具体而言:[-+]?
:匹配零个或一个正号+
或负号-
。\d*\.\d+
:匹配小数(例如2.3
或-2.37719159
),\d*
匹配零个或多个数字,\.\d+
匹配小数点后至少一个数字。|\d+
:或者匹配整数。
-
\s*
:匹配零个或多个空格字符,允许坐标之间有空格。 -
\]
:匹配字面上的右方括号]
。
结果:
假设 line
是:
[WARN] [1744205712.201956, 389.939000]: [2025-04-09 21:35:12] [log] Robot tb3_1 previous assigned point: [-2.37719159 -0.51813818], Info Gain: 2.1900000652670863
- 正则表达式会匹配到
"previous assigned point: [-2.37719159 -0.51813818]"
。 coord_match.group(1)
将提取到-2.37719159
(x 坐标),coord_match.group(2)
将提取到-0.51813818
(y 坐标)。
示例6 —— 从字符串中提取出两个浮点数坐标
这段代码使用正则表达式从 pos_str
字符串中提取出两个浮点数坐标。具体来说,代码通过正则表达式匹配坐标对,并将两个浮点数(可能带有科学计数法)提取出来。
代码分析:
match = re.search(r"\[([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)[,\s]+([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)\]", pos_str)
正则表达式详细解析:
-
\[
和\]
:\[
和\]
匹配字面上的方括号[
和]
,因为方括号在正则表达式中是特殊字符,需要用反斜杠\
转义。
-
([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)
:- 这部分是一个捕获组,用来匹配一个浮点数(包括整数、小数和科学计数法表示的数)。
[-+]?
:匹配可选的正号+
或负号-
,允许数值带有符号。\d*
:匹配零个或多个数字。这里是用来处理小数点前的数字(可以没有)。\.?
:匹配零个或一个小数点.
,用于处理整数和小数。\d+
:匹配一个或多个数字,用于处理小数点后的数字。(?:[eE][-+]?\d+)?
:这是一个非捕获组,匹配科学计数法部分,允许使用e
或E
来表示指数部分,后跟一个可选的符号(+
或-
)和一个或多个数字。例如,1.5e-3
或2.0E+10
。
-
[,\s]+
:[,\s]+
匹配一个或多个逗号,
或空白字符(包括空格、制表符等),这部分用于处理坐标之间的分隔符。
-
([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)
:- 这是第二个捕获组,类似于第一个组,匹配第二个浮点数坐标。它的工作方式与第一个捕获组相同,处理第二个坐标值。
匹配逻辑:
-
pos_str
:是待匹配的字符串,应该是一个包含两个浮点数坐标的字符串,格式类似于:"[ 0.24770621 -4.59249853 ]"
或者
"[1.23e4 -5.67E-2]"
-
该正则表达式会提取这两个坐标(即,括号内的两个数字)。
示例:
假设 pos_str
为:
"[ 0.24770621 -4.59249853 ]"
- 正则表达式会匹配到:
- 第一个捕获组(x 坐标):
0.24770621
- 第二个捕获组(y 坐标):
-4.59249853
- 第一个捕获组(x 坐标):
提取值:
如果正则表达式成功匹配,match.group(1)
将包含第一个坐标值(x 坐标),match.group(2)
将包含第二个坐标值(y 坐标)。
示例代码:
pos_str = "[ 0.24770621 -4.59249853 ]"
match = re.search(r"\[([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)[,\s]+([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)\]", pos_str)
if match:
x = match.group(1)
y = match.group(2)
print(f"x: {x}, y: {y}")
else:
print("No match found.")
结果:
对于 pos_str = "[ 0.24770621 -4.59249853 ]"
,输出将是:
x: 0.24770621, y: -4.59249853
总结:
- 正则表达式用于提取包含在方括号内的两个浮点数(可能带有科学计数法)的坐标。
- 提取的坐标将被分别存储在
match.group(1)
和match.group(2)
中。