【问题标题】:Python,how to extract text between two markers multiple times throughout text file?Python,如何在整个文本文件中多次提取两个标记之间的文本?
【发布时间】:2019-10-22 21:10:01
【问题描述】:

我无法从 txt 文件中提取部分文本。使用 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.cppsingleInteger 值保存到字典中,例如 {stringOfFilePathandName:(0 或 1)}

文本包含我不需要的其他文件扩展名(如 .cpp)。另外,我不知道文件的编码,所以我将其读取为二进制。

我的问题与以下链接中解决的问题有共同之处:

Python read through file until match, read until next pattern

https://sopython.com/canon/92/extract-text-from-a-file-between-two-markers/ - 我不太明白

python - Read file from and to specific lines of text- 这是我尝试复制的,但仅适用于一个实例。我需要在整个文件中迭代这个过程。

目前我已经尝试过这种方法,它只适用于一次:

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

【问题讨论】:

  • 请提供一些真实的字符串
  • 您对{b'stringOfFilePathandName.cpp': b'0', b'stringOfFilePathandName2.cpp': b'1'} 输出是否满意?或者您想在结果中包含 UTF8 字符串,例如 {'stringOfFilePathandName.cpp': '0', 'stringOfFilePathandName2.cpp': '1'}
  • @WiktorStribiżew 是的,没关系,我可以稍后再删除,谢谢
  • @Nikos 你不能去掉b 前缀,你需要重新编码这些值。请参阅下面的答案。
  • @Jan 我添加了一个链接

标签: python regex parsing text


【解决方案1】:

你可以使用

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 demo

注意事项

  • 您需要将所有文件读入内存才能使这个多行正则表达式工作,因此我使用fin.read()
  • 当您以二进制模式读取文件时,不会删除 CR,因此我在每个 \n 之前添加了 \r?(可选 CR)
  • 要将字节字符串转换为 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 组:10
  • \r? - 一个可选的 CR
  • $ - 行尾。

【讨论】:

  • 在运行 filename 正则表达式(主要是检查 .cpp 出现多少次)和您的解决方案(9765 与 3361)时,我得到不同的总出现次数。也许我省略或误解了文本格式。为了复制,我在初始帖子中添加了文本文件链接。
  • @Nikos 模式是正确的,你只是有重复的文件名。如果它们不是唯一的,不要使用字典,使用元组列表。我更新了代码。
  • 哦,当然,我的无知...非常感谢。如果我可以问更多问题,您能否提供有关正则表达式的指南/额外资源?
  • @Nikos 我不知道你的正则表达式知识水平,所以我只能建议在regexone.com 完成所有课程,阅读regular-expressions.inforegex SO tag description(还有许多其他链接到很棒的在线资源),以及名为 What does the regex mean 的社区 SO 帖子。另外,rexegg.com 值得一看。
  • @NikosH。嗨,我已经开始上传一些 regex videos on Youtube,如果你想了解更多关于正则表达式的信息,请随时查看它们。由于我是 Youtube 的新手,如果有任何建议,我将不胜感激。
【解决方案2】:

一种可能性是将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 进行一次传递。如果您不能这样做,那么您将需要继续使用当前的解析方法。

【讨论】:

  • 它不适用于当前代码,因为 OP 正在逐行读取文件。 input 必须保存整个文件内容。此外,它不会返回字典(尽管它很容易调整)。
  • @WiktorStribiżew 刚刚注意到...我在回答中添加了一个警告。
  • 我使用 Wiktor 的答案中的 decode ,它似乎有效。正如你提到的,我首先将整个文件读入一个变量并运行 .findall。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-11
相关资源
最近更新 更多