【问题标题】:Using regular expression to split into columns使用正则表达式拆分为列
【发布时间】:2020-08-25 15:44:21
【问题描述】:

我急需帮助,我想使用 regex(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 数据帧并尝试使用 re.split 但它给了我错误

*ValueError: 1 columns passed, passed data had 24 columns*

希望我能得到我需要的输出吗?

【问题讨论】:

    标签: python regex pandas valueerror


    【解决方案1】:

    如下使用正则表达式

    数据

     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
    

    基本上;

    (?&lt;=\])[:\s+]+]: 后面的空格分隔

    或-|

    (?&lt;=[x])\s+x 之后的空格分隔

    或-|

    ([A-Za-z]+\s+\d+\s+\d+:\d+:\d+)\s+拆分timestamp

    结果

    【讨论】:

    • 感谢您的帮助,但是我有一个错误显示 TypeError: Cannot use .str.split with values of inferred dtype 'bytes'. 这与我的 df 是 df = pd.DataFrame(url) 有关吗
    • 你运行的是什么版本的熊猫?
    • 我目前使用的是 '0.25.1' 版本
    • 转换成 str 后,我的数据变成了 [['J'], ['a'], ['n'], [' '], [' '], ['9'], [''], ['0'], ['1'], [':'], ['0'], ['4'], [':'], ['4'], ['9 '], [' '], ['句法 '], [' '], [ 's'], ['s'],....
    【解决方案2】:

    所以你可以像这样创建一个命名的正则表达式,

    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
    

    希望这会有所帮助,干杯!

    【讨论】:

    • 您好,谢谢您的帮助,但是我仍然收到TypeError: Cannot use .str.extract with values of inferred dtype 'bytes'. 的错误,您知道原因吗?我确实将我的原始 df 链接到 utf-8 形式的 url,其中有一列之前
    • 是的,我在转换后出现错误,TypeError: string indices must be integers
    • df[value']= df['value'].str.decode("utf-8") ,应该按照我读过的内容来完成这项工作。你也在做同样的事情吗?
    【解决方案3】:

    下面的正则表达式可以拆分语句。必需的列将位于捕获组中。

    (.*:\d\d)\s(.*?)\s(.*?:)\s(.*)

    查看以下链接以供参考:

    regexr.com/549bm

    比如第二条记录会被拆分成

    7 月 10 日 04:17:11

    语法

    su(pam_unix)[95367]:

    由 (uid=0) 为用户 abc 打开的会话

    【讨论】:

      【解决方案4】:

      刚开始,“它必须使用正则表达式”没有任何理由没有任何意义 - 出于您的目的,一些拆分将更快地弄清楚并且可能在它的健壮程度方面相似。话说……

      如果您想使用正则表达式来解析这些类似 syslog 的消息,您只需找出 4 种格式中的至少 3 种,然后将它们与(命名的)组结合在一起。

      我们希望最终得到如下结果:

      re_log = rf'(?P<date>{re_date}) (?P<device>{re_device}) (?P<source>{re_source}): (?P<message>{re_message})'
      

      注意组之间的空格和冒号。

      由于消息不太可能遵循任何可用的模式,所以它必须是我们的通配符:

      re_message = r'.*'
      

      同样,设备希望是有效的设备 ID 或主机名(例如,没有空格,只有字母数字和破折号):

      re_device = r'[\w-]+'
      

      我们可以使用 datetime 或 time 或一些解析来获得日期的正式解析,但我们并不关心,所以让我们大致匹配您的格式。我们不知道您的日志格式是使用前导零还是忽略它们,因此我们允许:

      re_date = r'\w{3} \d{1,2} \d{1,2}:\d{2}:\d{2}'
      

      源有点非结构化,但只要它没有空格,我们就可以匹配所有内容,因为我们在 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]'}
      

      【讨论】:

        【解决方案5】:

        解决方案

        您需要使用以下正则表达式模式以及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
        

        【讨论】:

          猜你喜欢
          • 2021-04-23
          • 2020-08-07
          • 2018-04-06
          • 1970-01-01
          • 1970-01-01
          • 2017-09-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多