Python, как извлечь текст между двумя маркерами несколько раз по всему текстовому файлу? - PullRequest
2 голосов
/ 07 июня 2019

У меня проблемы с извлечением фрагментов текста из текстового файла.Используя python 3, у меня есть формат ниже по всему текстовому файлу:

    integer stringOfFilePathandName.cpp string integer
    ...not needed text...
    ...not needed text...
    singleInteger( zero or one)
    ---------------------------------
    integer stringOfFilePathandName2.cpp string integer
    ...not needed text...
    ...not needed text...
    singleInteger( zero or one)
    ---------------------------------

Количество нежелательных текстовых строк не является стабильным для каждого экземпляра шаблона. Мне нужно сохранить значение stringOfFilePathandName.cpp и singleInteger , если это возможно, в словарь, например {stringOfFilePathandName: (0 или 1)} .

Текст содержит другие расширения файлов (например, .cpp), которые мне не нужны.Кроме того, я не знаю кодировку файла, поэтому я читаю его как двоичный файл.

Моя проблема имеет общие особенности с проблемами, указанными по ссылкам ниже:

Python читает файл до совпадениячитать до следующего шаблона

https://sopython.com/canon/92/extract-text-from-a-file-between-two-markers/ - который я не совсем понимаю

python - чтение файла из и в определенные строки текста - это я пытался скопировать, но работал только для одного экземпляра.Мне нужно повторить этот процесс по всему файлу.

В настоящее время я пробовал это, который работает для одного случая:

fileRegex = re.compile(r".*\.cpp")

with open('txfile',"rb") as fin:
   filename = None
   for line in input_data:
       if re.search(fileRegex,str(line)):
           filename = ((re.search(fileRegex,str(line))).group()).lstrip("b'") 
           break
   for line in input_data:
       if (str(line).lstrip("b'").rstrip("\\n'"))=="0" or (str(line).lstrip("b'").rstrip("\\n'"))=="1":
        dictOfFiles[filename] = (str(line).lstrip("b'").rstrip("\\n'"))

   del filename

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

за запрос вот текстовый файл: https://raw.githubusercontent.com/CGCL-codes/VulDeePecker/master/CWE-119/CGD/cwe119_cgd.txt

Ответы [ 2 ]

3 голосов
/ 07 июня 2019

Одной из возможностей будет использование re.findall с шаблоном регулярных выражений, который может охватывать более одной строки:

input = """1 file1.cpp blah 3
           not needed
           not needed
           2
           ---------------------------------
           9 file1.cpp blah 5
           not needed
           not needed
           3
           ---------------------------------"""
matches = re.findall(r'(\w+\.cpp).*?(\d+)(?=\s+--------)', input, re.DOTALL)
print(matches)

Это печатает:

[('file1.cpp', '2'), ('file1.cpp', '3')]

Этот ответ предполагает, что вы можете допустить чтение всего файла в память, а затем сделать один проход с помощью re.findall. Если вы не можете сделать это, вам нужно будет продолжить текущий подход к синтаксическому анализу.

2 голосов
/ 07 июня 2019

Вы можете использовать

fileRegex = re.compile(rb"^\d+\s+(\S+\.cpp)\s.*(?:\r?\n(?![01]\r?$).*)*\r?\n([10]+)\r?$", re.M)
dictOfFiles = []
with open(r'txfile','rb') as fin:
    dictOfFiles = [(k.decode('utf-8'), (int)(v.decode('utf-8'))) for k, v in fileRegex.findall(fin.read())]

Затем print(dictOfFiles) возвращает

[('stringOfFilePathandName.cpp': 0), ('stringOfFilePathandName2.cpp': 1)....]

См. Демоверсию regex .

ПРИМЕЧАНИЯ

  • Для работы этого многострочного регулярного выражения необходимо прочитать весь файл в память, поэтому я использую fin.read()
  • Когда вы читаете в файле в двоичном режиме, CR не удаляются, поэтому я добавляю \r? (необязательный CR) перед каждым \n
  • Чтобы преобразовать байтовые строки в строки Unicode, нам нужно использовать .decode('utf-8') для результатов.

Сведения о регулярном выражении (на случай, если вам потребуется изменить его позже):

  • ^ - начало строки (из-за re.M, ^ соответствует начальным позициям линии)
  • \d+ - 1+ цифр
  • \s+ - 1+ пробелов
  • (\S+\.cpp) - Группа 1: 1+ непробельных символа, а затем .cpp
  • \s - пробел
  • .* - 0+ символов, кроме символов разрыва строки, как можно больше
  • (?:\r?\n(?![01]\r?$).*)*
  • \r?\n - разрыв строки CRLF или LF
  • ([10]) - Группа 2: a 1 или 0
  • \r? - опция CR
  • $ - конец строки.
...