синтаксический анализ раздела python regex mediawiki

#python #regex

#python #регулярное выражение

Вопрос:

У меня есть текст, похожий на следующий:

 ==Mainsection1==  
Some text here  
===Subsection1.1===  
Other text here  

==Mainsection2==  
Text goes here  
===Subsecttion2.1===  
Other text goes here. 
  

В приведенном выше тексте основные разделы 1 и 2 имеют разные имена, которые могут быть любыми по желанию пользователя. То же самое касается подразделов.

Что я хочу сделать с регулярным выражением, так это получить текст основного раздела, включая его подраздел (если он есть). Да, это из википедии. Все имена основных разделов начинаются с == и заканчиваются на == все подразделы, которые имеют больше, чем имя 2== in there.

 regex =re.compile('==(.*)==([^=]*)', re.MULTILINE)  
regex.findall(text)
  

Но приведенное выше возвращает каждый отдельный раздел.
Это означает, что он отлично возвращает основной раздел, но видит подраздел самостоятельно.

Я надеюсь, что кто-нибудь сможет мне помочь с этим, поскольку это некоторое время меня беспокоило

редактировать: результат должен быть:

 [('Mainsection1', 'Some text heren===Subsection1.1===  
Other text heren'), ('Mainsection2', 'Text goes heren===Subsecttion2.1===  
Other text goes here.n')]
  

Редактировать 2:
Я переписал свой код, чтобы не использовать регулярное выражение. Я пришел к выводу, что достаточно просто просто разобрать его самостоятельно. Что делает его немного более читаемым для меня.

Итак, вот мой код:

 def createTokensFromText(text):    
    sections = []
    cur_section = None
    cur_lines = []


    for line in text.split('n'):
        line = line.strip()
        if line.startswith('==') and not line.startswith('==='):
            if cur_section:
                sections.append( (cur_section, 'n'.join(cur_lines)) )
                cur_lines = []
            cur_section = line
            continue
        if cur_section:
            cur_lines.append(line)

    if cur_section:
        sections.append( (cur_section, 'n'.join(cur_lines)) )
    return sections
  

Спасибо всем за помощь!

Все предоставленные ответы мне очень помогли!

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

1. Может быть, вам было бы лучше использовать уже существующий анализатор разметки викимедиа? На первый взгляд, из secure.wikimedia.org/wikipedia/mediawiki/wiki /… , mwlib выглядит наиболее многообещающим.

2. Это не очень хорошая работа для регулярных выражений. Вам лучше использовать настоящий синтаксический анализатор (например, PLY или PyParsing), или еще лучше: библиотеку, которую уже написал кто-то другой.

3. Возможно, это не очень хорошая работа для регулярных выражений, но это, безусловно, выполнимо — проблема в том, насколько ваш конкретный синтаксис близок к любому доступному синтаксическому анализатору wiki — и какие у вас могут быть причины для отклонения от «стандартных» или, по крайней мере, популярных синтаксисов

4. Пока это кажется выполнимым с регулярным выражением. Но спасибо за ссылку, mwlib выглядит многообещающим

Ответ №1:

Во-первых, это должно быть известно, я немного знаю о Python, но я никогда не программировал на нем формально… Codepad сказал, что это работает, так что поехали! : D — Извините, выражение настолько сложное:

 (?<!=)==([^=] )==(?!=)([sS]*?(?=$|(?<!=)==[^=] ==(?!=)))
  

Я полагаю, это делает то, о чем вы просили! на Codepad этот код:

 import re

wikiText = """==Mainsection1==
Some text here
===Subsection1.1===
Other text here

==Mainsection2==
Text goes here
===Subsecttion2.1===
Other text goes here. """

outputArray = re.findall('(?<!=)==([^=] )==(?!=)([sS]*?(?=$|(?<!=)==[^=] ==(?!=)))', wikiText)
print outputArray
  

Выдает этот результат:

 [('Mainsection1', 'nSome text heren===Subsection1.1===nOther text herenn'), ('Mainsection2', 'nText goes heren===Subsecttion2.1===nOther text goes here. ')]
  

РЕДАКТИРОВАТЬ: разбитое выражение, по сути, говорит:

 01 (?<!=)        # First, look behind to assert that there is not an equals sign
02 ==            # Match two equals signs
03 ([^=] )       # Capture one or more characters that are not an equals sign
04 ==            # Match two equals signs
05 (?!=)         # Then verify that there are no equals signs following this
06 (             # Start a capturing group
07   [sS]*?    #   Match zero or more of ANY character (even CrLf), but BE LAZY
08   (?=         #   Look ahead to verify that either...
09     $         #     this is the end of the 
10     |         #     -OR-
11     (?<!=)    #     when I look behind there is no equals sign
12     ==        #     then there are two equals signs
13     [^=]      #     then one or more characters that are not equals signs
14     ==        #     then two equals signs
15     (?!=)     #     then verify that there are no equals signs following this
16   )           #   End look-ahead group
17 )             # End capturing group 
  

Строка 03 и Строка 06 определяют группы захвата для заголовка основного раздела и содержимого основного раздела соответственно.

Строка 07 требует много объяснений, если вы не очень хорошо владеете регулярными выражениями…

  • s И S внутри символьного класса [] будет соответствовать всему, что является пробелом или не является пробелом (т. Е. Чему УГОДНО) — одной из альтернатив этому является использование . оператора, но в зависимости от параметров вашего компилятора (или возможности указывать параметры) это может соответствовать или не соответствовать CrLf (или возврату каретки / переводу строки). Поскольку вы хотите сопоставить несколько строк, это самый простой способ обеспечить совпадение.
  • *? В конце означает, что он будет соответствовать нулю или более экземплярам символьного класса «anything», но БУДЬТЕ ЛЕНИВЫ В ЭТОМ — «ленивые» кванторы (иногда называемые «неохотными») противоположны «жадному» квантору по умолчанию (без ? следования ему) и не будут использоватьисходный символ, если только следующий за ним источник не может быть сопоставлен с частью выражения, которая следует за отложенным квантификатором. Другими словами, это будет использовать любые символы, пока не найдет либо конец исходного текста, ЛИБО другой основной раздел, который указан ровно двумя и только двумя знаками равенства по обе стороны от одного или нескольких символов, которые не являются знаком равенства (включая пробелы). Без оператора lazy он попытался бы использовать весь исходный текст, а затем «вернуться назад», пока он не сможет соответствовать одному из элементов после него в выражении (конец источника или заголовок раздела)

Строка 08 — это «прогноз», который указывает, что выражение follwoing должно быть сопоставлено, но не должно использоваться.

ЗАВЕРШЕНИЕ РЕДАКТИРОВАНИЯ

AFAIK, это должно быть настолько сложным, чтобы правильно исключить подразделы… Если вы хотите сопоставить имя раздела и содержимое раздела с именованными группами, вы можете попробовать это:

 (?<!=)==(?P<SectionName>[^=] )==(?!=)(?P<SectionContent>[sS]*?(?=$|(?<!=)==[^=] ==(?!=)))
  

Если хотите, я могу разбить его для вас! Просто спросите! РЕДАКТИРОВАНИЕ (см. Редактирование выше) ЗАВЕРШЕНИЕ РЕДАКТИРОВАНИЯ

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

1. Это также работает, большое спасибо. Не могли бы вы разбить регулярное выражение для меня?

2. Большое спасибо! очень понятное объяснение

3. @Fox если вам помог мой ответ или другой ответ, пожалуйста, выберите лучший, нажав на галочку / галочку под стрелками голосования в верхней части ответа — это помогает поощрять более хорошие и полезные ответы в будущем: D

Ответ №2:

Проблема здесь в том, что ==(.*)== совпадения ==(=Subsection=)== , поэтому первое, что нужно сделать, это убедиться = , что внутри заголовка нет : ==([^=]*)==([^=]*) .

Затем нам нужно убедиться, что = перед началом совпадения нет, в противном случае первый = из трех игнорируется, а субтитры совпадают. Это поможет: (?<!=)==([^=]*)==([^=]*) , это означает «Совпадения, если им не предшествует …».

Мы также можем сделать это в конце, чтобы убедиться, что это дает конечный результат (?<!=)==([^=]*)==(?!=)([^=]*) .

 >>> re.findall('(?<!=)==([^=]*)==(?!=)([^=]*)', x,re.MULTILINE)
[('Mainsection1', 'nSome text heren'),
 ('Mainsection2', 'nText goes heren')]
  

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

 >>> re.findall('(?<!=)==([^=]*)==n([^=]*)', x,re.MULTILINE)
[('Mainsection1', 'Some text heren'), ('Mainsection2', 'Text goes heren')]
  

Редактировать :

 section = re.compile(r"(?<!=)==([^=]*)==(?!=)")

result = []
mo = section.search(x)
previous_end = 0
previous_section = None
while mo is not None:
    start = mo.start()
    if previous_section:
        result.append((previous_section, x[previous_end:start]))
    previous_section = mo.group(0)
    previous_end = mo.end()
    mo = section.search(x, previous_end)
result.append((previous_section, x[previous_end:]))
print result
  

Это проще, чем кажется: мы постоянно ищем заголовок раздела после предыдущего и добавляем его к результату с текстом между началом этого заголовка и концом предыдущего. Настройте его в соответствии с вашим стилем и вашими потребностями. Результатом является :

 [('==Mainsection1==',
  '  nSome text here  n===Subsection1.1===  nOther text here  nn'),
 ('==Mainsection2==',
  '  nText goes here  n===Subsecttion2.1===  nOther text goes here. ')]
  

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

1. Хм, мы добираемся туда, но я что-то упускаю. Мне все еще нужен текст из подразделов, чтобы результатом было:[(‘Mainsection1’, ‘Некоторый текст здесь n === Подраздел1.1 === Другой текст здесь n’ ) Пока спасибо

2. Боюсь, мы достигли предела моих знаний о регулярных выражениях. Что я хотел бы знать, так это использовать первую часть моего regex ( (?<!=)==([^=]*)==(?!=) ) для определения заголовков, а затем просто получить текст между совпадениями. Вы хотите, чтобы я развил эту идею, или это не вариант?

3. Это может сработать, поэтому, если вы хотите, пожалуйста, продолжайте! Большое спасибо до сих пор