Python — создание словаря из большого текстового файла, где ключ соответствует шаблону регулярных выражений

#regex #list #python-3.x #dictionary #tuples

#регулярное выражение #Список #python-3.x #словарь #Кортежи

Вопрос:

Мой вопрос: как мне создать словарь из списка, назначив ключи словаря на основе соответствия шаблону регулярных выражений (‘^—L-[0-9]{8}’), и присваивание значений с использованием всех строк между каждым ключом.

Пример выдержки из необработанного файла:

 SQL> --L-93752133
SQL> --SELECT table_name, tablespace_name from dba_tables where upper(table_name) like amp;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
  

Некоторые дополнительные детали: необработанный файл может быть большим (не менее 80 тыс. строк, но часто намного больше), и мне нужно сохранить исходный интервал, чтобы вывод по-прежнему был легко читаемым. Вот как я читаю файл и удаляю «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))
  

Но как мне назначить все строки между каждым ключом в качестве значения, принадлежащего самому последнему ключу, предшествующему им? Я играл с новыми списками, кортежами и словарями, но все, что я делаю, возвращает мусор. Цель состоит в том, чтобы данные и ключи были связаны друг с другом, чтобы я мог возвращать их по мере необходимости позже в моем скрипте.

Я потратил некоторое время на поиск похожего вопроса, но в большинстве других случаев исходный файл уже был в формате, подобном словарю, поэтому создание нового словаря было менее сложной проблемой. Возможно, словарь или кортеж не являются правильным ответом, но любая помощь будет оценена! Спасибо!

Ответ №1:

В общем, вы должны задаться вопросом, зачем вам читать весь файл, разбивать строки на список, а затем выполнять итерацию по списку. Это анти-шаблон Python.

Для текстовых файлов, ориентированных на строки, просто выполните:

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

Однако звучит так, что у вас есть многострочные шаблоны, ориентированные на блоки. Если это так, для файлов меньшего размера прочитайте весь файл в одну строку и используйте для этого регулярное выражение. Затем вы должны использовать group 1 и group 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)
  

ДЕМОНСТРАЦИЯ

В любом случае выполнение этого регулярного выражения с помощью строки примера дает:

 >>> 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;nn      LOG_MODEn      ------------n      NOARCHIVELOGnnSQL> nSQL> archive log listn      Database log mode              No Archive Moden      Automatic archival             Disabledn      Archive destination            USE_DB_RECOVERY_FILE_DESTn      Oldest online log sequence     3n      Current log sequence           5nSQL>',
 '--L-93752133': 'SQL> --SELECT table_name, tablespace_name from dba_tables where upper(table_name) like amp;tablename_from_developer;nSQL>', 
 '--L-42127143': 'SQL> nSQL> SELECT t.name TSName, e.encryptionalg Algorithm, d.file_name File Namen      2    FROM v tn      3       , v en      4       , dba_data_files dn      5   WHERE t.ts# = e.ts#n      6     AND t.name = d.tablespace_name;nn      no rows selected'}
  

Комментарии:

1. Спасибо, чувак! Причина, по которой мое регулярное выражение не включало часть «SQL>», заключалась в том, что после записи файла в список я удалил этот шаблон из начала каждой строки (в моем втором фрагменте кода выше). Однако оставление экземпляров этого шаблона также будет работать для того, что я делаю.

2. Если регулярное выражение захватывает правильные блоки, вы можете просто заменить "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
  

Комментарии:

1. Похоже, что я близок к тому, чтобы попасть туда, но при печати он возвращает пустой словарь. Кроме того, я переместил «keyed_dict [last_key] = in_between_lines» так, чтобы он находился в отдельной строке в блоке «if last_key:». Это правильный формат?

2. Забудьте об этом и используйте версию dawgs ;). Он прав, весь подход запутан.