Я пытаюсь извлечь определенные данные из LookML, определенного языка разметки. Если это пример кода:

explore: explore_name {}
explore: explore_name1 {
  label: "name"
  join: view_name {
      relationship: many_to_one
      type: inner
      sql_on: ${activity_type.activity_name}=${activity_type.activity_name} ;;
  }
}
explore: explore_name3 {}

Тогда я получил бы список, похожий на:

  • explore: character_balance {}
  • label: "name"
    join: activity_type {
      relationship: many_to_one
      type: inner
      sql_on: ${activity_type.activity_name}=${activity_type.activity_name} ;;
    }```
    
  • explore: explore_name4 {}

По сути, я начинаю матч в «исследовать» и заканчиваю его, когда нахожу еще один «исследовать» , который затем начинает следующий матч.

Вот то, что у меня было раньше, которое совпадает по всем строкам, пока не найдет ;, и это прекрасно работает: 'explore:\s[^;]*'. Но это останавливается на ';', если оно есть.

Как бы я изменил это так, чтобы он убрал все между «исследовать» и «исследовать»? Просто заменив ';' в моем регулярном выражении «исследовать» вместо этого останавливается всякий раз, когда он находит букву, которая соответствует чему-либо в [e, x, p, l, o, r, e] - что не является тем поведением, которое я хочу. Снятие квадратных скобок и ^ в конечном итоге разбивает все так, что он не может запрашивать несколько строк.

Что мне здесь делать?

0
Aman Jha 1 Июл 2019 в 23:19

3 ответа

Лучший ответ

Наивный подход заключается в достижении следующего слова «исследовать». Но если по какой-либо причине строковое значение содержит это слово, вы получите неверные результаты. Та же проблема, если вы пытаетесь прекратить использовать фигурные скобки, когда строка содержит вложенные скобки.

Вот почему я предлагаю более точное описание синтаксиса вашей строки с учетом строк и вложенных фигурных скобок. Поскольку модуль re не имеет функции рекурсии (для работы с вложенной структурой), я буду использовать pypi / модуль regex вместо этого:

import regex

pat = r'''(?xms)
    \b explore:
    [^\S\r\n]* # optional horizontal whitespaces
    [^\n{]* # possible content of the same line
    # followed by two possibilities
    (?: # the content stops at the end of the line with a ;
        ; [^\S\r\n]* $
      | # or it contains curly brackets and spreads over eventually multiple lines
        ( # group 1
            {
                [^{}"]*+ # all that isn't curly brackets nor double quotes
                (?:
                    " [^\\"]*+ (?: \\. [^\\"]* )*+ " # contents between quotes
                    [^{}"]*

                  |
                    (?1) # nested curly brackets, recursion in the group 1
                    [^{}"]*
                )*+
            }
        )
    )'''

results = [x.group(0) for x in regex.finditer(pat, yourstring)]

демонстрация

Чтобы быть более строгим, вы можете добавить поддержку для строки в одинарных кавычках, а также запретить использование «explore:» в начале шаблона в строке, используя конструкцию (*SKIP)(*FAIL).

1
Casimir et Hippolyte 1 Июл 2019 в 21:30

Хотя в Regex это выполнимо, вам следует использовать синтаксический анализатор, который понимает формат, поскольку решение Regex будет довольно хрупким.

Сказав это, вот решение Regex с включенным режимом DOTALL (, где . соответствует любому символу, включая символ новой строки ):

re.findall(r'explore:.*?\}', text, re.DOTALL)
  • explore: соответствует буквально
  • .*?\} не жадно соответствует следующему }

< Сильный > Пример:

In [1253]: text = '''explore: character_balance {} 
      ...: explore: tower_ends { 
      ...:   label: "Tower Results" 
      ...:   join: activity_type { 
      ...:       relationship: many_to_one 
      ...:       type: inner 
      ...:       sql_on: ${activity_type.activity_name}=${wba_fact_activity.activity_name} ;; 
      ...:   } 
      ...: } 
      ...: explore: seven11_core_session_start {}'''                                                                                                                                                        

In [1254]: re.findall(r'explore:.*?\}', text, re.DOTALL)                                                                                                                                     
Out[1254]: 
['explore: character_balance {}',
 'explore: tower_ends {\n  label: "Tower Results"\n  join: activity_type {\n      relationship: many_to_one\n      type: inner\n      sql_on: ${activity_type.activity_name}',
 'explore: seven11_core_session_start {}']
0
heemayl 1 Июл 2019 в 20:30

Вы можете использовать не жадное совпадение с проверочным утверждением, чтобы проверить наличие другого explore: или конца строки. Пытаться:

'explore:.*?(?=explore|$)'

0
tzaman 1 Июл 2019 в 20:35