Регулярное выражение Python для сопоставления нескольких слов в строке без перехода на следующую строку

Я пишу парсер для анализа вывода ниже:

    admin@str-s6000-on-5:~$ show interface status Ethernet4
      Interface        Lanes    Speed    MTU         Alias    Vlan    Oper    Admin            Type    Asym PFC
---------------  -----------  -------  -----  ------------  ------  ------  -------  --------------  ----------
      Ethernet4  29,30,31,32      40G   9100  fortyGigE0/4   trunk      up       up  QSFP+ or later         off
PortChannel0001          N/A      40G   9100           N/A  routed      up       up             N/A         N/A
PortChannel0002          N/A      40G   9100           N/A  routed      up       up             N/A         N/A
PortChannel0003          N/A      40G   9100           N/A  routed      up       up             N/A         N/A
PortChannel0004          N/A      40G   9100           N/A  routed      up       up             N/A         N/A

Я попытался написать регулярное выражение, соответствующее всем полям, как показано ниже.

(\S+)\s+([\d,]+)\s+(\S+)\s+(\d+)\s+(\S+)\s+(\S+)\s+([up|down])+\s+([up|down]+)\s+([\w\s+?]+)\s+(\S+)

Я могу правильно попасть в столбец «Администратор». Тип столбца содержит несколько слов, поэтому я использовал шаблон ([\w\s+?]+), надеясь, что он будет соответствовать нескольким рабочим данным, разделенным одним пробелом, где + является необязательным, за которым следует (\S+), чтобы соответствовать последнему столбцу. Проблема, с которой я сталкиваюсь, заключается в том, что регулярное выражение ([\w\s+?]+) порождает несколько строк и дает мне результат, как показано ниже.

Ethernet4 29,30,31,32 40G 9100 fortyGigE0/4 trunk up up QSFP+ or later off PortChannel0001 N/A

Я вижу, что \s также соответствует новой строке. как ограничить это, чтобы оно не соответствовало новой строке? может кто-нибудь, пожалуйста, помогите мне разъяснить.

Я посмотрел на это пространство Regex для одного или нескольких слов, разделенных пробелами, но это мне тоже не помогает. может кто-нибудь помочь мне понять это лучше?

Возможно вот так ^[^\S\n]*(\S+)[^\S\n]{2,}([\d,]+)[^\S\n]{2,}(\S+)[^\S\n]{2,}‌​(\d+)[^\S\n]{2,}(\S+‌​)[^\S\n]{2,}(\S+)[^\‌​S\n]{2,}(up|down)+[^‌​\S\n]{2,}(up|down)+[‌​^\S\n]{2,}([\w +?]+)[^\S\n]{2,}(\S+) См. regex101.com/r/2NqUGX/1

The fourth bird 29.07.2024 21:41
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
3
1
81
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Вы можете использовать такой шаблон, как:

^\s*(\S+)\s+([\d,]+)\s+(\S+)\s+(\d+)\s+(\S+)\s+(\S+)\s+(up|down)\s+(up|down)\s+(.*?)\s+(\S+)\s*$

  • Используя якоря ^ и $, вы можете ограничить свой шаблон.
  • (.*?) просто извлекает ваш 9-й столбец.

Пример:

import re

s = "      Ethernet4  29,30,31,32      40G   9100  fortyGigE0/4   trunk      up       up  QSFP+ or later         off   "
p = r'^\s*(\S+)\s+([\d,]+)\s+(\S+)\s+(\d+)\s+(\S+)\s+(\S+)\s+(up|down)\s+(up|down)\s+(.*?)\s+(\S+)\s*$'
print(re.findall(p, s))

Принты

[('Ethernet4', '29,30,31,32', '40G', '9100', 'fortyGigE0/4', 'trunk', 'up', 'up', 'QSFP+ or later', 'off')]

При необходимости вы также можете добавить соответствующие флаги: (?im)^\s*(\S+)\s+([\d,]+)\s+(\S+)\s+(\d+)\s+(\S+)\s+(\S+)\s+‌​(up|down)\s+(up|down‌​)\s+(.*?)\s+(\S+)\s*‌​$

user24714692 29.07.2024 21:59

Поскольку \s также может соответствовать символам новой строки, вы можете использовать класс отрицательных символов [^\S\n] для сопоставления пробельных символов без символов новой строки.

Похоже, ваши данные разделены двумя или более пробелами, поэтому вы можете использовать [^\S\n]{2,}

Предпоследняя группа может быть не жадной ([\w +?]+?), но поскольку там также есть пробел, вы также можете сопоставить хотя бы один символ слова (\w[\w +?]*?)

Обратите внимание, что ([up|down]) — это класс символов, соответствующий одному из перечисленных символов, и это не то же самое, что (up|down), который представляет собой группу, соответствующую одной из альтернатив.

Например:

^[^\S\n]*(\S+)[^\S\n]{2,}([\d,]+)[^\S\n]{2,}(\S+)[^\S\n]{2,}(\d+)[^\S\n]{2,}(\S+)[^\S\n]{2,}(\S+)[^\S\n]{2,}(up|down)+[^\S\n]{2,}(up|down)+[^\S\n]{2,}(\w[\w +?]*?)[^\S\n]{2,}(\S+)$

Посмотрите демонстрацию регулярных выражений

Если разделительный пробел также может быть 1 вместо 2, вы также можете написать шаблон, как в этом примере.

хорошее использование [^\S\n] по всему регулярному выражению

anubhava 30.07.2024 08:49

Вы можете построить регулярное выражение программно (10 столбцов = повторить один и тот же шаблон 10 раз):

import re

s = """\
      Interface        Lanes    Speed    MTU         Alias    Vlan    Oper    Admin            Type    Asym PFC
---------------  -----------  -------  -----  ------------  ------  ------  -------  --------------  ----------
      Ethernet4  29,30,31,32      40G   9100  fortyGigE0/4   trunk      up       up  QSFP+ or later         off
PortChannel0001          N/A      40G   9100           N/A  routed      up       up             N/A         N/A
PortChannel0002          N/A      40G   9100           N/A  routed      up       up             N/A         N/A
PortChannel0003          N/A      40G   9100           N/A  routed      up       up             N/A         N/A
PortChannel0004          N/A      40G   9100           N/A  routed      up       up             N/A         N/A
"""

print(*re.findall(r"\s*(\S+(?:\s\S+)*)" * 10 + "$", s, flags=re.M)[2:], sep = "\n")

Распечатки:

('Ethernet4', '29,30,31,32', '40G', '9100', 'fortyGigE0/4', 'trunk', 'up', 'up', 'QSFP+ or later', 'off')
('PortChannel0001', 'N/A', '40G', '9100', 'N/A', 'routed', 'up', 'up', 'N/A', 'N/A')
('PortChannel0002', 'N/A', '40G', '9100', 'N/A', 'routed', 'up', 'up', 'N/A', 'N/A')
('PortChannel0003', 'N/A', '40G', '9100', 'N/A', 'routed', 'up', 'up', 'N/A', 'N/A')
('PortChannel0004', 'N/A', '40G', '9100', 'N/A', 'routed', 'up', 'up', 'N/A', 'N/A')

Привет @Андрей, спасибо, что нашел время ответить... просто пытаюсь немного понять. что делает * перед re.findall? . А не могли бы вы объяснить назначение "$" в конце? это указывает на конец строки?

Vijay 30.07.2024 21:27

@Vijay string * 10 означает: повторите эту строку 10 раз. Да, $ означает конец строки. Вот почему у меня установлен флаг re.M.

Andrej Kesely 30.07.2024 21:32

Нет нет. я пытался понять * перед re.findall

Vijay 30.07.2024 21:37
Ответ принят как подходящий

Предположим, для простоты, данные были следующими.

str = """
admin@str-s6000-on-5:~$ show interface status Ethernet4
      Interface     Lanes   MTU  Alias    Ad         Type    Asym PFC
---------------  --------  ---- ------  ----  -----------  ----------
      Ethernet4  29,30,31  9100  fG0/4    up  Q+ or later         off
PortChannel0001       N/A  9100    N/A    up          N/A         N/A
"""

Я бы посоветовал вам использовать как подробный режим (также известный как свободный интервал) (re.VERBOSE), так и именованные группы захвата, чтобы регулярное выражение самодокументировалось:

import re

rgx = r"""
    ^                        # match beginning of line
    [ ]*                     # match zero or more spaces  
    (?P<Interface>\S+)       # match one or more non-whitespaces and
                             # save to capture group 'Interface'
    [ ]+
    (?P<Lanes>\d+(?:,\d+)*)  # match one or more strings of two or
                             # more digits separated by a comma and
                             # save to capture group 'Lanes'
    [ ]+
    (?P<MTU>\d+)             # match one or more digits and save to
                             # capture group 'MTU'
    [ ]+
    (?P<Alias>\S+)           # match one or more non-whitespaces and
                             # save to capture group 'Alias'
    [ ]+
    (?P<Ad>up|down)          # match 'up' or 'down' and save to
                             # capture group 'Ad'
    [ ]+
    (?P<Type>\S+(?:[ ]\S+)*) # match one or more groups of
                             # non-whitespaces separated by one
                             # space and save to capture group 'Type'
    [ ]*
    (?P<Asym_PFC>off|on)     # match 'up' or 'down' and save to
                             # capture group 'Asym_PFC'
    [ ]*
    $                        # match end of line
    """

Примечание. Я предположил, что пробелы в тексте — это просто пробелы (а не табуляции, например), и в этом случае предпочтительнее использовать пробелы в выражении. Кроме того, я написал, что каждое пространство находится в группе захвата ([ ]); в противном случае оно будет удалено вместе с пробелами, которые не являются частью выражения. Есть и другие способы защитить помещения, один из которых — сбежать из них (\ ).

Затем вы можете извлечь содержимое групп захвата следующим образом.

match = re.search(rgx, str, re.VERBOSE | re.MULTILINE)

if match:
    print("capture group 'Interface': ", match.group('Interface'))
    print("capture group 'Lanes':     ", match.group('Lanes'))
    print("capture group 'MTU':       ", match.group('MTU'))
    print("capture group 'Alias':     ", match.group('Alias'))
    print("capture group 'Ad':        ", match.group('Ad'))
    print("capture group 'Type':      ", match.group('Type'))
    print("capture group 'Asym_PFC':  ", match.group('Asym_PFC'))
else:
    print('did not find')

который отображает

capture group 'Interface':  Ethernet4
capture group 'Lanes':      29,30,31
capture group 'MTU':        9100
capture group 'Alias':      fG0/4
capture group 'Ad':         up
capture group 'Type':       Q+ or later
capture group 'Asym_PFC':   off

Демо

Обратите внимание, что может быть достаточно определить, является ли каждая строка хранителем, и если она просто разбивает строку на регулярное выражение {2,}; то есть разделить на два или более пробелов (после удаления пробелов в начале и/или конце строки). Если, например, линии интереса начинаются 'Ethernet', возможно, дополненные пробелами слева, мы могли бы использовать регулярное выражение r'^ *Ethernet.*' для идентификации линий интереса и r' {2,}' для разделения этих строк и присвоения частей переменным.

Демо

Я новичок в Python, если это видно.

Cary Swoveland 30.07.2024 08:27

Большое спасибо @cary Swowland за прекрасное объяснение и читаемый синтаксис. Я не знал, что мы можем так чисто написать регулярное выражение. Еще раз спасибо.

Vijay 30.07.2024 20:02

Другие вопросы по теме