使用正则表达式读取数据
Reading data using regular expression
对于我的项目,我需要读取文件并将其与我的常量进行匹配,一旦匹配,就需要将它们存储在字典中。我将在下面展示我的数据样本和到目前为止的数据。
我的数据:
TIMESTAMP: 1579051725 20100114-202845
.1.2.3.4.5.6.7.8.9 = 234567890
ifTb: name-nam-na
.1.3.4.1.2.1.1.1.1.1.1.128 = STRING: AA1
.1.3.4.1.2.1.1.1.1.1.1.129 = STRING: Eth1
.1.3.4.1.2.1.1.1.1.1.1.130 = STRING: Eth2
此数据有 5 个我想收集的重要部分:
时间戳后的日期:1579051725
Num
(数字的第一部分直到 128、129、130 等):.1.3.4.1.2.1.1.1.1.1.1
Num2
(第二部分):128
or 129
or 130
or others in my larger data set
Syntax
:在这种情况下它被命名为:STRING
Counter
:在本例中它们是字符串; AA1
或 Eth1
或 Eth2
我也有(需要有)常量Num
作为程序中的字典,它保存上面的值和常量syntax
我要通读数据文件,
如果Num
匹配我在程序中的常量,
抢Num2
,
检查 Syntax
是否与程序中的常量 syntax
匹配
抢Counter
当我说抓取时,我的意思是将该数据放在相应的字典下。
简而言之,我想读取数据文件,将其中的5个变量拆分,匹配2个变量与常量字典值,并在字典下抓取并存储3个变量(包括时间)。
我现在无法拆分数据。我可以拆分除 Num
和 Num2
之外的所有内容。我也不知道如何创建常量字典以及我应该如何放在常量字典下。
我很想使用正则表达式而不是 if 语句,但无法弄清楚要使用什么符号,因为数据在单词中包含很多点。
到目前为止我有以下内容:
constant_dic1 = {[".1.3.4.1.2.1.1.1.1.1.1"]["STRING" ]}
data_cols = {'InterfaceNum':[],"IndexNum":[],"SyntaxName":[],"Counter":[],"TimeStamp":[]}
fileN = args.File_Name
with open (fileN, 'r') as f:
for lines in f:
if lines.startswith('.'):
if ': ' in lines:
lines=lines.split("=")
first_part = lines[0].split()
second_part = lines[1].split()
for i in first_part:
f_f = i.split("{}.{}.{}.{}.{}.{}.{}.{}.{}.{}.{}.")
print (f_f[0])
一旦我 运行 程序,我收到 "TypeError: list indices must be integers or slices, not str".
的错误
当我注释掉字典部分时,输出是 Num
以及 Num2
。它不会被拆分,也不会只打印 Num
部分。
感谢任何帮助!
如果有任何其他来源,请在下面告诉我。
如果我需要在不投票的情况下对问题进行任何更新,请告诉我。
谢谢!
更新代码
import pandas as pd
import io
import matplotlib
matplotlib.use('TkAgg') # backend option for matplotlib #TkAgg #Qt4Agg #Qt5Agg
import matplotlib.pyplot as plt
import re # regular expression
import argparse # for optional arguments
parser = argparse.ArgumentParser()
parser.add_argument('File_Name', help="Enter the file name | At least one file is required to graph")
args=parser.parse_args()
data_cols = {'InterfaceNum':[],"IndexNum":[],"SyntaxName":[],"Counter":[],"TimeStamp":[]}
fileN = args.File_Name
input_data = fileN
expr = r"""
TIMESTAMP:\s(\d+) # date - TimeStamp
| # ** OR **
((?:\.\d+)+) # num - InterfaceNum
\.(\d+)\s=\s # num2 - IndexNum
(\w+):\s # syntax - SyntaxName
(\w+) # counter - Counter
"""
expr = re.compile(expr, re.VERBOSE)
data = {}
keys = ['TimeStamp', 'InterfaceNum', 'IndexNum', 'SyntaxName', 'Counter']
with io.StringIO(input_data) as data_file:
for line in data_file:
try:
find_data = expr.findall(line)[0]
vals = [date, num, num2, syntax, counter] = list(find_data)
if date:
cur_date = date
data[cur_date] = {k: [] for k in keys}
elif num:
vals[0] = cur_date
for k, v in zip(keys, vals):
data[cur_date][k].append(v)
except IndexError:
# expr.findall(...)[0] indexes an empty list when there's no
# match.
pass
data_frames = [pd.DataFrame.from_dict(v) for v in data.values()]
print(data_frames[0])
我收到错误
Traceback (most recent call last):
File "v1.py", line 47, in <module>
print(data_frames[0])
IndexError: list index out of range
新数据
TIMESTAMP: 1579051725 20100114-202845
.1.2.3.4.5.6.7.8.9 = 234567890
ifTb: name-nam-na
.1.3.4.1.2.1.1.1.1.1.1.128 = STRING: AA1
.1.3.4.1.2.1.1.1.1.1.1.129 = STRING: Eth1
.1.3.4.1.2.1.1.1.1.1.1.130 = STRING: Eth2
.1.2.3.4.5.6.7.8.9.10.11.131 = INT32: A
更新代码 (v2)
import pandas as pd
import io
import matplotlib
import re # regular expression
file = r"/home/rusif.eyvazli/Python_Projects/network-switch-packet-loss/s_data.txt"
def get_dev_data(file_path, timestamp=None, iface_num=None, idx_num=None,
syntax=None, counter=None):
timestamp = timestamp or r'\d+'
iface_num = iface_num or r'(?:\.\d+)+'
idx_num = idx_num or r'\d+'
syntax = syntax or r'\w+'
counter = counter or r'\w+'
# expr = r"""
# TIMESTAMP:\s({timestamp}) # date - TimeStamp
# | # ** OR **
# ({iface_num}) # num - InterfaceNum
# \.({idx_num})\s=\s # num2 - IndexNum
# ({syntax}):\s # syntax - SyntaxName
# ({counter}) # counter - Counter
# """
expr = r"TIMESTAMP:\s(\d+)|((?:\.\d+)+)\.(\d+)\s=\s(\w+):\s(\w+)"
# expr = re.compile(expr, re.VERBOSE)
expr = re.compile(expr)
rows = []
keys = ['TimeStamp', 'InterfaceNum', 'IndexNum', 'SyntaxName', 'Counter']
cols = {k: [] for k in keys}
with open(file_path, 'r') as data_file:
for line in data_file:
try:
find_data = expr.findall(line)[0]
vals = [tstamp, num, num2, sntx, ctr] = list(find_data)
if tstamp:
cur_tstamp = tstamp
elif num:
vals[0] = cur_tstamp
rows.append(vals)
for k, v in zip(keys, vals):
cols[k].append(v)
except IndexError:
# expr.findall(line)[0] indexes an empty list when no match.
pass
return rows, cols
const_num = '.1.3.4.1.2.1.1.1.1.1.1'
const_syntax = 'STRING'
result_5 = get_dev_data(file)
# Use the results of the first dict retrieved to initialize the master
# dictionary.
master_dict = result_5[1]
df = pd.DataFrame.from_dict(master_dict)
df = df.loc[(df['InterfaceNum'] == '.1.2.3.4.5.6.7.8.9.10.11') & (df['SyntaxName'] == 'INT32' )]
print(f"\n{df}")
输出
TimeStamp InterfaceNum IndexNum SyntaxName Counter
3 1579051725 .1.2.3.4.5.6.7.8.9.10.11 131 INT32 A
请使用 python "re" 包在 python 中使用正则表达式。这个包使得在 python 中使用正则表达式变得如此容易,你可以使用这个包中的各种函数来实现你需要的。 https://docs.python.org/3/library/re.html#module-contents 使用此 link 阅读文档。
有一个名为 re.Pattern.match() 的函数可用于匹配模式,您需要尝试一下。
使用正则表达式解析原始文件输入
下面的函数是如何使用正则表达式解析原始文件输入的示例。
循环正则表达式捕获组以构建记录。这是一个可重用的模式,可以在许多情况下应用。在 'Groupings in compound regular expressions' 部分有更多关于它如何工作的信息。
该函数将过滤与参数值匹配的记录。将它们保留为默认值,函数 returns 所有数据行。
def get_dev_data(file_path, timestamp=None, iface_num=None, idx_num=None,
syntax=None, counter=None):
timestamp = timestamp or r'\d+'
iface_num = iface_num or r'(?:\.\d+)+'
idx_num = idx_num or r'\d+'
syntax = syntax or r'\w+'
counter = counter or r'\w+'
expr = rf"""
TIMESTAMP:\s({timestamp}) # date - TimeStamp
| # ** OR **
({iface_num}) # num - InterfaceNum
\.({idx_num})\s=\s # num2 - IndexNum
({syntax}):\s # syntax - SyntaxName
({counter}) # counter - Counter
"""
expr = re.compile(expr, re.VERBOSE)
rows = []
keys = ['TimeStamp', 'InterfaceNum', 'IndexNum', 'SyntaxName', 'Counter']
cols = {k: [] for k in keys}
with open(file_path, 'r') as data_file:
for line in data_file:
try:
find_data = expr.findall(line)[0]
vals = [tstamp, num, num2, sntx, ctr] = list(find_data)
if tstamp:
cur_tstamp = tstamp
elif num:
vals[0] = cur_tstamp
rows.append(vals)
for k, v in zip(keys, vals):
cols[k].append(v)
except IndexError:
# expr.findall(line)[0] indexes an empty list when no match.
pass
return rows, cols
返回一个元组。第一项,rows
,是一个简单格式的数据行列表;第二项 cols
是一个以列名为键的字典,每个键都有一个行数据列表。两者都包含相同的数据,并且都可以分别被 Pandas 和 pd.DataFrame.from_records()
或 pd.DataFrame.from_dict()
消化。
过滤示例
这显示了如何使用函数参数过滤记录。我认为最后一个 result_4
符合问题中的描述。假设 iface_num
设置为您的 const_num
,syntax
设置为您的 const_syntax
值。只会返回匹配的记录。
if __name__ == '__main__':
file = r"/test/inputdata.txt"
result_1 = get_dev_data(file)[0]
result_2 = get_dev_data(file, counter='Eth2')[0]
result_3 = get_dev_data(file, counter='Eth2|AA1')[0]
result_4 = get_dev_data(file,
iface_num='.1.3.4.1.2.1.1.1.1.1.1', syntax='STRING')[0]
for var_name, var_val in zip(['result_1', 'result_2', 'result_3', 'result_4'],
[ result_1, result_2, result_3, result_4]):
print(f"{var_name} = {var_val}")
输出
result_1 = [['1579051725', '.1.3.4.1.2.1.1.1.1.1.1', '128', 'STRING', 'AA1'], ['1579051725', '.1.3.4.1.2.1.1.1.1.1.1', '129', 'STRING', 'Eth1'], ['1579051725', '.1.3.4.1.2.1.1.1.1.1.1', '130', 'STRING', 'Eth2']]
result_2 = [['1579051725', '.1.3.4.1.2.1.1.1.1.1.1', '130', 'STRING', 'Eth2']]
result_3 = [['1579051725', '.1.3.4.1.2.1.1.1.1.1.1', '128', 'STRING', 'AA1'], ['1579051725', '.1.3.4.1.2.1.1.1.1.1.1', '130', 'STRING', 'Eth2']]
result_4 = [['1579051725', '.1.3.4.1.2.1.1.1.1.1.1', '128', 'STRING', 'AA1'], ['1579051725', '.1.3.4.1.2.1.1.1.1.1.1', '129', 'STRING', 'Eth1'], ['1579051725', '.1.3.4.1.2.1.1.1.1.1.1', '130', 'STRING', 'Eth2']]
使用第一个返回的元组项,可以使用返回记录的偏移量访问列数据。例如 TimeStamp
将像 first_item[0][0]
一样访问 - 第一行,第一列。或者,可以将行转换为数据帧并以这种方式访问。
输入文件/test/inputdata.txt
TIMESTAMP: 1579051725 20100114-202845
.1.2.3.4.5.6.7.8.9 = 234567890
ifTb: name-nam-na
.1.3.4.1.2.1.1.1.1.1.1.128 = STRING: AA1
.1.3.4.1.2.1.1.1.1.1.1.129 = STRING: Eth1
.1.3.4.1.2.1.1.1.1.1.1.130 = STRING: Eth2
将行数据转换为 Pandas 数据框
函数输出中的第一个元组项将是与我们定义的列相对应的数据行。可以使用 pd.DataFrame.from_records()
:
将此格式转换为 Pandas 数据帧
>>> row_data = [['1579051725', '.1.3.4.1.2.1.1.1.1.1.1', '128', 'STRING', 'AA1']]]
>>>
>>> column_names = ['TimeStamp', 'InterfaceNum', 'IndexNum',
... 'SyntaxName', 'Counter']
>>>
>>> pd.DataFrame.from_records(row_data, columns=column_names)
TimeStamp InterfaceNum IndexNum SyntaxName Counter
0 1579051725 .1.3.4.1.2.1.1.1.1.1.1 128 STRING AA1
>>>
将列数据转换为 Pandas 数据帧
该函数还生成一个字典作为包含相同数据的返回元组的第二项,它也可以使用 pd.DataFrame.from_dict()
.
生成相同的数据帧
>>> col_data = {'TimeStamp': ['1579051725'],
... 'InterfaceNum': ['.1.3.4.1.2.1.1.1.1.1.1'],
... 'IndexNum': ['128'], 'SyntaxName': ['STRING'],
... 'Counter': ['AA1']}
>>>
>>> pd.DataFrame.from_dict(col_data)
TimeStamp InterfaceNum IndexNum SyntaxName Counter
0 1579051725 .1.3.4.1.2.1.1.1.1.1.1 128 STRING AA1
>>>
词典示例
这里有几个过滤文件数据、初始化持久字典的例子。然后过滤更多数据并将其添加到持久字典中。我认为这也接近问题中描述的内容。
const_num = '.1.3.4.1.2.1.1.1.1.1.1'
const_syntax = 'STRING'
result_5 = get_dev_data(file, iface_num=const_num, syntax=const_syntax)
# Use the results of the first dict retrieved to initialize the master
# dictionary.
master_dict = result_5[1]
print(f"master_dict = {master_dict}")
result_6 = get_dev_data(file, counter='Eth2|AA1')
# Add more records to the master dictionary.
for k, v in result_6[1].items():
master_dict[k].extend(v)
print(f"master_dict = {master_dict}")
df = pandas.DataFrame.from_dict(master_dict)
print(f"\n{df}")
输出
master_dict = {'TimeStamp': ['1579051725', '1579051725', '1579051725'], 'InterfaceNum': ['.1.3.4.1.2.1.1.1.1.1.1', '.1.3.4.1.2.1.1.1.1.1.1', '.1.3.4.1.2.1.1.1.1.1.1'], 'IndexNum': ['128', '129', '130'], 'SyntaxName': ['STRING', 'STRING', 'STRING'], 'Counter': ['AA1', 'Eth1', 'Eth2']}
master_dict = {'TimeStamp': ['1579051725', '1579051725', '1579051725', '1579051725', '1579051725'], 'InterfaceNum': ['.1.3.4.1.2.1.1.1.1.1.1', '.1.3.4.1.2.1.1.1.1.1.1', '.1.3.4.1.2.1.1.1.1.1.1', '.1.3.4.1.2.1.1.1.1.1.1', '.1.3.4.1.2.1.1.1.1.1.1'], 'IndexNum': ['128', '129', '130', '128', '130'], 'SyntaxName': ['STRING', 'STRING', 'STRING', 'STRING', 'STRING'], 'Counter': ['AA1', 'Eth1', 'Eth2', 'AA1', 'Eth2']}
TimeStamp InterfaceNum IndexNum SyntaxName Counter
0 1579051725 .1.3.4.1.2.1.1.1.1.1.1 128 STRING AA1
1 1579051725 .1.3.4.1.2.1.1.1.1.1.1 129 STRING Eth1
2 1579051725 .1.3.4.1.2.1.1.1.1.1.1 130 STRING Eth2
3 1579051725 .1.3.4.1.2.1.1.1.1.1.1 128 STRING AA1
4 1579051725 .1.3.4.1.2.1.1.1.1.1.1 130 STRING Eth2
如果不需要字典数据的所有列,可以使用<dict>.pop(<key>)
省去其中的键。或者您可以从根据数据创建的任何数据框中删除列。
复合正则表达式中的分组
此表达式显示当所有参数都保留为默认值时在函数中计算的表达式。
expr = r"""
TIMESTAMP:\s(\d+) # date - TimeStamp
| # ** OR **
((?:\.\d+)+) # num - InterfaceNum
\.(\d+)\s=\s # num2 - IndexNum
(\w+):\s # syntax - SyntaxName
(\w+) # counter - Counter
"""
在上面的正则表达式中,有两个备选语句由OR、|
运算符分隔。这些备选方案匹配一行时间戳数据或设备数据。在这些子表达式中是用于捕获字符串数据的特定部分的分组。匹配组是通过在子表达式周围放置圆括号 (...)
创建的。非分组括号的语法是 (?:...)
.
无论匹配哪个备选子表达式,每次成功调用re.findall()
.仍然会返回相同数量的匹配组。可能有点违反直觉,但这这就是它的工作原理。
但是,此功能确实使编写代码以提取您捕获的匹配项的哪些字段变得容易,因为无论匹配的子表达式如何,您都知道组应该位于的位置:
[<tstamp>, <num>, <num2>, <syntax>, <counter>]
# ^expr1^ ^.............expr2..............^
并且由于无论哪个子表达式匹配,我们都有可预测数量的匹配组,因此它启用了一种可应用于许多场景的循环模式。通过测试单个匹配组是否为空,我们知道要采用循环中的哪个分支来处理命中的子表达式的数据。
if tstamp:
# First expression hit.
elif num:
# Second alt expression hit.
当表达式与具有时间戳的文本行匹配时,第一个子表达式命中,其组将被填充。
>>> re.findall(expr, "TIMESTAMP: 1579051725 20100114-202845", re.VERBOSE)
[('1579051725', '', '', '', '')]
此处,表达式中的第一组已填充,其他组为空白。其他分组属于其他子表达式。
现在,当表达式与设备数据的第一行匹配时,第二个子表达式会命中,并填充其组。时间戳组为空。
>>> re.findall(expr, ".1.3.4.1.2.1.1.1.1.1.1.128 = STRING: AA1", re.VERBOSE)
[('', '.1.3.4.1.2.1.1.1.1.1.1', '128', 'STRING', 'AA1')]
最后,如果两个子表达式都不匹配,则整个表达式都不会命中。在这种情况下,我们得到一个空列表。
>>> re.findall(expr, "ifTb: name-nam-na", re.VERBOSE)
[]
>>>
相比之下,这里是没有冗长语法和文档的表达式:
expr = r"TIMESTAMP:\s(\d+)|((?:\.\d+)+)\.(\d+)\s=\s(\w+):\s(\w+)"
对于我的项目,我需要读取文件并将其与我的常量进行匹配,一旦匹配,就需要将它们存储在字典中。我将在下面展示我的数据样本和到目前为止的数据。
我的数据:
TIMESTAMP: 1579051725 20100114-202845
.1.2.3.4.5.6.7.8.9 = 234567890
ifTb: name-nam-na
.1.3.4.1.2.1.1.1.1.1.1.128 = STRING: AA1
.1.3.4.1.2.1.1.1.1.1.1.129 = STRING: Eth1
.1.3.4.1.2.1.1.1.1.1.1.130 = STRING: Eth2
此数据有 5 个我想收集的重要部分:
时间戳后的日期:
1579051725
Num
(数字的第一部分直到 128、129、130 等):.1.3.4.1.2.1.1.1.1.1.1
Num2
(第二部分):128
or129
or130
or others in my larger data setSyntax
:在这种情况下它被命名为:STRING
Counter
:在本例中它们是字符串;AA1
或Eth1
或Eth2
我也有(需要有)常量Num
作为程序中的字典,它保存上面的值和常量syntax
我要通读数据文件,
如果
Num
匹配我在程序中的常量,抢
Num2
,检查
Syntax
是否与程序中的常量syntax
匹配抢
Counter
当我说抓取时,我的意思是将该数据放在相应的字典下。
简而言之,我想读取数据文件,将其中的5个变量拆分,匹配2个变量与常量字典值,并在字典下抓取并存储3个变量(包括时间)。
我现在无法拆分数据。我可以拆分除 Num
和 Num2
之外的所有内容。我也不知道如何创建常量字典以及我应该如何放在常量字典下。
我很想使用正则表达式而不是 if 语句,但无法弄清楚要使用什么符号,因为数据在单词中包含很多点。
到目前为止我有以下内容:
constant_dic1 = {[".1.3.4.1.2.1.1.1.1.1.1"]["STRING" ]}
data_cols = {'InterfaceNum':[],"IndexNum":[],"SyntaxName":[],"Counter":[],"TimeStamp":[]}
fileN = args.File_Name
with open (fileN, 'r') as f:
for lines in f:
if lines.startswith('.'):
if ': ' in lines:
lines=lines.split("=")
first_part = lines[0].split()
second_part = lines[1].split()
for i in first_part:
f_f = i.split("{}.{}.{}.{}.{}.{}.{}.{}.{}.{}.{}.")
print (f_f[0])
一旦我 运行 程序,我收到 "TypeError: list indices must be integers or slices, not str".
的错误当我注释掉字典部分时,输出是 Num
以及 Num2
。它不会被拆分,也不会只打印 Num
部分。
感谢任何帮助! 如果有任何其他来源,请在下面告诉我。 如果我需要在不投票的情况下对问题进行任何更新,请告诉我。 谢谢!
更新代码
import pandas as pd
import io
import matplotlib
matplotlib.use('TkAgg') # backend option for matplotlib #TkAgg #Qt4Agg #Qt5Agg
import matplotlib.pyplot as plt
import re # regular expression
import argparse # for optional arguments
parser = argparse.ArgumentParser()
parser.add_argument('File_Name', help="Enter the file name | At least one file is required to graph")
args=parser.parse_args()
data_cols = {'InterfaceNum':[],"IndexNum":[],"SyntaxName":[],"Counter":[],"TimeStamp":[]}
fileN = args.File_Name
input_data = fileN
expr = r"""
TIMESTAMP:\s(\d+) # date - TimeStamp
| # ** OR **
((?:\.\d+)+) # num - InterfaceNum
\.(\d+)\s=\s # num2 - IndexNum
(\w+):\s # syntax - SyntaxName
(\w+) # counter - Counter
"""
expr = re.compile(expr, re.VERBOSE)
data = {}
keys = ['TimeStamp', 'InterfaceNum', 'IndexNum', 'SyntaxName', 'Counter']
with io.StringIO(input_data) as data_file:
for line in data_file:
try:
find_data = expr.findall(line)[0]
vals = [date, num, num2, syntax, counter] = list(find_data)
if date:
cur_date = date
data[cur_date] = {k: [] for k in keys}
elif num:
vals[0] = cur_date
for k, v in zip(keys, vals):
data[cur_date][k].append(v)
except IndexError:
# expr.findall(...)[0] indexes an empty list when there's no
# match.
pass
data_frames = [pd.DataFrame.from_dict(v) for v in data.values()]
print(data_frames[0])
我收到错误
Traceback (most recent call last):
File "v1.py", line 47, in <module>
print(data_frames[0])
IndexError: list index out of range
新数据
TIMESTAMP: 1579051725 20100114-202845
.1.2.3.4.5.6.7.8.9 = 234567890
ifTb: name-nam-na
.1.3.4.1.2.1.1.1.1.1.1.128 = STRING: AA1
.1.3.4.1.2.1.1.1.1.1.1.129 = STRING: Eth1
.1.3.4.1.2.1.1.1.1.1.1.130 = STRING: Eth2
.1.2.3.4.5.6.7.8.9.10.11.131 = INT32: A
更新代码 (v2)
import pandas as pd
import io
import matplotlib
import re # regular expression
file = r"/home/rusif.eyvazli/Python_Projects/network-switch-packet-loss/s_data.txt"
def get_dev_data(file_path, timestamp=None, iface_num=None, idx_num=None,
syntax=None, counter=None):
timestamp = timestamp or r'\d+'
iface_num = iface_num or r'(?:\.\d+)+'
idx_num = idx_num or r'\d+'
syntax = syntax or r'\w+'
counter = counter or r'\w+'
# expr = r"""
# TIMESTAMP:\s({timestamp}) # date - TimeStamp
# | # ** OR **
# ({iface_num}) # num - InterfaceNum
# \.({idx_num})\s=\s # num2 - IndexNum
# ({syntax}):\s # syntax - SyntaxName
# ({counter}) # counter - Counter
# """
expr = r"TIMESTAMP:\s(\d+)|((?:\.\d+)+)\.(\d+)\s=\s(\w+):\s(\w+)"
# expr = re.compile(expr, re.VERBOSE)
expr = re.compile(expr)
rows = []
keys = ['TimeStamp', 'InterfaceNum', 'IndexNum', 'SyntaxName', 'Counter']
cols = {k: [] for k in keys}
with open(file_path, 'r') as data_file:
for line in data_file:
try:
find_data = expr.findall(line)[0]
vals = [tstamp, num, num2, sntx, ctr] = list(find_data)
if tstamp:
cur_tstamp = tstamp
elif num:
vals[0] = cur_tstamp
rows.append(vals)
for k, v in zip(keys, vals):
cols[k].append(v)
except IndexError:
# expr.findall(line)[0] indexes an empty list when no match.
pass
return rows, cols
const_num = '.1.3.4.1.2.1.1.1.1.1.1'
const_syntax = 'STRING'
result_5 = get_dev_data(file)
# Use the results of the first dict retrieved to initialize the master
# dictionary.
master_dict = result_5[1]
df = pd.DataFrame.from_dict(master_dict)
df = df.loc[(df['InterfaceNum'] == '.1.2.3.4.5.6.7.8.9.10.11') & (df['SyntaxName'] == 'INT32' )]
print(f"\n{df}")
输出
TimeStamp InterfaceNum IndexNum SyntaxName Counter
3 1579051725 .1.2.3.4.5.6.7.8.9.10.11 131 INT32 A
请使用 python "re" 包在 python 中使用正则表达式。这个包使得在 python 中使用正则表达式变得如此容易,你可以使用这个包中的各种函数来实现你需要的。 https://docs.python.org/3/library/re.html#module-contents 使用此 link 阅读文档。
有一个名为 re.Pattern.match() 的函数可用于匹配模式,您需要尝试一下。
使用正则表达式解析原始文件输入
下面的函数是如何使用正则表达式解析原始文件输入的示例。
循环正则表达式捕获组以构建记录。这是一个可重用的模式,可以在许多情况下应用。在 'Groupings in compound regular expressions' 部分有更多关于它如何工作的信息。
该函数将过滤与参数值匹配的记录。将它们保留为默认值,函数 returns 所有数据行。
def get_dev_data(file_path, timestamp=None, iface_num=None, idx_num=None,
syntax=None, counter=None):
timestamp = timestamp or r'\d+'
iface_num = iface_num or r'(?:\.\d+)+'
idx_num = idx_num or r'\d+'
syntax = syntax or r'\w+'
counter = counter or r'\w+'
expr = rf"""
TIMESTAMP:\s({timestamp}) # date - TimeStamp
| # ** OR **
({iface_num}) # num - InterfaceNum
\.({idx_num})\s=\s # num2 - IndexNum
({syntax}):\s # syntax - SyntaxName
({counter}) # counter - Counter
"""
expr = re.compile(expr, re.VERBOSE)
rows = []
keys = ['TimeStamp', 'InterfaceNum', 'IndexNum', 'SyntaxName', 'Counter']
cols = {k: [] for k in keys}
with open(file_path, 'r') as data_file:
for line in data_file:
try:
find_data = expr.findall(line)[0]
vals = [tstamp, num, num2, sntx, ctr] = list(find_data)
if tstamp:
cur_tstamp = tstamp
elif num:
vals[0] = cur_tstamp
rows.append(vals)
for k, v in zip(keys, vals):
cols[k].append(v)
except IndexError:
# expr.findall(line)[0] indexes an empty list when no match.
pass
return rows, cols
返回一个元组。第一项,rows
,是一个简单格式的数据行列表;第二项 cols
是一个以列名为键的字典,每个键都有一个行数据列表。两者都包含相同的数据,并且都可以分别被 Pandas 和 pd.DataFrame.from_records()
或 pd.DataFrame.from_dict()
消化。
过滤示例
这显示了如何使用函数参数过滤记录。我认为最后一个 result_4
符合问题中的描述。假设 iface_num
设置为您的 const_num
,syntax
设置为您的 const_syntax
值。只会返回匹配的记录。
if __name__ == '__main__':
file = r"/test/inputdata.txt"
result_1 = get_dev_data(file)[0]
result_2 = get_dev_data(file, counter='Eth2')[0]
result_3 = get_dev_data(file, counter='Eth2|AA1')[0]
result_4 = get_dev_data(file,
iface_num='.1.3.4.1.2.1.1.1.1.1.1', syntax='STRING')[0]
for var_name, var_val in zip(['result_1', 'result_2', 'result_3', 'result_4'],
[ result_1, result_2, result_3, result_4]):
print(f"{var_name} = {var_val}")
输出
result_1 = [['1579051725', '.1.3.4.1.2.1.1.1.1.1.1', '128', 'STRING', 'AA1'], ['1579051725', '.1.3.4.1.2.1.1.1.1.1.1', '129', 'STRING', 'Eth1'], ['1579051725', '.1.3.4.1.2.1.1.1.1.1.1', '130', 'STRING', 'Eth2']]
result_2 = [['1579051725', '.1.3.4.1.2.1.1.1.1.1.1', '130', 'STRING', 'Eth2']]
result_3 = [['1579051725', '.1.3.4.1.2.1.1.1.1.1.1', '128', 'STRING', 'AA1'], ['1579051725', '.1.3.4.1.2.1.1.1.1.1.1', '130', 'STRING', 'Eth2']]
result_4 = [['1579051725', '.1.3.4.1.2.1.1.1.1.1.1', '128', 'STRING', 'AA1'], ['1579051725', '.1.3.4.1.2.1.1.1.1.1.1', '129', 'STRING', 'Eth1'], ['1579051725', '.1.3.4.1.2.1.1.1.1.1.1', '130', 'STRING', 'Eth2']]
使用第一个返回的元组项,可以使用返回记录的偏移量访问列数据。例如 TimeStamp
将像 first_item[0][0]
一样访问 - 第一行,第一列。或者,可以将行转换为数据帧并以这种方式访问。
输入文件/test/inputdata.txt
TIMESTAMP: 1579051725 20100114-202845
.1.2.3.4.5.6.7.8.9 = 234567890
ifTb: name-nam-na
.1.3.4.1.2.1.1.1.1.1.1.128 = STRING: AA1
.1.3.4.1.2.1.1.1.1.1.1.129 = STRING: Eth1
.1.3.4.1.2.1.1.1.1.1.1.130 = STRING: Eth2
将行数据转换为 Pandas 数据框
函数输出中的第一个元组项将是与我们定义的列相对应的数据行。可以使用 pd.DataFrame.from_records()
:
>>> row_data = [['1579051725', '.1.3.4.1.2.1.1.1.1.1.1', '128', 'STRING', 'AA1']]]
>>>
>>> column_names = ['TimeStamp', 'InterfaceNum', 'IndexNum',
... 'SyntaxName', 'Counter']
>>>
>>> pd.DataFrame.from_records(row_data, columns=column_names)
TimeStamp InterfaceNum IndexNum SyntaxName Counter
0 1579051725 .1.3.4.1.2.1.1.1.1.1.1 128 STRING AA1
>>>
将列数据转换为 Pandas 数据帧
该函数还生成一个字典作为包含相同数据的返回元组的第二项,它也可以使用 pd.DataFrame.from_dict()
.
>>> col_data = {'TimeStamp': ['1579051725'],
... 'InterfaceNum': ['.1.3.4.1.2.1.1.1.1.1.1'],
... 'IndexNum': ['128'], 'SyntaxName': ['STRING'],
... 'Counter': ['AA1']}
>>>
>>> pd.DataFrame.from_dict(col_data)
TimeStamp InterfaceNum IndexNum SyntaxName Counter
0 1579051725 .1.3.4.1.2.1.1.1.1.1.1 128 STRING AA1
>>>
词典示例
这里有几个过滤文件数据、初始化持久字典的例子。然后过滤更多数据并将其添加到持久字典中。我认为这也接近问题中描述的内容。
const_num = '.1.3.4.1.2.1.1.1.1.1.1'
const_syntax = 'STRING'
result_5 = get_dev_data(file, iface_num=const_num, syntax=const_syntax)
# Use the results of the first dict retrieved to initialize the master
# dictionary.
master_dict = result_5[1]
print(f"master_dict = {master_dict}")
result_6 = get_dev_data(file, counter='Eth2|AA1')
# Add more records to the master dictionary.
for k, v in result_6[1].items():
master_dict[k].extend(v)
print(f"master_dict = {master_dict}")
df = pandas.DataFrame.from_dict(master_dict)
print(f"\n{df}")
输出
master_dict = {'TimeStamp': ['1579051725', '1579051725', '1579051725'], 'InterfaceNum': ['.1.3.4.1.2.1.1.1.1.1.1', '.1.3.4.1.2.1.1.1.1.1.1', '.1.3.4.1.2.1.1.1.1.1.1'], 'IndexNum': ['128', '129', '130'], 'SyntaxName': ['STRING', 'STRING', 'STRING'], 'Counter': ['AA1', 'Eth1', 'Eth2']}
master_dict = {'TimeStamp': ['1579051725', '1579051725', '1579051725', '1579051725', '1579051725'], 'InterfaceNum': ['.1.3.4.1.2.1.1.1.1.1.1', '.1.3.4.1.2.1.1.1.1.1.1', '.1.3.4.1.2.1.1.1.1.1.1', '.1.3.4.1.2.1.1.1.1.1.1', '.1.3.4.1.2.1.1.1.1.1.1'], 'IndexNum': ['128', '129', '130', '128', '130'], 'SyntaxName': ['STRING', 'STRING', 'STRING', 'STRING', 'STRING'], 'Counter': ['AA1', 'Eth1', 'Eth2', 'AA1', 'Eth2']}
TimeStamp InterfaceNum IndexNum SyntaxName Counter
0 1579051725 .1.3.4.1.2.1.1.1.1.1.1 128 STRING AA1
1 1579051725 .1.3.4.1.2.1.1.1.1.1.1 129 STRING Eth1
2 1579051725 .1.3.4.1.2.1.1.1.1.1.1 130 STRING Eth2
3 1579051725 .1.3.4.1.2.1.1.1.1.1.1 128 STRING AA1
4 1579051725 .1.3.4.1.2.1.1.1.1.1.1 130 STRING Eth2
如果不需要字典数据的所有列,可以使用<dict>.pop(<key>)
省去其中的键。或者您可以从根据数据创建的任何数据框中删除列。
复合正则表达式中的分组
此表达式显示当所有参数都保留为默认值时在函数中计算的表达式。
expr = r"""
TIMESTAMP:\s(\d+) # date - TimeStamp
| # ** OR **
((?:\.\d+)+) # num - InterfaceNum
\.(\d+)\s=\s # num2 - IndexNum
(\w+):\s # syntax - SyntaxName
(\w+) # counter - Counter
"""
在上面的正则表达式中,有两个备选语句由OR、|
运算符分隔。这些备选方案匹配一行时间戳数据或设备数据。在这些子表达式中是用于捕获字符串数据的特定部分的分组。匹配组是通过在子表达式周围放置圆括号 (...)
创建的。非分组括号的语法是 (?:...)
.
无论匹配哪个备选子表达式,每次成功调用re.findall()
.仍然会返回相同数量的匹配组。可能有点违反直觉,但这这就是它的工作原理。
但是,此功能确实使编写代码以提取您捕获的匹配项的哪些字段变得容易,因为无论匹配的子表达式如何,您都知道组应该位于的位置:
[<tstamp>, <num>, <num2>, <syntax>, <counter>]
# ^expr1^ ^.............expr2..............^
并且由于无论哪个子表达式匹配,我们都有可预测数量的匹配组,因此它启用了一种可应用于许多场景的循环模式。通过测试单个匹配组是否为空,我们知道要采用循环中的哪个分支来处理命中的子表达式的数据。
if tstamp:
# First expression hit.
elif num:
# Second alt expression hit.
当表达式与具有时间戳的文本行匹配时,第一个子表达式命中,其组将被填充。
>>> re.findall(expr, "TIMESTAMP: 1579051725 20100114-202845", re.VERBOSE)
[('1579051725', '', '', '', '')]
此处,表达式中的第一组已填充,其他组为空白。其他分组属于其他子表达式。
现在,当表达式与设备数据的第一行匹配时,第二个子表达式会命中,并填充其组。时间戳组为空。
>>> re.findall(expr, ".1.3.4.1.2.1.1.1.1.1.1.128 = STRING: AA1", re.VERBOSE)
[('', '.1.3.4.1.2.1.1.1.1.1.1', '128', 'STRING', 'AA1')]
最后,如果两个子表达式都不匹配,则整个表达式都不会命中。在这种情况下,我们得到一个空列表。
>>> re.findall(expr, "ifTb: name-nam-na", re.VERBOSE)
[]
>>>
相比之下,这里是没有冗长语法和文档的表达式:
expr = r"TIMESTAMP:\s(\d+)|((?:\.\d+)+)\.(\d+)\s=\s(\w+):\s(\w+)"