使用正则表达式拆分成列
Using regular expression to split into columns
我急需帮助有一个数据,我想使用正则表达式(python)拆分成列,它必须使用正则表达式
Jan 9 01:04:49 syntax sshd(pam_unix)[21354]: authentication failure; logname= uid=0 euid=0 tty=NODEVssh ruser= rhost=120-123-141-4.hinet-ip.hinet.com
Jul 10 04:17:11 syntax su(pam_unix)[95367]: session opened for user abc by (uid=0)
May 1 14:06:19 syntax su(pam_unix)[95367]: session closed for user abc
Oct 23 18:08:26 syntax logrotate: ALERT exited abnormally with [1]
Jun 14 21:42:52 syntax su(pam_unix)[95367]: session opened for user cbx by (uid=0)
假设输出
它实际上来自 url,我把它变成了 pandas dataFrame 并尝试使用 re.split 但它给了我错误
*ValueError: 1 columns passed, passed data had 24 columns*
希望能得到我需要的输出?
下面的正则表达式可以拆分语句。必需的列将在捕获组中。
(.*:\d\d)\s(.*?)\s(.*?:)\s(.*)
检查以下link以供参考:
比如第二条记录会被拆分成
7 月 10 日04:17:11
语法
su(pam_unix)[95367]:
会话由 (uid=0)
为用户 abc 打开
刚开始,"it must use regular expression" 毫无理由地没有任何意义 - 出于您的目的,一些拆分将更快地弄清楚并且可能在它的稳健性方面相似。话虽这么说...
如果您想使用正则表达式来解析这些类似 syslog 的消息,您只需找出 4 种格式中的至少 3 种,然后将它们与(命名的)组放在一起。
我们希望以这样的方式结束:
re_log = rf'(?P<date>{re_date}) (?P<device>{re_device}) (?P<source>{re_source}): (?P<message>{re_message})'
注意组之间的 space 和冒号。
由于消息不太可能遵循任何可用的模式,因此这必须是我们的通配符:
re_message = r'.*'
同样,该设备希望是一个有效的设备 ID 或主机名,(例如,没有 spaces,只有字母数字和破折号):
re_device = r'[\w-]+'
我们可以使用 datetime 或 time 或一些解析来获得日期的正式解析,但我们并不关心,所以让我们粗略地匹配您的格式。我们不知道您的日志格式是使用前导零还是省略它们,因此我们允许:
re_date = r'\w{3} \d{1,2} \d{1,2}:\d{2}:\d{2}'
源有点非结构化,但只要它没有 space 我们就可以匹配所有内容,因为我们在 re_log
表达式中有冒号来捕获它:
re_source = r'[^ ]+'
最后,尝试一下会给我们一些可以应用于您的消息的东西
>>> import re
>>> eg = "Oct 23 18:08:26 syntax logrotate: ALERT exited abnormally with [1]"
>>> m = re.match(re_log, eg)
>>> m.groupdict()
{'date': 'Oct 23 18:08:26',
'device': 'syntax',
'source': 'logrotate',
'message': 'ALERT exited abnormally with [1]'}
所以你可以像这样创建一个命名的正则表达式,
r'(?P<Timestamp>\w{3}\s+\d{1,2}\s\d{1,2}:\d{2}:\d{2})\s(?P<A1>\w+)\s(?P<A2>[\S]+)\:\s(?P<A3>.*)'
如果上述正则表达式不起作用,您可以创建自己的正则表达式并在 regex101.com
上进行测试
您可以使用您提供的示例了解我是如何做到的 here。
然后使用 str.extract 将命名组命名为列名。
代码看起来像,
import pandas as pd
df = pd.DataFrame(data=["Jan 9 01:04:49 syntax sshd(pam_unix)[21354]: authentication failure; logname= uid=0 euid=0 tty=NODEVssh ruser= rhost=120-123-141-4.hinet-ip.hinet.com",
"Jul 10 04:17:11 syntax su(pam_unix)[95367]: session opened for user abc by (uid=0)",
"May 1 14:06:19 syntax su(pam_unix)[95367]: session closed for user abc"], columns=["value"])
print(df)
在控制台上,
value
0 Jan 9 01:04:49 syntax sshd(pam_unix)[21354]: ...
1 Jul 10 04:17:11 syntax su(pam_unix)[95367]: se...
2 May 1 14:06:19 syntax su(pam_unix)[95367]: se...
添加这个以将值列拆分为您需要的列,
pattern = r'(?P<Timestamp>\w{3}\s+\d{1,2}\s\d{1,2}:\d{2}:\d{2})\s(?P<A1>\w+)\s(?P<A2>[\S]+)\:\s(?P<A3>.*)'
df1 = df['value'].str.extract(pattern, expand=True)
print(df1)
在控制台上,
Timestamp A1 A2 A3
0 Jan 9 01:04:49 syntax sshd(pam_unix)[21354] authentication failure; logname= uid=0 euid=0 ...
1 Jul 10 04:17:11 syntax su(pam_unix)[95367] session opened for user abc by (uid=0)
2 May 1 14:06:19 syntax su(pam_unix)[95367] session closed for user abc
希望这对您有所帮助,干杯!
解决方案
您需要结合使用以下正则表达式模式和 pandas.Series.str.findall()
才能快速轻松地获取它。
我还制作了一个便捷函数: process_logdata()
这样你就可以直接使用了。此答案底部提供了便捷功能。
df = process_logdata(log_file_name='logfile.txt')
print(df)
逻辑:
这是方便函数的逻辑,process_logdata()
。
# regex pattern
pattern = '\s*(\w{3}\s+\d{1,2}\s+\d{2}:\d{2}:\d{2})\s+(\S+)\s+(\S+?:)\s+(.*)'
# read log file
df = pd.read_csv('logfile.txt', header=None).rename(columns={0: 'logline'})
# process data
ds = df.logline.str.strip().str.findall(pattern)
a = np.array([list(e) for e in ds]).reshape(ds.size,-1)
# finalize processed data as a dataframe
df = pd.DataFrame(a, columns=['Timestamp', 'A1', 'A3', 'A3'])
print(df)
例子
这里我们使用虚拟数据(以字符串形式提供)。首先,我们将其加载到 pandas 数据帧中,然后对其进行处理。
import numpy as np
import pandas as pd
import re
from io import StringIO
s = """
Jan 9 01:04:49 syntax sshd(pam_unix)[21354]: authentication failure; logname= uid=0 euid=0 tty=NODEVssh ruser= rhost=120-123-141-4.hinet-ip.hinet.com
Jul 10 04:17:11 syntax su(pam_unix)[95367]: session opened for user abc by (uid=0)
May 1 14:06:19 syntax su(pam_unix)[95367]: session closed for user abc
Oct 23 18:08:26 syntax logrotate: ALERT exited abnormally with [1]
Jun 14 21:42:52 syntax su(pam_unix)[95367]: session opened for user cbx by (uid=0)
"""
s = re.sub('\n\s*\n', '\n', s).strip()
#print(s)
df = pd.read_csv(StringIO(s), header=None).rename(columns={0: 'logline'})
pattern = '\s*(\w{3}\s+\d{1,2}\s+\d{2}:\d{2}:\d{2})\s+(\S+)\s+(\S+?:)\s+(.*)'
ds = df.logline.str.strip().str.findall(pattern)
a = np.array([list(e) for e in ds]).reshape(ds.size,-1)
df = pd.DataFrame(a, columns=['Timestamp', 'A1', 'A3', 'A3'])
print(df)
输出:
便利函数
import numpy as np
import pandas as pd
import re
def process_logdata(log_file_name):
"""Returns a dataframe created from the log file.
"""
## Define regex pattern
pattern = '\s*(\w{3}\s+\d{1,2}\s+\d{2}:\d{2}:\d{2})\s+(\S+)\s+(\S+?:)\s+(.*)'
## Read log file
df = (pd
.read_csv(log_file_name, header=None)
.rename(columns={0: 'logline'})
)
## Process data
ds = df['logline']str.strip().str.findall(pattern)
a = np.array([list(e) for e in ds]).reshape(ds.size,-1)
## Finalize processed data as a dataframe
df = pd.DataFrame(a, columns=['Timestamp', 'A1', 'A3', 'A3'])
return df
使用正则表达式如下
数据
df=pd.DataFrame({'Text':['Jan 9 01:04:49 syntax sshd(pam_unix)[21354]: authentication failure; logname= uid=0 euid=0 tty=NODEVssh ruser= rhost=120-123-141-4.hinet-ip.hinet.com','Jul 10 04:17:11 syntax su(pam_unix)[95367]: session opened for user abc by (uid=0)','May 1 14:06:19 syntax su(pam_unix)[95367]: session closed for user ab']})
正则表达式= ([A-Za-z]+\s+\d+\s+\d+:\d+:\d+)\s+|(?<=\])[:\s+]+|(?<=[x])\s+
df2=df.Text.str.split('([A-Za-z]+\s+\d+\s+\d+:\d+:\d+)\s+|(?<=\])[:\s+]+|(?<=[x])\s+', n=3, expand=True)
df2.rename(columns=({0:'DROP1',1:'Timestamp', 2:'A1', 3:'DROP', 4:'A2', 5:'DROP2',6:'A3'}),inplace=True)#Rename columns
df2.drop(columns=['DROP2','DROP1','DROP'],inplace=True)#Drop unwanted columns
基本上;
(?<=\])[:\s+]+
被 ]:
之后的 space 拆分
或-|
(?<=[x])\s+
被 x
之后的 space 拆分
或- |
([A-Za-z]+\s+\d+\s+\d+:\d+:\d+)\s+
拆分timestamp
结果
我急需帮助有一个数据,我想使用正则表达式(python)拆分成列,它必须使用正则表达式
Jan 9 01:04:49 syntax sshd(pam_unix)[21354]: authentication failure; logname= uid=0 euid=0 tty=NODEVssh ruser= rhost=120-123-141-4.hinet-ip.hinet.com
Jul 10 04:17:11 syntax su(pam_unix)[95367]: session opened for user abc by (uid=0)
May 1 14:06:19 syntax su(pam_unix)[95367]: session closed for user abc
Oct 23 18:08:26 syntax logrotate: ALERT exited abnormally with [1]
Jun 14 21:42:52 syntax su(pam_unix)[95367]: session opened for user cbx by (uid=0)
假设输出
它实际上来自 url,我把它变成了 pandas dataFrame 并尝试使用 re.split 但它给了我错误
*ValueError: 1 columns passed, passed data had 24 columns*
希望能得到我需要的输出?
下面的正则表达式可以拆分语句。必需的列将在捕获组中。
(.*:\d\d)\s(.*?)\s(.*?:)\s(.*)
检查以下link以供参考:
比如第二条记录会被拆分成
7 月 10 日04:17:11
语法
su(pam_unix)[95367]:
会话由 (uid=0)
为用户 abc 打开刚开始,"it must use regular expression" 毫无理由地没有任何意义 - 出于您的目的,一些拆分将更快地弄清楚并且可能在它的稳健性方面相似。话虽这么说...
如果您想使用正则表达式来解析这些类似 syslog 的消息,您只需找出 4 种格式中的至少 3 种,然后将它们与(命名的)组放在一起。
我们希望以这样的方式结束:
re_log = rf'(?P<date>{re_date}) (?P<device>{re_device}) (?P<source>{re_source}): (?P<message>{re_message})'
注意组之间的 space 和冒号。
由于消息不太可能遵循任何可用的模式,因此这必须是我们的通配符:
re_message = r'.*'
同样,该设备希望是一个有效的设备 ID 或主机名,(例如,没有 spaces,只有字母数字和破折号):
re_device = r'[\w-]+'
我们可以使用 datetime 或 time 或一些解析来获得日期的正式解析,但我们并不关心,所以让我们粗略地匹配您的格式。我们不知道您的日志格式是使用前导零还是省略它们,因此我们允许:
re_date = r'\w{3} \d{1,2} \d{1,2}:\d{2}:\d{2}'
源有点非结构化,但只要它没有 space 我们就可以匹配所有内容,因为我们在 re_log
表达式中有冒号来捕获它:
re_source = r'[^ ]+'
最后,尝试一下会给我们一些可以应用于您的消息的东西
>>> import re
>>> eg = "Oct 23 18:08:26 syntax logrotate: ALERT exited abnormally with [1]"
>>> m = re.match(re_log, eg)
>>> m.groupdict()
{'date': 'Oct 23 18:08:26',
'device': 'syntax',
'source': 'logrotate',
'message': 'ALERT exited abnormally with [1]'}
所以你可以像这样创建一个命名的正则表达式,
r'(?P<Timestamp>\w{3}\s+\d{1,2}\s\d{1,2}:\d{2}:\d{2})\s(?P<A1>\w+)\s(?P<A2>[\S]+)\:\s(?P<A3>.*)'
如果上述正则表达式不起作用,您可以创建自己的正则表达式并在 regex101.com
上进行测试您可以使用您提供的示例了解我是如何做到的 here。
然后使用 str.extract 将命名组命名为列名。
代码看起来像,
import pandas as pd
df = pd.DataFrame(data=["Jan 9 01:04:49 syntax sshd(pam_unix)[21354]: authentication failure; logname= uid=0 euid=0 tty=NODEVssh ruser= rhost=120-123-141-4.hinet-ip.hinet.com",
"Jul 10 04:17:11 syntax su(pam_unix)[95367]: session opened for user abc by (uid=0)",
"May 1 14:06:19 syntax su(pam_unix)[95367]: session closed for user abc"], columns=["value"])
print(df)
在控制台上,
value
0 Jan 9 01:04:49 syntax sshd(pam_unix)[21354]: ...
1 Jul 10 04:17:11 syntax su(pam_unix)[95367]: se...
2 May 1 14:06:19 syntax su(pam_unix)[95367]: se...
添加这个以将值列拆分为您需要的列,
pattern = r'(?P<Timestamp>\w{3}\s+\d{1,2}\s\d{1,2}:\d{2}:\d{2})\s(?P<A1>\w+)\s(?P<A2>[\S]+)\:\s(?P<A3>.*)'
df1 = df['value'].str.extract(pattern, expand=True)
print(df1)
在控制台上,
Timestamp A1 A2 A3
0 Jan 9 01:04:49 syntax sshd(pam_unix)[21354] authentication failure; logname= uid=0 euid=0 ...
1 Jul 10 04:17:11 syntax su(pam_unix)[95367] session opened for user abc by (uid=0)
2 May 1 14:06:19 syntax su(pam_unix)[95367] session closed for user abc
希望这对您有所帮助,干杯!
解决方案
您需要结合使用以下正则表达式模式和 pandas.Series.str.findall()
才能快速轻松地获取它。
我还制作了一个便捷函数: process_logdata()
这样你就可以直接使用了。此答案底部提供了便捷功能。
df = process_logdata(log_file_name='logfile.txt')
print(df)
逻辑:
这是方便函数的逻辑,process_logdata()
。
# regex pattern
pattern = '\s*(\w{3}\s+\d{1,2}\s+\d{2}:\d{2}:\d{2})\s+(\S+)\s+(\S+?:)\s+(.*)'
# read log file
df = pd.read_csv('logfile.txt', header=None).rename(columns={0: 'logline'})
# process data
ds = df.logline.str.strip().str.findall(pattern)
a = np.array([list(e) for e in ds]).reshape(ds.size,-1)
# finalize processed data as a dataframe
df = pd.DataFrame(a, columns=['Timestamp', 'A1', 'A3', 'A3'])
print(df)
例子
这里我们使用虚拟数据(以字符串形式提供)。首先,我们将其加载到 pandas 数据帧中,然后对其进行处理。
import numpy as np
import pandas as pd
import re
from io import StringIO
s = """
Jan 9 01:04:49 syntax sshd(pam_unix)[21354]: authentication failure; logname= uid=0 euid=0 tty=NODEVssh ruser= rhost=120-123-141-4.hinet-ip.hinet.com
Jul 10 04:17:11 syntax su(pam_unix)[95367]: session opened for user abc by (uid=0)
May 1 14:06:19 syntax su(pam_unix)[95367]: session closed for user abc
Oct 23 18:08:26 syntax logrotate: ALERT exited abnormally with [1]
Jun 14 21:42:52 syntax su(pam_unix)[95367]: session opened for user cbx by (uid=0)
"""
s = re.sub('\n\s*\n', '\n', s).strip()
#print(s)
df = pd.read_csv(StringIO(s), header=None).rename(columns={0: 'logline'})
pattern = '\s*(\w{3}\s+\d{1,2}\s+\d{2}:\d{2}:\d{2})\s+(\S+)\s+(\S+?:)\s+(.*)'
ds = df.logline.str.strip().str.findall(pattern)
a = np.array([list(e) for e in ds]).reshape(ds.size,-1)
df = pd.DataFrame(a, columns=['Timestamp', 'A1', 'A3', 'A3'])
print(df)
输出:
便利函数
import numpy as np
import pandas as pd
import re
def process_logdata(log_file_name):
"""Returns a dataframe created from the log file.
"""
## Define regex pattern
pattern = '\s*(\w{3}\s+\d{1,2}\s+\d{2}:\d{2}:\d{2})\s+(\S+)\s+(\S+?:)\s+(.*)'
## Read log file
df = (pd
.read_csv(log_file_name, header=None)
.rename(columns={0: 'logline'})
)
## Process data
ds = df['logline']str.strip().str.findall(pattern)
a = np.array([list(e) for e in ds]).reshape(ds.size,-1)
## Finalize processed data as a dataframe
df = pd.DataFrame(a, columns=['Timestamp', 'A1', 'A3', 'A3'])
return df
使用正则表达式如下
数据
df=pd.DataFrame({'Text':['Jan 9 01:04:49 syntax sshd(pam_unix)[21354]: authentication failure; logname= uid=0 euid=0 tty=NODEVssh ruser= rhost=120-123-141-4.hinet-ip.hinet.com','Jul 10 04:17:11 syntax su(pam_unix)[95367]: session opened for user abc by (uid=0)','May 1 14:06:19 syntax su(pam_unix)[95367]: session closed for user ab']})
正则表达式= ([A-Za-z]+\s+\d+\s+\d+:\d+:\d+)\s+|(?<=\])[:\s+]+|(?<=[x])\s+
df2=df.Text.str.split('([A-Za-z]+\s+\d+\s+\d+:\d+:\d+)\s+|(?<=\])[:\s+]+|(?<=[x])\s+', n=3, expand=True)
df2.rename(columns=({0:'DROP1',1:'Timestamp', 2:'A1', 3:'DROP', 4:'A2', 5:'DROP2',6:'A3'}),inplace=True)#Rename columns
df2.drop(columns=['DROP2','DROP1','DROP'],inplace=True)#Drop unwanted columns
基本上;
(?<=\])[:\s+]+
被 ]:
或-|
(?<=[x])\s+
被 x
或- |
([A-Za-z]+\s+\d+\s+\d+:\d+:\d+)\s+
拆分timestamp
结果