【问题标题】:Regular Expression for MCQ type stringsMCQ 类型字符串的正则表达式
【发布时间】:2020-06-29 21:02:36
【问题描述】:

如何从文本文档中提取多项选择题及其选项。每个问题都以数字和点开头。每个问题可以跨越多行,并且可能/可能没有句号或问号。 我想制作一本带有问题编号和相应问题和选项的字典。 我正在为此使用python。

17.
If you go on increasing the stretching force on a wire in a
guitar, its frequency.
(a)
increases
(b)
decreases
(c)
remains unchanged
(d)
None of these

some random text between questions
18.
A vibrating body
(a)
will always produce sound
(b)
may or may not produce sound if the amplitude of
vibration is low
(c)
will produce sound which depends upon frequency
(d)
None of these
19.
The wavelength of infrasonics in air is of the order of
(a)
100 m
(b)
101 m
(c)
10–1 m
(d)
10–2 m

【问题讨论】:

  • 我注意到其中一个问题的作者要求您选择他们的答案,如果它回答了您的问题。看到这一点很不寻常,特别是当问题很新鲜时,尽管有时成员会在一段时间后建议提问者考虑选择最有帮助的答案(如果有的话)。这部分是因为像您这样的新成员可能没有意识到他们应该做出选择。 Here is some information 关于那个...
  • ...请记住,不要急于做出选择。快速选择可能会阻止其他答案,并可能增加所选答案中的缺陷未被发现的机会。此外,虽然您可以随时更改您的选择(例如,如果您更喜欢在做出选择后发布的答案),但通常最好稍等片刻。许多人在这里等待至少几个小时,有些更长。

标签: python regex file-io


【解决方案1】:

解决方案

假设您的问题来自questions.txt 文件。

17.
If you go on increasing the stretching force on a wire in a
guitar, its frequency.
(a)
increases
(b)
decreases
(c)
remains unchanged
(d)
None of these

some random text between questions
18.
A vibrating body
(a)
will always produce sound
(b)
may or may not produce sound if the amplitude of
vibration is low
(c)
will produce sound which depends upon frequency
(d)
None of these
19.
The wavelength of infrasonics in air is of the order of
(a)
100 m
(b)
101 m
(c)
10–1 m
(d)
10–2 m

根据要求解析 questions.txt 的 Python 代码。

import re

filename = 'questions.txt'
questions = []

with open(file=filename, mode='r', encoding='utf8') as f:
    lines = f.readlines()

    is_label = False  # means matched: 17.|(a)|(b)|(c)|(d)
    statement = option_a = option_b = option_c = option_d = ''

    for line in lines:
        if re.match(r'^\d+\.$', line):
            is_statement = is_label = True
            is_option_a = is_option_b = is_option_c = is_option_d = False
        elif re.match(r'^\(a\)$', line):
            is_option_a = is_label = True
            is_statement = is_option_b = is_option_c = is_option_d = False
        elif re.match(r'^\(b\)$', line):
            is_option_b = is_label = True
            is_statement = is_option_a = is_option_c = is_option_d = False
        elif re.match(r'^\(c\)$', line):
            is_option_c = is_label = True
            is_statement = is_option_a = is_option_b = is_option_d = False
        elif re.match(r'^\(d\)$', line):
            is_option_d = is_label = True
            is_statement = is_option_a = is_option_b = is_option_c = False
        else:
            is_label = False

        if is_label:
            continue

        if is_statement:
            statement += line
        elif is_option_a:
            option_a = line.rstrip()
        elif is_option_b:
            option_b = line.rstrip()
        elif is_option_c:
            option_c = line.rstrip()
        elif is_option_d:
            option_d = line.rstrip()

            if statement:
                questions.append({
                    'statement': statement.rstrip(),
                    'options': [option_a, option_b, option_c, option_d]
                })
                statement = option_a = option_b = option_c = option_d = ''

print(questions)

输出

[
  {
    "statement": "If you go on increasing the stretching force on a wire in a\nguitar, its frequency.",
    "options": [
      "increases",
      "decreases",
      "remains unchanged",
      "None of these"
    ]
  },
  {
    "statement": "A vibrating body",
    "options": [
      "will always produce sound",
      "vibration is low",
      "will produce sound which depends upon frequency",
      "None of these"
    ]
  },
  {
    "statement": "The wavelength of infrasonics in air is of the order of",
    "options": [
      "100 m",
      "101 m",
      "10–1 m",
      "10–2 m"
    ]
  }
]

旁注

  • some random text between questions 之类的文本会被忽略
  • 多行语句的问题保持原样(意味着有意不删除换行符)。您可以选择将\n 替换为<space> 字符。

【讨论】:

  • 字典推导(就像我使用的那个)比 for 循环更快。虽然我确实发现您的数据更有条理。
  • 我们如何处理跨多行的选项? @Ahmed 的答案需要在问题之间空行。但是文本文档不满足他的条件。
  • 问题是您如何知道选项 (d) 何时结束以及some random text between questions 何时开始?
【解决方案2】:

Hamza 的答案很好,但它忽略了一个事实,即答案可能是多行的。

更好的解决方案: (假设有问题的文本在 data.txt 文件中)

import re

with open('data.txt', 'r', encoding='utf8') as file:
    data = file.read()

questions = re.split(r'\n\s*\n', data) #splits the questions into a list assuming there is no empty lines inside each question
final_questions = []

for question in questions:
    if question != None and '(a)' in question: #extra check to make sure that this a question
        statement = re.findall(r'[^(]+', question)[0].replace('\n', ' ').rstrip()
        option_a = re.findall(r'\(a\)[^(]+', question)[0].replace('\n', ' ').rstrip()
        option_b = re.findall(r'\(b\)[^(]+', question)[0].replace('\n', ' ').rstrip()
        option_c = re.findall(r'\(c\)[^(]+', question)[0].replace('\n', ' ').rstrip()
        option_d = re.findall(r'\(d\)[^(]+', question)[0].replace('\n', ' ').rstrip()
        final_questions.append({
                    'statement': statement.rstrip(),
                    'options': [option_a, option_b, option_c, option_d]
                })

print(final_questions)

输出:

[
   {
      "statement":"17. If you go on increasing the stretching force on a wire in a guitar, its frequency.",
      "options":[
         "(a) increases",
         "(b) decreases",
         "(c) remains unchanged",
         "(d) None of these"
      ]
   },
   {
      "statement":"18. A vibrating body",
      "options":[
         "(a) will always produce sound",
         "(b) may or may not produce sound if the amplitude of vibration is low",
         "(c) will produce sound which depends upon frequency",
         "(d) None of these"
      ]
   },
   {
      "statement":"19. The wavelength of infrasonics in air is of the order of",
      "options":[
         "(a) 100 m",
         "(b) 101 m",
         "(c) 10–1 m",
         "(d) 10–2 m"
      ]
   }
]

注意::每个问题之间应至少有一个空行

【讨论】:

    【解决方案3】:

    正则表达式:\d+\.([^(]+) 它得到数字,然后是一个点。

    然后它会捕获所有不是( 的东西(答案的开头)。

    如果您不确定是否那么简单,请测试正则表达式 here

    Python 代码:

    import re # Imports the standard regex module
    
    text_doc = """
    17.
    If you go on increasing the stretching force on a wire in a
    guitar, its frequency.
    (a)
    increases
    (b)
    decreases
    (c)
    remains unchanged
    (d)
    None of these
    
    some random text between questions
    18.
    A vibrating body
    (a)
    will always produce sound
    (b)
    may or may not produce sound if the amplitude of
    vibration is low
    (c)
    will produce sound which depends upon frequency
    (d)
    None of these
    19.
    The wavelength of infrasonics in air is of the order of
    (a)
    100 m
    (b)
    101 m
    (c)
    10–1 m
    (d)
    10–2 m
    """
    
    question_getter = re.compile('\\d+\\.([^(]+)')
    
    print(question_getter.findall(text_doc))
    
    

    编辑:但是由于很多人在这里解析东西,我想我也会解析东西

    获取可能答案的正则表达式:\([a-zA-Z]+\)\n(.+)

    proof

    更新的 Python:

    import re # Imports the standard regex module
    
    
    text_doc = """
    17.
    If you go on increasing the stretching force on a wire in a
    guitar, its frequency.
    (a)
    increases
    (b)
    decreases
    (c)
    remains unchanged
    (d)
    None of these
    
    some random text between questions
    18.
    A vibrating body
    (a)
    will always produce sound
    (b)
    may or may not produce sound if the amplitude of
    vibration is low
    (c)
    will produce sound which depends upon frequency
    (d)
    None of these
    19.
    The wavelength of infrasonics in air is of the order of
    (a)
    100 m
    (b)
    101 m
    (c)
    10–1 m
    (d)
    10–2 m
    """
    
    question_getter = re.compile('\\d+\\.([^(]+)')
    answer_getter = re.compile('\\([a-zA-Z]+\\)\\n(.+)')
    
    
    # This is where the magical parsing happens
    # It could've been organized differently
    parsed = {question:answer_getter.findall(text_doc)
        for question in question_getter.findall(text_doc)
    }
    
    print(parsed)
    
    

    【讨论】:

    • question_getter 效果很好。选项呢?
    • @VigneshVeeresh 选项?好的:正则表达式:\([a-zA-Z]\)\n(.+) 用于获取选项 我应该编辑我的帖子以便解析整个文档吗?
    • @VigneshVeeresh ,如果我的回答回答了您的问题,您可以打勾以使其成为例外答案。好吧,只有你喜欢我的。
    【解决方案4】:

    您可以将以下正则表达式与 Python 的标准 re 模块一起使用来匹配每个问题。

    r'(?P<number>\d+)\. *\r?\n(?P<question>(?:(?!\([a-z]\)).*\r?\n)+)(?P<options>(?:(?!(?<=\n)\d+\. *\r?\n).*\r?\n)+)'
    

    问题编号将包含在捕获组(命名)number 中,问题本身将包含在捕获组question 中,选项将包含在捕获组options 中。

    然后可以使用 Python 代码轻松获取捕获组的内容并根据需要进行处理。例如,可以构造一个问题数组,每个问题都是一个带有数字、问题和选​​项键的哈希,或者可能是一个哈希,其中键是问题编号,值是带有问题和选项键的哈希。

    Start your engine!

    Python 的正则表达式引擎执行以下操作。

    (?P<number>\d+)  : match 1+ digits in capture group 'number'
    \. *\r?\n        : match '.' 0+ spaces, line terminator 
    (?P<question>    : begin capture group 'question'
      (?:            : begin non-capture group
        (?!          : begin negative lookahead
          \([a-z]\)  : match '(', one lowercase letter, ')'
        )            : end negative lookahead
        .*\r?\n      : match 0+ characters, '\r' optionally, '\n'
      )              : end non-capture group
      +              : execute non-capture group 1+ times
    )                : end capture group 'question'
    (?P<options>     : begin capture group 'options'
      (?:            : begin non-capture group
        (?!          : begin negative lookahead
          (?<=\n)    : positive lookbehind asserts next character is
                       preceded by a '\n'
          \d+        : match 1+ digits
          \. *\r?\n  : match '.' 0+ spaces, line terminator 
        )            : end negative lookahead
        .*\r?\n      : match 0+ characters, '\r' optionally, '\n'
      )              : end non-capture group
      +              : execute non-capture group 1+ times
    )                : end capture group 'options'
    

    在两个位置,我匹配任何字符 (.)。这当然可以用一个限制可能性的字符类来代替,例如[a-zA-Z\d() -–]ref

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-09
      • 1970-01-01
      相关资源
      最近更新 更多