Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/317.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 从文本文件中提取节名和起始页_Python_Regex - Fatal编程技术网

Python 从文本文件中提取节名和起始页

Python 从文本文件中提取节名和起始页,python,regex,Python,Regex,我有一个类似这样的字符(“…”实际上不在原始文本中,但我使用它来忽略一些冗长且可能不相关的文本;^L”表示每页的开头(注意:“^L”是作为控制字符键入的,而不是字符“^”和字符“L”): 所需输出为: 1.2.1 Abstraction by Parameters 6 1.2.2 Abstraction by Specification 7 2.1.2 Type Checking 14 我想做的是提取第三级节名和它们出现的页码 第三级节名始终显示在一行中 出现第三级节名的页码

我有一个类似这样的字符(“…”实际上不在原始文本中,但我使用它来忽略一些冗长且可能不相关的文本;^L”表示每页的开头(注意:“^L”是作为控制字符键入的,而不是字符“^”和字符“L”):

所需输出为:

1.2.1 Abstraction by Parameters 6    
1.2.2 Abstraction by Specification 7    
2.1.2 Type Checking 14
我想做的是提取第三级节名它们出现的页码

  • 第三级节名始终显示在一行中
  • 出现第三级节名的页码是单行中出现的第一个数字,位于节名前最后一个“^L”之后
我想知道如何用Python编写程序来实现这一点

以下是我到目前为止的想法:

  • 用于匹配第三级节名的正则表达式模式是
    ^\s*\d+\.\d+\.\d+.*\n
  • 在每个页面中,“^L”表示页面的开头(注意:“^L”是作为控制字符键入的,而不是字符“^”和字符“L”)。将“^L”部分与页面页码匹配的正则表达式模式是
    ^L.*\n.*\s*\d+\s*\n

我仍然不知道如何继续。谢谢你的启发!

你需要用的字符是
\x0C
/
^L
/
chr(12)

迭代法

import re

def foo(lines):
    name = ''
    pages = []
    page_break_char = chr(0xC)

    for line in lines:

        if re.match('^\s*\d+\.\d+\.\d+.*', line):
            name = line
        elif re.match('^\d+$', line):
            pages.append(line)
        elif page_break_char in line:

            if name:
                yield name, pages

            del pages[:]

    if name:
        yield name, pages
用法

text = '''

.. A language ...

\x0CIntroduction

6

A preferable alternative is ...

1.2.1 Abstraction by Parameters 
Abstraction allows us, ...

\x0C1.2 Abstraction

7

and ...

1.2.2 Abstraction by Specification 
... ...
\x0CAn Overview of CLU

14

In addition to ...

2.1.2 Type Checking ...

'''

lines = text.split('\n')
for name, pages in foo(lines):
    print name, ' '.join(pages)
输出


在线演示
你必须了解文本-我需要仔细检查它才能完成这项工作。页码看起来像-
FormFeedPageOnlineFeed
LineFeedPageOnlineFeed
,一个页面上可能有多个三级标题,所以从一个正则表达式开始,它将提取整个页面并捕获页码

pages = re.compile(r'[\n\f](\d+)\n.*?(?=[\n\f](\d+)\n)', flags = re.DOTALL | re.MULTILINE)
然后在每页中搜索第三级标题,并将其全部放在一起

third_level = re.compile('\n(\d+\.\d+\.\d+[^\n]*)')
for page in pages.finditer(s):
    page_no = page.group(1)
    for item in third_level.finditer(page.group()):
        print '{}\t{}'.format(item.group(1), page_no)
这会产生54个三级标题。不幸的是,这是你的文本所独有的。在我弄明白这一点的时候,我可以用一个好的文本编辑器半手动地提取信息,该编辑器可以进行正则表达式搜索(这就是我弄明白并验证它所做的)

您可能能够优化
页面
-
*?
与前瞻断言相结合的页面有点异味


编辑以提取第一级和第二级标题,然后与第三级标题合并。对于OP的文件是唯一的

提取内容和附录信息,单独提取附录以简化合并

现在查找所有第三级标题和页码-同上,但存储在列表中

third_level = re.compile('\n(\d+\.\d+\.\d+[^\n]*)')
pages = re.compile(r'[\n\f](\d+)\n.*?(?=[\n\f](\d+)\n)', flags = re.DOTALL | re.MULTILINE)

third_levels = list()

for page in pages.finditer(s, content_end):
    page_no = page.group(1)
    for item in third_level.finditer(page.group()):
        third_levels.append('{}\t{}'.format(item.group(1), page_no))
合并内容和第三级标题,排序并添加附录标题

a = content_items + third_levels

def key(item):
    '''Extract digits from the beginning of item for a sort key.

    item is a string
    >>> key('1 ABC')
    (1, None, None)
    >>> key('1.2 ABC')
    (1, 2, None)
    >>> key('1.2.3 ABC')
    (1, 2, 3)
    >>>
    '''
    item = item.split()
    item = item[0].split('.')
    a, b, c = None, None, None
    try:
        a = int(item[0])
    except ValueError as e:
        pass
    try:
        b = int(item[1])
    except IndexError as e:
        pass
    try:
        c = int(item[2])
    except IndexError as e:
        pass
    return a, b, c

a.sort(key = key)
a.extend(appendices)

请从输入中删除
符号。
…一种语言…
我指的是格式。这是
r'^\^'
regex匹配字符串
^
符号吗?只需发布实际的文件内容。我会编辑您的帖子。开头是否有空格?中间是否有空行?谢谢您的评论。可能有,也可能有每行开头不能有空格。两行文字之间可能有空行,也可能没有空行。我给出了一些观察结果,可能有助于识别文章中的第三级章节名称和页码。+1非常感谢!这里比我之前给出的简化文本要长。您的程序在上面运行顺利,尽管输出是h作为重复检测,虽然每个第三级节名只出现一次,并且一些检测的页码虽然显示但未找到,但有些检测找到了不止一个页码。我们是否过度简化了对文本的假设?谢谢。您的代码按预期工作。假设我有regex的
second_level
first_level
用于其他级别的章节。我们如何根据当前代码创建一些代码,以按原始文本中显示的顺序输出章节标题、页码和级别?(例如,
1这是第1章,…,
2.1这是第302节,…,
4.3.2这是第603小节
)?@Tim-我想我会尝试直接从目录部分提取它,并将其存储在一个集合中。然后使用上面的代码将所有三级内容存储在集合中。然后按标题编号对集合进行排序。谢谢。(1)我提到的不是从目录而是从主文本中提取其他级别的原因,是因为其他文本文件可能没有目录假设提取了所有级别的标题,要重新创建目录,我们是否必须根据它们在文本文件中的位置对它们进行排序,并根据它们的级别对它们进行缩进?(1)请记住,这是您提到的文本所特有的,我发现在模式中存在一些不一致的地方,这些模式似乎是任意的(我没有解释其中的几个)。(2)我想到了一种不同的方法,
re.match
对象确实有
start
end
span
方法返回匹配的索引-您可以使用它们进行排序。也许可以创建一个匹配的模式(PageNo或LevelOne或LevelTwo或LevelTwo或Appendix),查找并保存所有匹配项,按位置排序,在有多少个点(?)上构造缩进,作为开始。
content_item = re.compile('\d+\.?\d*? [^\n]*')
appendix_item = re.compile('Appendix[^\n]*|[ABC]\.[^\n]*')

# Indices to limit the contents search
content_start = re.search('\f\fContents', s).span()[1]
content_end = re.search('\f\fPreface', s).span()[0]

content_items = content_item.findall(s, content_start, content_end)
appendices = appendix_item.findall(s, content_start, content_end)
third_level = re.compile('\n(\d+\.\d+\.\d+[^\n]*)')
pages = re.compile(r'[\n\f](\d+)\n.*?(?=[\n\f](\d+)\n)', flags = re.DOTALL | re.MULTILINE)

third_levels = list()

for page in pages.finditer(s, content_end):
    page_no = page.group(1)
    for item in third_level.finditer(page.group()):
        third_levels.append('{}\t{}'.format(item.group(1), page_no))
a = content_items + third_levels

def key(item):
    '''Extract digits from the beginning of item for a sort key.

    item is a string
    >>> key('1 ABC')
    (1, None, None)
    >>> key('1.2 ABC')
    (1, 2, None)
    >>> key('1.2.3 ABC')
    (1, 2, 3)
    >>>
    '''
    item = item.split()
    item = item[0].split('.')
    a, b, c = None, None, None
    try:
        a = int(item[0])
    except ValueError as e:
        pass
    try:
        b = int(item[1])
    except IndexError as e:
        pass
    try:
        c = int(item[2])
    except IndexError as e:
        pass
    return a, b, c

a.sort(key = key)
a.extend(appendices)