【问题标题】:Python - creating a dictionary from large text file where the key matches regex patternPython - 从键匹配正则表达式模式的大文本文件创建字典
【发布时间】:2017-02-19 00:14:34
【问题描述】:

我的问题:如何通过基于正则表达式匹配的字典键('^--L-[0-9]{8}')从列表中创建字典,并通过使用每个键之间的所有行来分配值。

原始文件的示例摘录:

SQL> --L-93752133
SQL> --SELECT table_name, tablespace_name from dba_tables where upper(table_name) like &tablename_from_developer;
SQL> 
SQL> --L-52852243
SQL> 
SQL> SELECT log_mode FROM v$database;

      LOG_MODE
      ------------
      NOARCHIVELOG

SQL> 
SQL> archive log list
      Database log mode              No Archive Mode
      Automatic archival             Disabled
      Archive destination            USE_DB_RECOVERY_FILE_DEST
      Oldest online log sequence     3
      Current log sequence           5
SQL> 
SQL> --L-42127143
SQL> 
SQL> SELECT t.name "TSName", e.encryptionalg "Algorithm", d.file_name "File Name"
      2    FROM v$tablespace t
      3       , v$encrypted_tablespaces e
      4       , dba_data_files d
      5   WHERE t.ts# = e.ts#
      6     AND t.name = d.tablespace_name;

      no rows selected

一些额外的细节:原始文件可能很大(至少 80K+ 行,但通常更大),我需要保留原始间距,以便输出仍然易于阅读。以下是我读取文件并从每行开头删除“SQL>”的方式:

with open(rawFile, 'r') as inFile:
    content = inFile.read()

rawList = content.splitlines()

for line in rawList:
    cleanLine = re.sub('^SQL> ', '', line)

查找我要查找的字典键很容易:

pattern = re.compile(r'^--L-[0-9]{8}')
if pattern.search(cleanLine) is not None:
    itemID = pattern.search(cleanLine)
    print(itemID.group(0))

但是如何将每个键之间的所有行分配为属于它们之前的最新键的值?我一直在玩新的列表、元组和字典,但我所做的一切都是返回垃圾。目标是让数据和键相互关联,以便稍后在脚本中根据需要返回它们。

我花了一些时间寻找类似的问题,但在大多数其他情况下,源文件已经是类似字典的格式,因此创建新字典不是一个复杂的问题。也许字典或元组不是正确的答案,但任何帮助将不胜感激!谢谢!

【问题讨论】:

    标签: regex list python-3.x dictionary tuples


    【解决方案1】:

    一般来说,您应该质疑为什么要读取整个文件,将行拆分成一个列表,然后遍历该列表。这是一个 Python 反模式。

    对于面向行的文本文件,只需:

    with open(fn) as f:
       for line in f:
           # process a line
    

    但是,听起来您有面向多行块的模式。如果是这样,对于较小的文件,将整个文件读入一个字符串并使用正则表达式。然后您将使用第 1 组和第 2 组作为您的 dict 中的键值:

    pat=re.compile(pattern, flags)
    with open(file_name) as f:
        di={m.group(1):m.group(2) for m in pat.finditer(f.read())}
    

    如果文件较大,请使用mmap

    import re, mmap
    
    pat=re.compile(pattern, flags)
    with open(file_name, 'r+') as f:
        mm = mmap.mmap(f.fileno(), 0)
        for i, m in enumerate(pat.finditer(mm)):
            # process each block accordingly...
    

    至于正则表达式,我有点不清楚你想捕捉或不捕捉什么。我认为这个正则表达式是我理解你想要的:

    ^SQL> (--L-[0-9]{8})(.*?)(?=SQL> --L-[0-9]{8}|\Z)
    

    Demo

    在任何一种情况下,使用示例字符串运行该正则表达式都会产生:

    >>> pat=re.compile(r'^SQL> (--L-[0-9]{8})\s*(.*?)\s*(?=SQL> --L-[0-9]{8}|\Z)', re.S | re.M)
    >>> with open(file_name) as f:
    ...     di={m.group(1):m.group(2) for m in pat.finditer(f.read())}
    ... 
    
    >>> di
    {'--L-52852243': 'SQL> \nSQL> SELECT log_mode FROM v;\n\n      LOG_MODE\n      ------------\n      NOARCHIVELOG\n\nSQL> \nSQL> archive log list\n      Database log mode              No Archive Mode\n      Automatic archival             Disabled\n      Archive destination            USE_DB_RECOVERY_FILE_DEST\n      Oldest online log sequence     3\n      Current log sequence           5\nSQL>',
     '--L-93752133': 'SQL> --SELECT table_name, tablespace_name from dba_tables where upper(table_name) like &tablename_from_developer;\nSQL>', 
     '--L-42127143': 'SQL> \nSQL> SELECT t.name TSName, e.encryptionalg Algorithm, d.file_name File Name\n      2    FROM v t\n      3       , v e\n      4       , dba_data_files d\n      5   WHERE t.ts# = e.ts#\n      6     AND t.name = d.tablespace_name;\n\n      no rows selected'}
    

    【讨论】:

    • 谢谢老哥!我的正则表达式不包含“SQL>”部分的原因是在将文件写入列表后,我从每一行的开头删除了该模式(在我上面的第二个代码 sn-p 中)。但是,保留该模式的实例也适用于我正在做的事情。
    • 如果正则表达式捕获了正确的块,您可以在继续下一个之前替换 "SQL> " 部分。如果需要,可以使用 {m.group(1):m.group(2).replace("SQL>","") for m in pat.finditer(f.read())}re.sub 之类的东西。
    【解决方案2】:

    这样的?

    with open(rawFile, 'r') as inFile:
    content = inFile.read()
    
    rawList = content.splitlines()
    keyed_dict = {}
    in_between_lines = ""
    last_key = 0
    for line in rawList:
     cleanLine = re.sub('^SQL> ', '', line)
     pattern = re.compile(r'^--L-[0-9]{8}')
     if pattern.search(cleanLine) is not None:      
      itemID = pattern.search(cleanLine)
      if last_key: keyed_dict[last_key] = in_between_lines
      last_key = itemID.group(0)
      in_between_lines = ""
     else:
      in_between_lines += cleanLine
    

    【讨论】:

    • 这看起来很接近让我到达那里,但是当我打印它时它返回一个空字典。此外,我移动了“keyed_dict[last_key] = in_between_lines”,使其位于“if last_key:”块中的自己的行上。这是正确的格式吗?
    • 忘记它并使用 dawgs 版本 ;)。他是对的,整个方法很混乱。
    猜你喜欢
    • 1970-01-01
    • 2018-07-15
    • 2016-06-01
    • 2013-02-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多