Содержание

Регулярные выражения в Python: теория и практика

Рассмотрим регулярные выражения в Python, начиная синтаксисом и заканчивая примерами использования.

Примечание Вы читаете улучшенную версию некогда выпущенной нами статьи.

  1. Для чего нужны регулярные выражения?
  2. Регулярные выражения в Python
  3. Задачи

Для чего нужны регулярные выражения?

  • для определения нужного формата, например телефонного номера или email-адреса;
  • для разбивки строк на подстроки;
  • для поиска, замены и извлечения символов;
  • для быстрого выполнения нетривиальных операций.

Синтаксис  таких выражений в основном стандартизирован, так что вам следует понять их лишь раз, чтобы использовать в любом языке программирования.

Примечание Не стоит забывать, что регулярные выражения не всегда оптимальны, и для простых операций часто достаточно встроенных в Python функций.

Хотите узнать больше? Обратите внимание на статью о регулярках для новичков.

Регулярные выражения в Python

В Python для работы с регулярками есть модуль re. Его нужно просто импортировать:

import re

А вот наиболее популярные методы, которые предоставляет модуль:

  • re.match()
  • re.search()
  • re.findall()
  • re.split()
  • re.sub()
  • re.compile()

Рассмотрим каждый из них подробнее.

re.match(pattern, string)

Этот метод ищет по заданному шаблону в начале строки. Например, если мы вызовем метод match() на строке «AV Analytics AV» с шаблоном «AV», то он завершится успешно. Но если мы будем искать «Analytics», то результат будет отрицательный:

import re
result = re.match(r'AV', 'AV Analytics Vidhya AV')
print result

Результат:
<_sre.SRE_Match object at 0x0000000009BE4370>

Искомая подстрока найдена. Чтобы вывести её содержимое, применим метод group() (мы используем «r» перед строкой шаблона, чтобы показать, что это «сырая» строка в Python):

result = re.match(r'AV', 'AV Analytics Vidhya AV')
print result.group(0)

Результат:
AV

Теперь попробуем найти «Analytics» в данной строке. Поскольку строка начинается на «AV», метод вернет None:

result = re.match(r'Analytics', 'AV Analytics Vidhya AV')
print result

Результат:
None

Также есть методы start() и end() для того, чтобы узнать начальную и конечную позицию найденной строки.

result = re.match(r'AV', 'AV Analytics Vidhya AV')
print result.start()
print result.end()

Результат:
0
2

Эти методы иногда очень полезны для работы со строками.

re.search(pattern, string)

Метод похож на match(), но ищет не только в начале строки.

В отличие от предыдущего, search() вернёт объект, если мы попытаемся найти «Analytics»:

result = re.search(r'Analytics', 'AV Analytics Vidhya AV')
print result.group(0)

Результат:
Analytics

Метод search() ищет по всей строке, но возвращает только первое найденное совпадение.

re.findall(pattern, string)

Возвращает список всех найденных совпадений. У метода findall() нет ограничений на поиск в начале или конце строки. Если мы будем искать «AV» в нашей строке, он вернет все вхождения «AV». Для поиска рекомендуется использовать именно

findall(), так как он может работать и как re.search(), и как re.match().

result = re.findall(r'AV', 'AV Analytics Vidhya AV')
print result

Результат:
['AV', 'AV']

re.split(pattern, string, [maxsplit=0])

Этот метод разделяет строку по заданному шаблону.

result = re. split(r'y', 'Analytics')
print result

Результат:
['Anal', 'tics']

В примере мы разделили слово «Analytics» по букве «y». Метод split() принимает также аргумент maxsplit со значением по умолчанию, равным 0. В данном случае он разделит строку столько раз, сколько возможно, но если указать этот аргумент, то разделение будет произведено не более указанного количества раз. Давайте посмотрим на примеры:

result = re.split(r'i', 'Analytics Vidhya')
print result

Результат:
['Analyt', 'cs V', 'dhya'] # все возможные участки.
result = re.split(r'i', 'Analytics Vidhya',maxsplit=1)
print result

Результат:
['Analyt', 'cs Vidhya']

Мы установили параметр maxsplit равным 1, и в результате строка была разделена на две части вместо трех.

re.sub(pattern, repl, string)

Ищет шаблон в строке и заменяет его на указанную подстроку.

Если шаблон не найден, строка остается неизменной.

result = re.sub(r'India', 'the World', 'AV is largest Analytics community of India')
print result

Результат:
'AV is largest Analytics community of the World'

re.compile(pattern, repl, string)

Мы можем собрать регулярное выражение в отдельный объект, который может быть использован для поиска. Это также избавляет от переписывания одного и того же выражения.

pattern = re.compile('AV')
result = pattern.findall('AV Analytics Vidhya AV')
print result
result2 = pattern.findall('AV is largest analytics community of India')
print result2

Результат:
['AV', 'AV']
['AV']

До сих пор мы рассматривали поиск определенной последовательности символов. Но что, если у нас нет определенного шаблона, и нам надо вернуть набор символов из строки, отвечающий определенным правилам? Такая задача часто стоит при извлечении информации из строк. и $ Начало и конец строки соответственно {n,m} От n до m вхождений ({,m} — от 0 до m) a|b Соответствует a или b ()

Группирует выражение и возвращает найденный текст \t, \n, \r Символ табуляции, новой строки и возврата каретки соответственно

Больше информации по специальным символам можно найти в документации для регулярных выражений в Python 3.

Ну хватит теории. Рассмотрим примеры использования Python RegEx.

Задачи

Вернуть первое слово из строки

Сначала попробуем вытащить каждый символ (используя .)

result = re.findall(r'.', 'AV is largest Analytics community of India')
print result

Результат:
['A', 'V', ' ', 'i', 's', ' ', 'l', 'a', 'r', 'g', 'e', 's', 't', ' ', 'A', 'n', 'a', 'l', 'y', 't', 'i', 'c', 's', ' ', 'c', 'o', 'm', 'm', 'u', 'n', 'i', 't', 'y', ' ', 'o', 'f', ' ', 'I', 'n', 'd', 'i', 'a']

Для того, чтобы в конечный результат не попал пробел, используем вместо . \w.

result = re.findall(r'\w', 'AV is largest Analytics community of India')
print result

Результат:
['A', 'V', 'i', 's', 'l', 'a', 'r', 'g', 'e', 's', 't', 'A', 'n', 'a', 'l', 'y', 't', 'i', 'c', 's', 'c', 'o', 'm', 'm', 'u', 'n', 'i', 't', 'y', 'o', 'f', 'I', 'n', 'd', 'i', 'a']

Теперь попробуем достать каждое слово (используя * или +)

result = re.findall(r'\w*', 'AV is largest Analytics community of India')
print result

Результат:
['AV', '', 'is', '', 'largest', '', 'Analytics', '', 'community', '', 'of', '', 'India', '']

И снова в результат попали пробелы, так как * означает «ноль или более символов». Для того, чтобы их убрать, используем +:

result = re.findall(r'\w+', 'AV is largest Analytics community of India')
print result
Результат:
['AV', 'is', 'largest', 'Analytics', 'community', 'of', 'India']

Теперь вытащим первое слово, используя ^:

result = re. , то мы получим последнее слово, а не первое:

result = re.findall(r'\w+$', 'AV is largest Analytics community of India')
print result

Результат:
[‘India’]

Вернуть первые два символа каждого слова

Вариант 1: используя \w, вытащить два последовательных символа, кроме пробельных, из каждого слова:

result = re.findall(r'\w\w', 'AV is largest Analytics community of India')
print result

Результат:
['AV', 'is', 'la', 'rg', 'es', 'An', 'al', 'yt', 'ic', 'co', 'mm', 'un', 'it', 'of', 'In', 'di']

Вариант 2: вытащить два последовательных символа, используя символ границы слова (\b):

result = re.findall(r'\b\w.', 'AV is largest Analytics community of India')
print result

Результат:
['AV', 'is', 'la', 'An', 'co', 'of', 'In']

Вернуть домены из списка email-адресов

Сначала вернём все символы после «@»:

result = re. findall(r'@\w+', '[email protected], [email protected], [email protected], [email protected]')
print result

Результат:
['@gmail', '@test', '@analyticsvidhya', '@rest']

Как видим, части «.com», «.in» и т. д. не попали в результат. Изменим наш код:

result = re.findall(r'@\w+.\w+', '[email protected], [email protected], [email protected], [email protected]')
print result

Результат:
['@gmail.com', '@test.in', '@analyticsvidhya.com', '@rest.biz']

Второй вариант — вытащить только домен верхнего уровня, используя группировку — ( ):

result = re.findall(r'@\w+.(\w+)', '[email protected], [email protected], [email protected], [email protected]')
print result

Результат:
['com', 'in', 'com', 'biz']

Извлечь дату из строки

Используем \d для извлечения цифр.

result = re.findall(r'\d{2}-\d{2}-\d{4}', 'Amit 34-3456 12-05-2007, XYZ 56-4532 11-11-2011, ABC 67-8945 12-01-2009')
print result

Результат:
['12-05-2007', '11-11-2011', '12-01-2009']

Для извлечения только года нам опять помогут скобки:

result = re. findall(r'\d{2}-\d{2}-(\d{4})', 'Amit 34-3456 12-05-2007, XYZ 56-4532 11-11-2011, ABC 67-8945 12-01-2009')
print result

Результат:
['2007', '2011', '2009']

Извлечь слова, начинающиеся на гласную

Для начала вернем все слова:

result = re.findall(r'\w+', 'AV is largest Analytics community of India')
print result

Результат:
['AV', 'is', 'largest', 'Analytics', 'community', 'of', 'India']

А теперь — только те, которые начинаются на определенные буквы (используя []):

result = re.findall(r'[aeiouAEIOU]\w+', 'AV is largest Analytics community of India')
print result

Результат:
['AV', 'is', 'argest', 'Analytics', 'ommunity', 'of', 'India']

Выше мы видим обрезанные слова «argest» и «ommunity». Для того, чтобы убрать их, используем \b для обозначения границы слова:

result = re.findall(r'\b[aeiouAEIOU]\w+', 'AV is largest Analytics community of India')
print result

Результат:
['AV', 'is', 'Analytics', 'of', 'India']

Также мы можем использовать ^ внутри квадратных скобок для инвертирования группы:

result = re. aeiouAEIOU ]\w+', 'AV is largest Analytics community of India')
print result

Результат:
['largest', 'community']

Проверить формат телефонного номера

Номер должен быть длиной 10 знаков и начинаться с 8 или 9. Есть список телефонных номеров, и нужно проверить их, используя регулярки в Python:

li = ['9999999999', '999999-999', '99999x9999']

for val in li:
    if re.match(r'[8-9]{1}[0-9]{9}', val) and len(val) == 10:
        print 'yes'
    else:
        print 'no'

Результат:
yes
no
no

Разбить строку по нескольким разделителям

Возможное решение:

line = 'asdf fjdk;afed,fjek,asdf,foo' # String has multiple delimiters (";",","," ").
result = re.split(r'[;,\s]', line)
print result

Результат:
['asdf', 'fjdk', 'afed', 'fjek', 'asdf', 'foo']

Также мы можем использовать метод re.sub() для замены всех разделителей пробелами:

line = 'asdf fjdk;afed,fjek,asdf,foo'
result = re. sub(r'[;,\s]',' ', line)
print result

Результат:
asdf fjdk afed fjek asdf foo

Извлечь информацию из html-файла

Допустим, нужно извлечь информацию из html-файла, заключенную между <td> и </td>, кроме первого столбца с номером. Также будем считать, что html-код содержится в строке.

Пример содержимого html-файла:

1NoahEmma2LiamOlivia3MasonSophia4JacobIsabella5WilliamAva6EthanMia7MichaelEmily

С помощью регулярных выражений в Python это можно решить так (если поместить содержимое файла в переменную test_str):

result = re.findall(r'\d([A-Z][A-Za-z]+)([A-Z][A-Za-z]+)', test_str)
print result

Результат:
[('Noah', 'Emma'), ('Liam', 'Olivia'), ('Mason', 'Sophia'), ('Jacob', 'Isabella'), ('William', 'Ava'), ('Ethan', 'Mia'), ('Michael', 'Emily')]

Адаптированный перевод «Beginners Tutorial for Regular Expressions in Python»

Функция re.

sub — Документация Python для сетевых инженеров 3.0

Функция re.sub работает аналогично методу replace в строках. Но в функции re.sub можно использовать регулярные выражения, а значит, делать замены по более сложным условиям.

Заменим запятые, квадратные скобки и слово via на пробел в строке ospf_route:

In [7]: ospf_route = 'O    10.0.24.0/24 [110/41] via 10.0.13.3, 3d18h, FastEthernet0/0'

In [8]: re.sub(r'(via|[,\[\]])', ' ', ospf_route)
Out[8]: 'O        10.0.24.0/24  110/41    10.0.13.3  3d18h  FastEthernet0/0'

С помощью re.sub можно трансформировать строку. Например, преобразовать строку mac_table таким образом:

In [9]: mac_table = '''
   ...:  100    aabb.cc10.7000    DYNAMIC     Gi0/1
   ...:  200    aabb.cc20.7000    DYNAMIC     Gi0/2
   ...:  300    aabb.cc30.7000    DYNAMIC     Gi0/3
   ...:  100    aabb.cc40.7000    DYNAMIC     Gi0/4
   ...:  500    aabb.cc50.7000    DYNAMIC     Gi0/5
   ...:  200    aabb.cc60.7000    DYNAMIC     Gi0/6
   . ..:  300    aabb.cc70.7000    DYNAMIC     Gi0/7
   ...: '''

In [4]: print(re.sub(r' *(\d+) +'
   ...:              r'([a-f0-9]+)\.'
   ...:              r'([a-f0-9]+)\.'
   ...:              r'([a-f0-9]+) +\w+ +'
   ...:              r'(\S+)',
   ...:              r'\1 \2:\3:\4 \5',
   ...:              mac_table))
   ...:

100 aabb:cc10:7000 Gi0/1
200 aabb:cc20:7000 Gi0/2
300 aabb:cc30:7000 Gi0/3
100 aabb:cc40:7000 Gi0/4
500 aabb:cc50:7000 Gi0/5
200 aabb:cc60:7000 Gi0/6
300 aabb:cc70:7000 Gi0/7

Регулярное выражение разделено на группы:

  • (\d+) - первая группа. Сюда попадет номер VLAN
  • ([a-f,0-9]+).([a-f,0-9]+).([a-f,0-9]+) - три следующие группы (2, 3, 4) описывают MAC-адрес
  • (\S+) - пятая группа. Описывает интерфейс.

Во втором регулярном выражении эти группы используются. Для того, чтобы сослаться на группу, используется обратный слеш и номер группы. Чтобы не пришлось экранировать обратный слеш, используется raw строка.

В итоге вместо номеров групп будут подставлены соответствующие подстроки. Для примера, также изменен формат записи MAC-адреса.

Python re.sub заменить соответствующим содержимым



Пытаясь разобраться с регулярными выражениями в Python, я пытаюсь вывести некоторые HTML, выделенные в части URL. Мой вклад-это

images/:id/size

мой вывод должен быть

images/<span>:id</span>/size

Если я сделаю это в Javascript году

method = 'images/:id/size';
method = method.replace(/\:([a-z]+)/, '<span>$1</span>')
alert(method)

Я получаю желаемый результат, но если я сделаю это в Python

>>> method = 'images/:id/huge'
>>> re.sub('\:([a-z]+)', '<span>$1</span>', method)
'images/<span>$1</span>/huge'

Я не знаю, как заставить Python вернуть правильный результат, а не $1 ? Является ли re. sub даже правильной функцией для этого?

python regex
Поделиться Источник Smudge     25 августа 2011 в 13:28

4 ответа


  • re.sub в python 2.7

    Попытка заменить куски текста в файлах html на 'xxx' с помощью re.sub, python 2.7. Я могу заставить его работать только с базовыми строками, у которых нет пробелов или новых строк. Этот код не находит ничего, чтобы заменить. Я пробовал DOTALL и другие вещи, но ничего не работает. Он просто...

  • Re.sub python-замена нескольких элементов одновременно

    Я должен заменить диапазон акцентированных символов в строке с соответствующими не акцентированными буквами. Я думал использовать re.sub, но я понятия не имею, как заменить больше элементов (каждый с другим элементом) одновременно. Чтобы было понятнее: import re...



92

Просто используйте \1 вместо $1 :

In [1]: import re

In [2]: method = 'images/:id/huge'

In [3]: re. sub(r'(:[a-z]+)', r'<span>\1</span>', method)
Out[3]: 'images/<span>:id</span>/huge'

Также обратите внимание на использование необработанных строк (r'...') для регулярных выражений. Это не обязательно, но устраняет необходимость избегать обратных косых черт, что, возможно, делает код немного более читаемым.

Поделиться NPE     25 августа 2011 в 13:32


Поделиться Unknown     25 августа 2011 в 13:31



6

Обратное отношение ко всему значению соответствия равно \g<0>, см. документацию re.sub :

Backreference \g<0> заменяет всю подстроку, соответствующую RE.

Смотрите демонстрацию Python :

import re
method = 'images/:id/huge'
print(re. sub(r':[a-z]+', r'<span>\g<0></span>', method))
# => images/<span>:id</span>/huge

Поделиться Wiktor Stribiżew     17 января 2019 в 11:47


  • python re.sub, только замените часть спички

    Я очень новичок в python Мне нужно сопоставить все случаи по одному выражению regex и сделать замену. это пример подстроки -- > желаемый результат: <cross_sell id=123 sell_type=456> --> <cross_sell> я пытаюсь сделать это в своем коде: myString =...

  • Замена строк с использованием re.sub в python

    Во время обучения Python regex, я хочу, чтобы заменить все Python 2х как функции печати для Python 3х, как с помощью re.sub : import re with open(py2.py, r) as f: matter = f.read() mtr = re.sub(rprint\s+\(.+)\+,rprint(, matter) with open(pr.py, w) as f: final = f.write(mtr) Вопрос py2.py является:...



5

Для замещающей части Python использует \1 так, как это делают sed и vi, а не $1 так, как это делают Perl, Java и Javascript (среди прочих). Кроме того, поскольку \1 интерполируется в обычных строках как символ U+0001, необходимо использовать необработанную строку или \escape it.

Python 3.2 (r32:88445, Jul 27 2011, 13:41:33) 
[GCC 4.0.1 (Apple Inc. build 5465)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> method = 'images/:id/huge'
>>> import re
>>> re.sub(':([a-z]+)', r'<span>\1</span>', method)
'images/<span>id</span>/huge'
>>> 

Поделиться tchrist     25 августа 2011 в 13:35


Похожие вопросы:


python регулярное выражение re.sub ошибка

У меня есть.csv файлы, которые могли быть смешаны в скобках: line = fdf,dfdf,(1,2,3,4,5),(ss,dd), Теперь я хочу заменить все () на, так что это выглядит так: line = 'fdf,dfdf,1,2,3,4,5,ss,dd,' Мой...


Python re. sub заменить html атрибуты

Я пытаюсь изменить размер изображений из кода html. Это один из примеров : Моя цель-заменить height=108 и width=150 с высотой и шириной 400. Я пробовал следующие строки, хотя они, похоже, не...


python re.sub: заменить подстроку строкой

В python, с re.sub , как я могу заменить подстроку новой строкой ? от number = 20 s = hello number 10, Agosto 19 к s = hello number 20, Agosto 19 Я стараюсь re.sub(r'number ([0-9]*)', number, s)...


re.sub в python 2.7

Попытка заменить куски текста в файлах html на 'xxx' с помощью re.sub, python 2.7. Я могу заставить его работать только с базовыми строками, у которых нет пробелов или новых строк. Этот код не...


Re.sub python-замена нескольких элементов одновременно

Я должен заменить диапазон акцентированных символов в строке с соответствующими не акцентированными буквами. Я думал использовать re.sub, но я понятия не имею, как заменить больше элементов (каждый.

..


python re.sub, только замените часть спички

Я очень новичок в python Мне нужно сопоставить все случаи по одному выражению regex и сделать замену. это пример подстроки -- > желаемый результат: <cross_sell id=123 sell_type=456> -->...


Замена строк с использованием re.sub в python

Во время обучения Python regex, я хочу, чтобы заменить все Python 2х как функции печати для Python 3х, как с помощью re.sub : import re with open(py2.py, r) as f: matter = f.read() mtr =...


re.sub заменить подчеркивание внутри второй группы

Я пытаюсь re.sub в python, где я хочу захватить две группы и заменить новым предложением. Первая группа-это cat, а вторая группа-amazing hat. cat - это первые слова / число перед (точка), а...


Python 2 Против 3 re.sub() regex заменить разницу

Я пытаюсь regex заменить n вхождений буквы одной буквой. Должно быть достаточно просто, так как я знаю регулярные выражения . Python 2.7.15 работает как и ожидалось: >>> re.sub('x*', 'y',...


Python re.sub группа чисел после слова

Как я могу заменить 'некоторое число n(например, 11 цифр любого числа) или некоторое число словом(например, любой символ, такой как as12345678iu) на 'resi'? Я попробовал с re.sub, но ничего не...

Python re.sub() не заменяет каждый матч



Я использую Python 3 и у меня есть две строки: abbcabb и abca . Я хочу удалить каждое двойное вхождение одного символа . Например:

abbcabb должен дать c и abca должен дать bc .

Я попробовал следующее regex ( здесь ):

(.)(.*?)\1

Но, это дает неправильный вывод для первой строки. Кроме того, когда я попробовал еще один ( здесь ):

(.)(.*?)*?\1

Но, этот снова дает неправильный вывод. Что здесь происходит не так?


Код python - это оператор печати:

print(re.sub(r'(.)(.*?)\1', '\g<2>', s)) # s is the string
python regex string replace capturing-group
Поделиться Источник Rahul Verma     15 декабря 2018 в 08:15

5 ответов


  • Python re.sub возврат двоичных символов

    Я пытаюсь восстановить канал JSON, используя выражения re.sub() regex в Python. (Я также работаю с поставщиком корма, чтобы исправить это). У меня есть два выражения, чтобы исправить: 1. milepost: milepost: 723.46 в которых отсутствует конечная цитата, и 2. }, } который не должен иметь запятую....

  • Python re.sub не ответный матч

    В моем мозгу, следующее: >>> re.sub('([eo])', '_\1_', 'aeiou') должны вернуться: 'a_e_i_o_u' вместо этого он возвращается: 'a_\x01_i_\x01_u' Я уверен, что у меня спазм мозга, но я не могу понять, что случилось.



3

Он может быть решен без регулярного выражения, как показано ниже

>>>''.join([i for i in s1 if s1.count(i) == 1])
'bc'
>>>''.join([i for i in s if s.count(i) == 1])
'c'

Поделиться JON     15 декабря 2018 в 08:36



2

re.sub() не выполняет перекрывающиеся замены. После того, как он заменяет первый матч, он начинает смотреть после окончания матча. Поэтому, когда вы выполняете замену на

abbcabb

сначала он заменяет abbca на bbc . Затем он заменяет bb пустой строкой. Он не возвращается и не ищет другой матч в bbc .

Если вы хотите этого,вам нужно написать свой собственный цикл.

while True:
    newS = re. sub(r'(.)(.*?)\1', r'\g<2>', s)
    if newS == s:
        break
    s = newS
print(newS)

DEMO

Поделиться Barmar     15 декабря 2018 в 08:23



2

Регулярные выражения не кажутся идеальным решением

  • они не обрабатывают перекрытие, поэтому ему нужен цикл (как в этом ответе ), и он создает строки снова и снова (производительность страдает)
  • они здесь излишни, нам просто нужно посчитать персонажей

Мне нравится этот ответ, но использование count неоднократно в списке понимания циклов по всем элементам каждый раз.

Он может быть решен без регулярного выражения и без сложности O(n**2) , только O(n) с использованием collections.Counter

  • сначала подсчитайте символы строки очень легко & быстро
  • затем отфильтруйте строку тестирования, если счетчик совпадает с помощью счетчика, который мы только что создали.

подобный этому:

import collections

s = "abbcabb"

cnt = collections.Counter(s)

s = "".join([c for c in s if cnt[c]==1])

(в качестве бонуса вы можете изменить счетчик, чтобы сохранить символы, которые имеют 2, 3, независимо от того, что происходит)

Поделиться Jean-François Fabre     15 декабря 2018 в 08:42


  • python re.sub, только замените часть спички

    Я очень новичок в python Мне нужно сопоставить все случаи по одному выражению regex и сделать замену. это пример подстроки -- > желаемый результат: <cross_sell id=123 sell_type=456> --> <cross_sell> я пытаюсь сделать это в своем коде: myString =...

  • Получить групповой матч в re.sub в Python

    В Perl, это возможно для меня, чтобы сделать замену и захватить групповой матч в то же время. напр. my $string = abcdef123; $string =~ s/(\d+)//; my $groupMatched = $1; # $groupMatched is 123 В Python, я могу сделать замену с помощью функции re. sub следующим образом. Однако я не могу найти способ...



1

EDIT: на основе обмена комментариями-Если вы просто обеспокоены четностью количества букв, то вам не нужен regex , а вместо этого нужен подход, подобный рекомендации @jon's. (Если вы не заботитесь о порядке, то более эффективный подход с очень длинными строками может использовать что-то вроде collections.Counter вместо этого.)


Мое лучшее предположение о том, что вы пытаетесь сопоставить: "один или несколько символов - назовите этот подшаблон A - затем другой набор одного или нескольких символов - назовите этот подшаблон B - затем снова подшаблон A".

Вы можете использовать + в качестве ярлыка для "one or more" (вместо того, чтобы указывать его один раз, а затем использовать * для rest совпадений), но в любом случае вам нужно правильно получить подшаблоны. Давайте попробуем:

>>> import re
>>> pattern = re. compile(r'(.+?)(.+?)\1')
>>> pattern.sub('\g<2>', 'abbcabbabca')
'bbcbaca'

Хм. Это не сработало. Почему? Поскольку первый шаблон не является жадным, наш "subpattern A" может просто соответствовать первому a в строке - он появляется позже, в конце концов. Поэтому, если мы используем жадное совпадение, Python будет отступать до тех пор, пока не найдет такой длинный шаблон для подшаблона A, который все еще позволяет появиться шаблону A-B-A:

>>> pattern = re.compile(r'(.+)(.+?)\1')
>>> pattern.sub('\g<2>', 'abbcabbabca')
'cbc'

Выглядит неплохо для меня.

Поделиться Karl Knechtel     15 декабря 2018 в 08:36



0

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

(.)(.*?)\1 не удаляет и не сопоставляет каждое двойное событие. Он соответствует 1 символу, за которым следует что-либо в середине, зажатое до тех пор, пока этот же символ не встретится снова.

Итак, для abbcabb часть "sandwiched" должна быть bbc между двумя a

EDIT: Вы можете попробовать что-то вроде этого вместо этого без регулярных выражений:

string = "abbcabb"
result = []
for i in string:
    if i not in result:
        result.append(i)
    else:
        result.remove(i)
print(''.join(result))

Обратите внимание,что это создает "last" нечетное вхождение строки, а не первое.

Для "first" известных случаев вы должны использовать счетчик, как указано в этом ответе . Просто измените условие, чтобы проверить нечетные числа. pseudo code(count[letter] %2 == 1)

Поделиться Paritosh Singh     15 декабря 2018 в 08:23


Похожие вопросы:


Почему re. /]+/$)', \1, str) я хочу получить tweqt/


Python re.sub заменяет все вместо одной группы

Итак, у меня есть строка, и мне нужно заменить каждый ; , окруженный на , (в Примере ; находится в CANDIES,CHOC,DK,NFS (45-59% CACAO SOL 90%**!!!;!!!** 60-69% CACAO SOL ) Код import re a =...


Python re.sub возврат двоичных символов

Я пытаюсь восстановить канал JSON, используя выражения re.sub() regex в Python. (Я также работаю с поставщиком корма, чтобы исправить это). У меня есть два выражения, чтобы исправить: 1. milepost: ...


Python re.sub не ответный матч

В моем мозгу, следующее: >>> re.sub('([eo])', '_\1_', 'aeiou') должны вернуться: 'a_e_i_o_u' вместо этого он возвращается: 'a_\x01_i_\x01_u' Я уверен, что у меня спазм мозга, но я не могу...


python re.sub, только замените часть спички

Я очень новичок в python Мне нужно сопоставить все случаи по одному выражению regex и сделать замену. это пример подстроки -- > желаемый результат: <cross_sell id=123 sell_type=456> -->...


Получить групповой матч в re.sub в Python

В Perl, это возможно для меня, чтобы сделать замену и захватить групповой матч в то же время. напр. my $string = abcdef123; $string =~ s/(\d+)//; my $groupMatched = $1; # $groupMatched is 123 В...


Python re.sub специфический синтаксис

Я пишу скрипт в python, который заменяет определенные строки в файлах Linux. Скажем, у меня есть файл с именем hi в каталоге /home, который содержит: hi 873840 Вот мой сценарий: #! /usr/bin/env...


Python re.sub заменить матч на это md5sum

Я пытаюсь разобрать такую строку: <@Something> there is some regular text <@something_else> and even more <@foo> <@bar> text И замените все <@tokens> их суммами md5....


Python re.sub() заменяет полное совпадение даже при использовании групп без захвата

Я считаю, что re. sub() заменяет полное совпадение, но в этом случае я хочу только заменить соответствующие группы и игнорировать группы без захвата. Как я могу пойти на это? string = 'aBCDeFGH'...

16 — Замены с помощью регулярных выражений

Содержание

  • Замены - функция re.sub(pattern, repl, string)
  • Именованные группы

Замены в регурярных выражениях

В стандартных строках Python есть функция .replace(old, new), которую можно успешно использовать для замены одной строки на другую:

>>> string = 'Алёна мыла ёлку!'
>>> print(string.replace('ё', 'е'))
Алена мыла елку!

Но, что, делать в более сложных случаях? Ну не писать же несколько раз вызов функции .replace() с разными аргументами

>>> string = 'Ёлку мыла Алёна...'
>>> print(string.replace('ё', 'е').replace('Ё', 'Е'))
Елку мыла Алена...

На помощь приходят регулярные выражения и модуль re со своей функцией re. sub().

Сигнатура методы такая: re.sub(pattern, repl, string), где

  • pattern - это регулярное выражение - шаблон для поиска строки, которую нужно заменить
  • repl - строка, на которую нужно произвести замену
  • string - сама строка, над которой нужно произвести манипуляции

Метод re.sub() ищет шаблон pattern в строке string и заменяет его на repl. Метод возвращает новую строку. Если шаблон не найден в строке, то текст возвращается без изменений.

>>> # Задача: заменить все числа на слово NUM
>>> #
>>> import re
>>> string = 'Мой дядя родился в 48 году и в 2000 ему было 52'
>>> pattern = '[0-9]+'
>>> print(re.sub(pattern, 'NUM', string))
Мой дядя родился в NUM году и в NUM ему было NUM

Пример с Алёной и заглавной и строчной буквой ё нельзя запрограммировать одной регуляркой. Подумайте, как можно сделать такую функцию, используя регулярки.

Использование групп при заменах

Представьте, что вам нужно написать функцию, которая будет менять американский формат записи даты MM/DD/YYYY на русский DD.MM.YYYY. Сейчас не будем говорить, про то, что дни могут быть только в диапазоне от 1 до 31, а месяцы от 1 до 12.

Функция может иметь слудующий вид:

def convert_dates(text):

    pattern = '([0-9]{2})/([0-9]{2})/([0-9]{4})'
    result = re.search(pattern, text)

    if result:
        mm = result.group(1)
        dd = result.group(2)
        yyyy = result.group(3)
        new_date = dd + '/' + mm + '/' + yyyy
        start, stop = result.span()
        text = text[:start] + new_date + text[stop:]

    return text

И работать так:

>>> convert_dates('Я влюбился в тебя 03/21/2017.')
'Я влюбился в тебя 21/03/2017.'

Но, что если, дат в тексте будет больше, чем одна. Да и вообще, неужели нужно писать столь сложный код для такой логики?

На помощь приходят группы. Функцию выше можно переписать так:

def convert_dates(text):

    pattern = '([0-9]{2})/([0-9]{2})/([0-9]{4})'
    repl = r'\2/\1/\3'

    return re.sub(pattern, repl, text)

А использовать так же:

>>> convert_dates('Я влюбился в тебя 03/21/2017. Мои родители познакомились 03/21/1999')
'Я влюбился в тебя 21/03/2017. Мои родители познакомились 21/03/1999'

Здесь repl - это еще один шаблон, который говорит функции re.sub() куда вставить ту или иную группы из шаблона pattern. Выглядит конечно страшно, но куда деваться.

Как, наверное, можно догадаться, \N - это указание на конкретную группу из шаблона pattern, которую нужно подставить. N - это номер группы.

Именованные группы

Когда количество групп в шаблонах увеличивается, то становится трудно с ними работать. Можно легко запутаться в индексах и допустить ошибку. Здесь на помощь приходят именованные группы, с помощью которых можно дать каждой группе своё имя. Осталось только привыкнуть к синтаксису.

Что, если, в наш пример с датой добавится еще и время…

def convert_dates(text):

    pattern = '(?P<m>[0-9]{2})/(?P<d>[0-9]{2})/(?P<y>[0-9]{4}) (?P<hm>[0-9]{2}:[0-9]{2})'
    repl = r'\g<d>/\g<m>/\g<y> в \g<hm>'

    return re.sub(pattern, repl, text)
>>> convert_dates('Я влюбился в тебя 03/21/2017 23:45 по московскому времени')
'Я влюбился в тебя 21/03/2017 в 23:45 по московскому времени'

(?P<m>[0-9]{2}) - ?P<m> - специальный синтаксис задания имени группы. Имя здесь только то, что заключено в скобки.

Обращение к группе происходит тоже с помощью спецального синтаксиса: \g<d>

Имена групп можно использовать в методе . group()

def get_mail_provider(email):

    pattern = '(?P<login>[a-zA-Z0-9_]+)@(?P<provider>(?P<name>[a-zA-Z0-9_]+)\.(?P<domain>[a-zA-Z]+))'
    result = re.search(pattern, email)

    if result:
        return result.group('provider')

    return None
>>> get_mail_provider('[email protected]')
'yandex.ru'

TBD

python - Python re.sub () не заменяет каждое совпадение

Я использую Python 3, и у меня есть две строки: abbcabb и abca. Я хочу удалить каждое двойное вхождение одного символа . Например:

abbcabb должен дать c, а abca должен дать bc.

Я попробовал следующее регулярное выражение (здесь):

(.)(.*?)\1

Но это дает неправильный вывод для первой строки. Кроме того, когда я попробовал другой (здесь):

(. )(.*?)*?\1

Но этот снова дает неправильный вывод. Что здесь не так?


Код Python является оператором print:

print(re.sub(r'(.)(.*?)\1', '\g<2>', s)) # s is the string

2

Rahul Verma 15 Дек 2018 в 11:15

5 ответов

Лучший ответ

Сайт объясняет это хорошо, наведите и используйте раздел объяснений.

(.)(.*?)\1 Не удаляет и не сопоставляет каждое двойное вхождение. Он соответствует 1 символу, за которым следует что-либо посередине, пока этот же символ не встретится снова.

Поэтому для abbcabb «зажатая» часть должна быть bbc между двумя a

РЕДАКТИРОВАТЬ: Вы можете попробовать что-то вроде этого без регулярных выражений:

string = "abbcabb"
result = []
for i in string:
    if i not in result:
        result. append(i)
    else:
        result.remove(i)
print(''.join(result))

Обратите внимание, что это создает «последнее» нечетное вхождение строки, а не первое.

Для «первого» известного случая вы должны использовать счетчик, как указано в этом ответе. Просто измените условие, чтобы проверить наличие нечетных чисел. pseudo code(count[letter] %2 == 1)

0

Paritosh Singh 15 Дек 2018 в 08:52

re.sub() не выполняет перекрывающихся замен. После того, как он заменяет первый матч, он начинает присматривать после окончания матча. Поэтому, когда вы выполняете замену на

abbcabb

Сначала он заменяет abbca на bbc. Затем он заменяет bb пустой строкой. Он не возвращается и не ищет другого совпадения в bbc.

Если вы хотите этого, вам нужно написать свой собственный цикл.

while True:
    newS = re.sub(r'(.)(.*?)\1', r'\g<2>', s)
    if newS == s:
        break
    s = newS
print(newS)

DEMO

2

Barmar 15 Дек 2018 в 08:35

Регулярные выражения не кажутся идеальным решением

  • они не обрабатывают перекрытие, поэтому ему необходим цикл (как в этом ответе), и он создает строки снова и снова ( производительность страдает)
  • они здесь излишни, нам просто нужно посчитать персонажей

Мне нравится этот ответ, но использование count многократно в цикле понимания списка каждый раз повторяет все элементы.

Его можно решить без регулярного выражения и без O(n**2) сложности, только O(n) с помощью collections. Counter

  • сначала подсчитать символы строки очень легко и быстро
  • затем отфильтруйте проверку строк, если количество совпадений будет соответствовать счетчику, который мы только что создали.

Типа того:

import collections

s = "abbcabb"

cnt = collections.Counter(s)

s = "".join([c for c in s if cnt[c]==1])

(в качестве бонуса вы можете изменить счетчик, чтобы сохранить персонажей, которые имеют 2, 3, в любом случае)

2

Jean-François Fabre 15 Дек 2018 в 09:51

Это может быть решено без регулярного выражения, как показано ниже

>>>''.join([i for i in s1 if s1.count(i) == 1])
'bc'
>>>''.join([i for i in s if s.count(i) == 1])
'c'

3

JON 15 Дек 2018 в 08:36

РЕДАКТИРОВАТЬ: на основе обмена комментариями - если вас интересует только четность подсчета букв, то вы не хотите регулярное выражение и вместо этого хотите подход, подобный рекомендации @ jon. (Если вы не заботитесь о порядке, то более производительный подход с очень длинными строками может использовать что-то вроде collections.Counter.)


Мое лучшее предположение относительно того, что вы пытаетесь сопоставить: «один или несколько символов - вызовите этот подшаблон A - за которым следует другой набор из одного или нескольких символов - вызовите этот подшаблон B - с последующим повторением подшаблона A».

Вы можете использовать + в качестве ярлыка для «одного или нескольких» (вместо того, чтобы указывать его один раз, а затем использовать * для остальных совпадений), но в любом случае вам нужно получить правильные подшаблоны. Давай попробуем:

>>> import re
>>> pattern = re.compile(r'(.+?)(.+?)\1')
>>> pattern.sub('\g<2>', 'abbcabbabca')
'bbcbaca'

Хм . Это не сработало. Почему? Поскольку первый шаблон не является жадным, наш «подшаблон A» может просто соответствовать первому a в строке - в конце концов, он действительно появляется позже. Поэтому, если мы используем жадное совпадение, Python будет возвращаться назад до тех пор, пока не найдет такой длинный шаблон для подшаблона A, который по-прежнему допускает появление шаблона A-B-A:

>>> pattern = re.compile(r'(.+)(.+?)\1')
>>> pattern.sub('\g<2>', 'abbcabbabca')
'cbc'

Выглядит хорошо для меня.

1

Karl Knechtel 15 Дек 2018 в 08:36

53790699

Регулярные выражения — Погружение в Python 3

❝ Некоторые люди, во время решения одной проблемы думают: «Я знаю, я буду использовать регулярные выражения». Теперь у них две проблемы… ❞— Jamie Zawinski

Погружение

Каждый новый язык программирования имеет встроенные функции для работы со строками. В Python, строки имеют методы для поиска и замены: index(), find(), split(), count(), replace() и т. д. Но эти методы ограничены для простейших случаев. Например метод index() ищет простую жёстко заданную часть строки и поиск всегда регистрозависимый. Чтобы выполнить регистронезависимый поиск по строке s, вы должны вызвать s.lower() или s.upper() для того чтобы быть уверенным что строка имеет соответствующий регистр для поиска. Методы replace() и split() имеют те же ограничения.

Если ваша задача может быть решена при помощи этих методов, лучше использовать их. Они простые и быстрые, легко читаемые, много может быть сказано о быстром, простом и удобочитаемом коде. Но если вы обнаружите что вы используете большое количество строковых функций с условиями if для обработки специальных случаев, или используете множество последовательных вызовов split() и join() чтобы нарезать на кусочки ваши строки, значит вы нуждаетесь в регулярных выражениях.

Регулярные выражения это мощный и (по большей части) стандартизированный способ для поиска, замены и парсинга текста при помощи комплексных шаблонов. Хотя синтаксис регулярных выражений довольно сложный и выглядит непохожим на нормальный код (прим. пер. «смахивает на perl»), конечный результат часто более удобочитаемый чем набор из последовательных функций для строк. Существует даже способ поместить комментарии внутрь регулярных выражений, таким образом вы можете включить небольшую документацию в регулярное выражение.

Если вы пользовались регулярными выражениями в других языках (таких как Perl, JavaScript, или PHP), синтаксис Python-а будет для вас достаточно привычным. Прочитайте обзор модуля re для того чтобы узнать о доступных функциях и их аргументах.

Учебный пример: Адрес Улицы

Эта серия примеров основана на реальных проблемах, которые появились в моей работе несколько лет назад, когда мне пришлось обработать и стандартизировать адреса улиц, экспортированных из устаревшей системы до того, как произвести импорт в новую систему. (Обратите внимание: это не придуманный пример, им всё ещё можно пользоваться). Этот пример показывает как я подошёл к проблеме:

>>> s = '100 NORTH MAIN ROAD'
>>> s.replace('ROAD', 'RD.')                ①
'100 NORTH MAIN RD.'
>>> s = '100 NORTH BROAD ROAD'
>>> s.replace('ROAD', 'RD.')                ②
'100 NORTH BRD. RD.'
>>> UNIQae610d7ca506639d-nowiki-00000003-QINU  ③
'100 NORTH BROAD RD.'
>>> import re                               ④
>>> re.sub('ROAD$', 'RD.', s)               ⑤
'100 NORTH BROAD RD.'

  • ① Моя задача стандартизировать адрес улицы, например 'ROAD' всегда выражается сокращением 'RD.'. На первый взгляд мне показалось, что это достаточно просто, и я могу использовать метод replace(). В конце концов, все данные уже в верхнем регистре и несовпадение регистра не составит проблемы. Строка поиска 'ROAD' являлась константой и обманчиво простой пример s.replace() вероятно работает.
  • ② Жизнь же, напротив, полна противоречивых примеров, и я быстро обнаружил один из них. Проблема заключалась в том что 'ROAD' появилась в адресе дважды, один раз как 'ROAD', а во второй как часть названия улицы 'BROAD'. Метод replace() обнаруживал 2 вхождения и слепо заменял оба, разрушая таким образом правильный адрес.
  • ③ Чтобы решить эту проблему вхождения более одной подстроки 'ROAD', вам необходимо прибегнуть к следующему: искать и заменять 'ROAD' в последних четырёх символах адреса (s[-4:]), оставляя строку отдельно (s[:-4]). Как вы могли заметить, это уже становится громоздким. К примеру, шаблон зависит от длины заменяемой строки. (Если вы заменяли 'STREET' на 'ST.', вам придется использовать s[:-6] и s[-6:].replace(...).) Не хотели бы вы вернуться к этому коду через полгода для отладки? Я не хотел бы.
  • ④ Пришло время перейти к регулярным выражениям. В Python все функции, связанные с регулярными выражениями содержится в модуле re.
  • ⑤ Взглянем на первый параметр: 'ROAD$'. , означающий «начало строки».) Используя функцию re.sub() вы ищете в строке s регулярное выражение 'ROAD$' и заменяете на 'RD.'. Оно совпадает с 'ROAD' в конце строки s, но не совпадает с 'ROAD', являющимся частью названия 'BROAD', так как оно находится в середине строки s.

Продолжая историю про обработку адресов, я скоро обнаружил, что предыдущий пример совпадения 'ROAD' на конце адреса был недостаточно хорош, так как не все адреса включали в себя определение улицы. Некоторые адреса просто оканчивались названием улицы. Я избегал этого в большинстве случаев, но если название улицы было 'BROAD', тогда регулярное выражение совпадало с 'ROAD' на конце строки 'BROAD', чего я совершенно не хотел.

>>> s = '100 BROAD'
>>> re.sub('ROAD$', 'RD.', s)
'100 BRD.'
>>> re.sub('\\bROAD$', 'RD.', s)   ①
'100 BROAD'
>>> re.sub(r'\bROAD$', 'RD.', s)   ②
'100 BROAD'
>>> s = '100 BROAD ROAD APT. 3'
>>> re. sub(r'\bROAD$', 'RD.', s)   ③
'100 BROAD ROAD APT. 3'
>>> re.sub(r'\bROAD\b', 'RD.', s)  ④
'100 BROAD RD. APT 3'

  • ① В действительности я хотел совпадения с 'ROAD' когда оно на конце строки и является самостоятельным словом (а не частью большего). Чтобы описать это в регулярном выражении необходимо использовать '\b', что означает «слово должно оказаться прямо тут.» В Python это сложно, так как '\' знак в строке должен быть экранирован. Иногда это называют как «бедствие бэкслэша» и это одна из причин почему регулярные выражения проще в Perl чем в Python. Однако недостаток Perl в том что регулярные выражения смешиваются с другим синтаксисом, если у вас ошибка, достаточно сложно определить где она, в синтаксисе или в регулярном выражении.
  • ② Чтобы обойти проблему «бедствие бэкслэша» вы можете использовать то, что называется неформатированная строка (raw string), путём применения префикса строки при помощи символа 'r'. Это скажет Python-у что ничего в этой строке не должно быть экранировано; '\t' это табулятор, но r'\t' это символ бэкслэша '\' , а следом за ним буква 't'. Я рекомендую всегда использовать неформатированную строку, когда вы имеете дело с регулярными выражениями; с другой стороны всё становится достаточно путанным (несмотря на то что наше регулярное выражения уже достаточно запутано).
  • *вздох* К неудаче я скоро обнаружил больше причин противоречащих моей логике. В этом случае адрес улицы содержал в себе цельное отдельное слово 'ROAD' и оно не было на конце строки, так как адрес содержал номер квартиры после определения улицы. Так как слово 'ROAD' не находится в конце строки, регулярное выражение re.sub() его пропускало и мы получали на выходе ту же строку что и на входе, а это то чего вы не хотите.
  • ④ Чтобы решить эту проблему я удалил символ '$' и добавил ещё один '\b'. Теперь регулярное выражение совпадало с 'ROAD' если оно являлось цельным словом в любой части строки, на конце, в середине и в начале.

Учебный пример: Римские цифры

Скорее всего вы видели римские цифры, даже если вы в них не разбираетесь. Вы могли видеть их на копирайтах старых фильмов и ТВ-шоу («Copyright MCMXLVI» вместо «Copyright 1946»), или на стенах в библиотеках университетов («учреждено MDCCCLXXXVIII» вместо « учреждено 1888»). Вы могли видеть их в структуре библиографических ссылок. Эта система отображения цифр относится к древней Римской империи (отсюда и название).

В римских цифрах семь символов, которые повторяются в различных комбинациях для отображения цифр.

  • I = 1
  • V = 5
  • X = 10
  • L = 50
  • C = 100
  • D = 500
  • M = 1000

Нижеследующие правила позволяют конструировать римские цифры:

  • Иногда символы складываются. I это 1, II это 2, и III это 3. VI это 6 (посимвольно, «5 и 1»), VII это 7, и VIII это 8.
  • Десятичные символы (I, X, C, и M) могут быть повторены до 3 раз. Для образования 4 вам необходимо отнять от следующего высшего символа пятёрки. Нельзя писать 4 как IIII; вместо этого, она записывается как IV («на 1 меньше 5»). 40 записывается как XL («на 10 меньше 50»), 41 как XLI, 42 как XLII, 43 как XLIII, и 44 как XLIV («на 10 меньше 50, и на 1 меньше 5»).
  • Иногда символы… обратны сложению. Разместив определённые символы до других, вы вычитаете их от конечного значения. Например 9, вам необходимо отнять от следующего высшего символа десять: 8 это VIII, но 9 это IX («на 1 меньше 10»), не VIIII (так как символ I не может быть повторён 4 раза). 90 это XC, 900 это CM.
  • Пятёрки не могут повторяться. 10 всегда отображается как X, никогда как VV. 100 всегда C, никогда LL.
  • Римские цифры читаются слева направо, поэтому положение символа имеет большое значение. DC это 600; CD это совершенно другая цифра (400, «на 100 меньше 500»). CI это 101; IC это даже не является допустимым Римским числом (так как вы не можете вычитать 1 прямо из 100; вам необходимо записать это как XCIX, «на 10 меньше 100, и на 1 меньше 10»). в начале, это означает что патерн должен совпасть с полной строкой, без других символов до и после символов М.
  • ② Сущность модуля re это функция search(), которая использует патерн регулярного выражения (pattern) и строку ('M') и ищет совпадения в соответствии регулярному выражению. Если совпадение обнаружено, search() возвращает объект который имеет различные методы описания совпадения; если совпадения не обнаружено, search() возвращает None, в Python значение нуля (null). Всё о чём мы заботимся в данный момент, совпадёт ли патерн, это можно сказать глянув на значение возвращаемое функцией search(). 'M' совпадает с этим регулярным выражением, так как первое опциональное M совпадает, а второе опциональное М и третье игнорируется.
  • ③ 'MM' совпадает так как первое и второе опциональное М совпадает а третье игнорируется
  • ④ 'MMM' совпадает полностью, так как все три символа М совпадают
  • ⑤ 'MMMM' не совпадает. Все три М совпадают, но регулярное выражение настаивает на конце строки, (так как требует символ $), а строка ещё не кончилась (из за четвёртого М). Поэтому search() возвращает None.
  • ⑥ Занимательно то, что пустая строка также совпадает с регулярным выражением, так как все символы М опциональны.

Проверка на сотни

? делает патерн необязательным

Расположение сотен более сложное чем тысяч, так как существует несколько взаимно исключающих путей записи и зависит от значения.

  • 100 = C
  • 200 = CC
  • 300 = CCC
  • 400 = CD
  • 500 = D
  • 600 = DC
  • 700 = DCC
  • 800 = DCCC
  • 900 = CM

Таким образом есть четыре возможных патерна:

  • CM
  • CD
  • От ноль до трёх символов C (ноль если месть сотен пусто)
  • D, от последующх нулей до трёх символов C

Два последних патерна комбинированные:

  • опциональное D, за ним от нуля до трёх символов C

Этот пример показывает как проверить позицию сотни в римском числе. ), потом тысячи (M?M?M?). Следом идёт новая часть в скобках, которая описывает три взаимоисключающих патерна разделённых вертикальной линией: CM, CD и D?C?C?C? (который является опциональным D и следующими за ним от нулей до трёх опциональных символов C). Парсер регулярного выражения проверяет каждый из этих патерновв последовательности от левого к правому, выбирая первый подходящий и игнорируя последующие.

  • ② 'MCM' совпадает так как первый M совпадает, второй и третий символ M игнорируется, символы CM совпадают (и CD и D?C?C?C? патерны после этого не анализируются). MCM это римское представление числа 1900.
  • ③ 'MD' совпадает так как первый M совпадает, второй и третий символ M игнорируется, и патерн D?C?C?C? Совпадает с D (три символа C опциональны и игнорируются). MD это римское представление числа 1500.
  • ④ 'MMMCCC' совпадает так как первый M совпадает, и патерн D?C?C?C? сопадает с CCC (символ D опциональный и игнорируются). MMMCCC i это римское представление числа 3300.
  • ⑤ 'MCMC' не совпадает. Первый символ M совпадает, второй и третий символ M игнорируется, также совпадает CM, но патерн $ не совпадает так как вы ещё не в конце строки (вы ещё имеете не совпадающий символ C). Символ C не совпадает как часть патерна D?C?C?C?, так как исключающий патерн CM уже совпал.
  • ⑥ Занимательно то, что пустая строка всё ещё совпадает с регулярным выражением, так как все символы М опциональны и игнорируются и пустая строка совпадает с патерном D?C?C?C? где все символы опциональны и игнорируются.

Опаньки! Вы заметили как быстро регулярные выражения становятся отвратительными? И пока что мы обработали только позиции тысяч и сотен в римском представлении чисел. Но если последуете далее, вы обнаружите что десятки и единицы описать будет легче, так как они имеют такой же патерн. Тем временем давайте рассмотрим другой путь описать этот патерн.

Использование синтаксиса {n, m}

модификатор {1,4} совпадает с 1 до 4 вхождением патерна

В предыдущей секции мы имели дело с патерном где одинаковый символ может повториться до трёх раз. M{0,3}$'        ①
>>> re.search(pattern, 'M')     ②
UNIQae610d7ca506639d-nowiki-00000049-QINU
>>> re.search(pattern, 'MM')    ③
<_sre.SRE_Match object at 0x008EE090>
>>> re.search(pattern, 'MMM')   ④
<_sre.SRE_Match object at 0x008EEDA8>
>>> re.search(pattern, 'MMMM')  ⑤
>>>

  • ① Этот патерн говорит: «Совпасть с началом строки, потом с от нуля до трёх символов М находящимися где угодно, потом с концом строки». Символы 0 и 3 могут быть любыми цифрами, если вам необходимо совпадение с 1 и более символами М, необходимо записать М{1,3}.
  • ② Тут патерн совпадает с началом строки, потом с одним из возможных трёх символов М, потом с концом строки.
  • ③ Тут патерн совпадает с началом строки, потом с двумя из возможных трёх символов М, потом с концом строки.
  • ④ Тут патерн совпадает с началом строки, потом с тремя из возможных трёх символов М, потом с концом строки. M?M?M?(CM|CD|D?C?C?C?)(XC|XL|L?X?X?X?)$'
    >>> re.search(pattern, 'MCMXL') ①
    <_sre.SRE_Match object at 0x008EEB48>
    >>> re.search(pattern, 'MCML') ②
    <_sre.SRE_Match object at 0x008EEB48>
    >>> re.search(pattern, 'MCMLX') ③
    UNIQae610d7ca506639d-nowiki-0000004F-QINU
    >>> re.search(pattern, 'MCMLXXX') ④
    UNIQae610d7ca506639d-nowiki-00000050-QINU
    >>> re.search(pattern, 'MCMLXXXX') ⑤
    >>>

    • ① Тут патерн совпадает с началом строки, потом с первым опциональным символом М, потом CM, потом XL, потом с концом строки. Вспомните что синтаксис (A|B|C) означает «совпасть только с одним оз символов A, B или C» У нас совпадает XL, и мы игнорируем XC и L?X?X?X?, а после этого переходим к концу строки. MCMXL это римское представление числа 1940.
    • ② Тут патерн совпадает с началом строки, потом с первым опциональным символом М, потом CM, потом с L?X?X?X?. Из L?X?X?X? Совпадает L и пропускает три опциональных символа X. После этого переходит к концу строки. MCML это римское представление числа 1950.
    • ③ Тут патерн совпадает с началом строки, потом с первым опциональным символом М, потом CM, потом с опциональным L и первым опциональным X, пропуская второй и третий опциональные символы X, после этого переходит к концу строки. MCMLX это римское представление числа 1960.
    • ④ Тут патерн совпадает с началом строки, потом с первым опциональным символом М, потом CM, потом с опциональным L и всеми тремя опциональными символами X, после этого переходит к концу строки. MCMLXXX это римское представление числа 1980.
    • ⑤ Тут патерн совпадает с началом строки, потом с первым опциональным символом М, потом CM, потом с опциональным L и всеми тремя опциональными символами X, после этого не совпадает с концом строки, так как есть ещё один символ X, таким образом патерн не срабатывает и возвращает None. MCMLXXXX это недопустимое римское число.


    (A|B) совпадает либо с A либо с B. M{0,3}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$'
    >>> re.search(pattern, 'MDLV') ①
    UNIQae610d7ca506639d-nowiki-00000053-QINU
    >>> re.search(pattern, 'MMDCLXVI') ②
    UNIQae610d7ca506639d-nowiki-00000054-QINU
    >>> re.search(pattern, 'MMMDCCCLXXXVIII') ③
    UNIQae610d7ca506639d-nowiki-00000055-QINU
    >>> re.search(pattern, 'I') ④
    UNIQae610d7ca506639d-nowiki-00000056-QINU

    • ① Тут патерн совпадает с началом строки, потом с одним из трёх возможных символов М, потом D?C{0,3}. Из них совпадает только опциональное D и ни один из опциональных C. Далее совпадает опциональное L из L?X{0,3} и ни один из трёх опциональных X. После совпадает с V из V?I{0,3} и ни с одним из трёх опциональных I и наконец с концом строки. MDLV это римское представление числа 1555.
    • ② Тут патерн совпадает с началом строки, потом с двумя из трёх возможных символов М, потом D и один опциональный C из D?C{0,3}. Потом L?X{0,3} с L и один из трёх возможных X, потом V?I{0,3} с V и одним из трёх I, потом с концом строки. MMDCLXVI это римское представление числа 2666.
    • ③ Тут патерн совпадает с началом строки, потом с тремя из трёх M, потом D и C из D?C{0,3}, потом L?X{0,3} с L и три из трёх X, потом V?I{0,3} с V и тремя из трёх I, потом конец строки. MMMDCCCLXXXVIII это римское представление числа 3888, и это максимально длинное римское число которое можно записать без расширенного синтаксиса.
    • ④ Смотрите внимательно. (Я чувствую себя магом, «Смотрите внимательно детки, сейчас кролик вылезет из моей шляпы ;)» Тут совпадает начало строки, ни один из трёх М, потом D?C{0,3} пропускает опциональный D и три опциональных C, потом L?X{0,3} пропуская опциональный L и три опциональных X,потом V?I{0,3} пропуская опциональный V и один из трёх опциональных I. Потом конец строки. Стоп, фуф.


    Если вы следовали всему и поняли с первой попытки, значит у вас получается лучше чем у меня. Теперь представьте что вы пытаетесь разобраться в чьих то регулярных выражениях в важной функции в рамках огромной программы. Или например представьте что вы возвращаетесь к собственной программе через несколько месяцев. Я делал это и это не слишком приятное зрелище.

    А сейчас давайте исследуем альтернативный синтаксис, который позволит легче выполнять поддержку ваших выражений.

    Подробные регулярные выражения

    До сих пор вы имели дело с тем что я называю «компактными» регулярными выражениями. Как вы могли заметить они трудны для прочтения, даже если вы понимаете что они делают. Нет гарантии что вы сможете разобраться в них спустя шесть месяцев. Что вам действительно необходимо так это вложенная документация

    Python позволяет вам сделать это при помощи подробных регулярных выражений. Подробные регулярные выражения отличаются от компактных двумя способами:

    • Пустые строки игнорируются, пробелы, табы и возвраты каретки не совпадают соответственно. Они вообще не совпадают. (Если вы хотите совпадения с пробелом в подробном регулярном выражении, вам необходимо поставить бэкслэш перед ним.                         # начало строки
            M{0,3}                    # тысячи - 0 до 3 M
            (CM|CD|D?C{0,3})          # сотни — 900 (CM), 400 (CD), 0-300 (0 до 3 C),
                                      # или 500-800 (D, с последующими от 0 до 3 C)
            (XC|XL|L?X{0,3})          # десятки - 90 (XC), 40 (XL), 0-30 (0 до 3 X),
                                      # или 50-80 (L, с последующими от 0 до 3 X)
            (IX|IV|V?I{0,3})          # единицы - 9 (IX), 4 (IV), 0-3 (0 до 3 I),
                                      # или 5-8 (V, с последующими от 0 до 3 I)
            $                         # конец строки
            '''
       >>> re.search(pattern, 'M', re.VERBOSE)                 ①
       UNIQae610d7ca506639d-nowiki-00000058-QINU
       >>> re.search(pattern, 'MCMLXXXIX', re.VERBOSE)         ②
       UNIQae610d7ca506639d-nowiki-00000059-QINU
       >>> re.search(pattern, 'MMMDCCCLXXXVIII', re.VERBOSE)   ③
       <_sre.SRE_Match object at 0x008EEB48>
       >>> re. search(pattern, 'M')                             ④

      • ① Главное что надо запомнить, это то что необходимо добавлять экстра аргументы для работы с ними: re.VERBOSE это константа определённая в модуле re которая служит сигналом что патерн должен быть использован как подробное регулярное выражение. Как вы можете видеть, этот патерн содержит большое количество пустых строк. (и все они игнорируются), а также несколько комментариев (которые игнорируются также). Если мы игнорируем комментарии и пустые строки, то получается то же самое регулярное выражение что и в предыдущем примере, но в гораздо более читабельном виде.
      • ② Здесь совпадает начало строки, потом одно и трёх возможных M, потом CM, потом L и три из возможных X, потом IX, потом конец строки.
      • ③ Здесь совпадает начало строки, потом три из трёх возможных M, потом D и три из возможных трёх C, потом L и три из трёх возможных X, потом V и три из трёх возможных I, потом конец строки.
      • ④ Тут не совпадает. Почему? Так как отсутствует флаг re.VERBOSE и функция re.search рассматривает патерн как компактное регулярное выражение, с значащими пробелами и символами #. Python не может автоматически определить является ли регулярное выражение подробным или нет. Python рассматривает каждое регулярное выражение как компактное до тех пор пока вы не укажете что оно подробное.

      Учебный пример: Обработка телефонных номеров

      \d совпадает с любыми цифрами (0–9). \D совпадает со всем кроме цифр


      До сих пор вы были сконцентрированы на полных патернах. Совпадает патерн или не совпадает, но регулярные выражения могут быть гораздо мощнее этого. Когда регулярное выражение совпадает с чем либо, вы можете получить специально выделенную часть совпадения. Вы можете узнать что совпало и где.

      Этот пример появился из ещё одной реальной проблемы которые я испытывал на предыдущей работе. Проблема была в обработке американских телефонных номеров. Клиент хочет ввести телефонный номер в простое поле (без разделителей), но потом также хочет сохранить индекс, магистраль, номер и опционально добавочную информацию в базе данных компании. (\d{3})-(\d{3})-(\d{4})$')  ①
      >>> phonePattern.search('800-555-1212').groups()             ②
      ('800', '555', '1212')
      >>> phonePattern.search('800-555-1212-1234')                 ③
      >>> phonePattern.search('800-555-1212-1234').groups()        ④
      Traceback (most recent call last):
        File "<stdin>", line 1, in <module>
      AttributeError: 'NoneType' object has no attribute 'groups'

      • ① Всегда читайте регулярное выражение слева направо. Выражение совпадает с началом строки и потом с (\d{3}). Что такое \d{3}? Итак , \d значит «любая цифра» (от 0 до 9). {3} значит «совпадение с конкретно тремя цифрами»; это вариации на тему {n, m} синтаксиса который вы наблюдали ранее. Если заключить это выражение в круглые скобки, то это значит «совпасть должно точно три цифры и потом запомнить их как группу которую я запрошу позже». Потом выражение должно совпасть с дефисом. Потом совпасть с другой группой из трёх цифр, потм опять дефис. Потом ещё одна группа из четырёх цифр. И в конце совпадение с концом строки.
      • ② Чтобы получить доступ к группам которые запомнил обработчик регулярного выражения, используйте метод groups() на объекте который возвращает метод search(). Он должен вернуть кортеж такого количества групп, которое было определено в регулярном выражении. В нашем случае определены три группы, одна с тремя цифрами, другая с тремя цифрами и третья с четырьмя цифрами.
      • ③ Это регулярное выражение не окончательный ответ, так как оно не обрабатывает расширение после телефонного номера. Для этого вы должны расширить регулярное выражение.
      • ④ Вот почему вы не должны использовать «цепочку» из методов search() и groups() в продакшн коде. Если метод search() не вернёт совпадения, то он вернёт None, это не стандартный объект регулярного выражения. Вызов None.groups() генерирует очевидное исключение: None не имеет метода groups(). (Конечно же это немного менее очевидно, когда вы получаете это исключение из глубин вашего кода. (\d{3})-(\d{3})-(\d{4})-(\d+)$')  ①
        >>> phonePattern.search('800-555-1212-1234').groups()              ②
        ('800', '555', '1212', '1234')
        >>> phonePattern.search('800 555 1212 1234')                       ③
        >>>
        >>> phonePattern.search('800-555-1212')                            ④
        >>>

        • ① Это регулярное выражение почти идентично предыдущему. Так же как и до этого оно совпадает с началом строки, потом с запомненной группой из трёх цифр, потом дефис, потом запомненная группа из трёх цифр, потом дефис, потом запомненная группа из четырёх цифр. Что же нового? Это совпадение с другим дефисом и запоминаемая группа из одной и более цифры.
        • ② Метод groups() теперь возвращает кортеж из четырёх элементов, а регулярное выражение теперь запоминает четыре группы.
        • ③ К неудаче это регулярное выражение не является финальным ответом, так как оно подразумевает что различные части номера разделены дефисом. (\d{3})\D+(\d{3})\D+(\d{4})\D+(\d+)$')  ①
          >>> phonePattern.search('800 555 1212 1234').groups()  ②
          ('800', '555', '1212', '1234')
          >>> phonePattern.search('800-555-1212-1234').groups()  ③
          ('800', '555', '1212', '1234')
          >>> phonePattern.search('80055512121234')              ④
          >>>
          >>> phonePattern.search('800-555-1212')                ⑤
          >>>

          • ① Держите свою шляпу. У вас совпадает начало строки, потом группа из трёх цифр, потом \D+. Что это за чертовщина? Ок, \D совпадает с любым символом кроме цифр и также «+» означает «1 или более». Итак \D+ означает один или более символом не являющихся цифрами. Это то что вы используете вместо символа дефиса «-» чтобы совпадало с любыми разделителями.
          • ② Использование \D+ вместо «-» значит, что теперь регулярное выражение совпадает с телефонным номером разделённым пробелами вместо дефисов.
          • ③ Конечно телефонные номера разделенные дефисами тоже срабатывают. (\d{3})\D*(\d{3})\D*(\d{4})\D*(\d*)$')  ①
            >>> phonePattern.search('80055512121234').groups()      ②
            ('800', '555', '1212', '1234')
            >>> phonePattern.search('800.555.1212 x1234').groups()  ③
            ('800', '555', '1212', '1234')
            >>> phonePattern.search('800-555-1212').groups()        ④
            ('800', '555', '1212', '')
            >>> phonePattern.search('(800)5551212 x1234')           ⑤
            >>>

            • ① Только одно изменение, замена «+» на «*». Вместо \D+ между частями номера, теперь используется \D*. Помните что «+» означает «1 или более»? Ок, «*» означает «ноль или более». Итак теперь вы можете обработать номер даже если он не содержит разделителей.
            • ② Подумать только, это действительно работает. Почему? У вас совпадает начало строки, потом запоминается группа из трёх цифр (800), потом ноль или более нецифровых символов, потом запоминается группа из трёх цифр (555), потом ноль или более нецифровых символов, потом запоминается группа из четырёх цифр (1212), потом ноль или более нецифровых символов, потом запоминается группа из произвольного количества цифр (1234), потом конец строки. \D*(\d{3})\D*(\d{3})\D*(\d{4})\D*(\d*)$')  ①
              >>> phonePattern.search('(800)5551212 ext. 1234').groups()                  ②
              ('800', '555', '1212', '1234')
              >>> phonePattern.search('800-555-1212').groups()                            ③
              ('800', '555', '1212', '')
              >>> phonePattern.search('work 1-(800) 555.1212 #1234')                      ④
              >>>

              • ① Это то же самое что и в предыдущем примере, кроме \D*, ноль или более нецифровых символов, до первой запомненной группы (код города). Заметьте что вы не запоминаете те нецифровые символы до кода города (они не в скобках). Если вы обнаружите их, вы просто пропустите их и запомните код города.
              • ② Вы можете успешно обработать телефонный номер, даже со скобками до кода города. (Правая скобка также обрабатывается; как нецифровой символ и совпадает с \D* после первой запоминаемой группы.)
              • ③ Простая проверка не поломали ли мы чего-то, что должно было работать. Так как лидирующие символы полностью опциональны, совпадает начало строки, ноль нецифровых символов, потом запоминается группа из трёх цифр (800), потом один нецифровой символ (дефис), потом группа из трёх цифр (555), потом один нецифровой (дефис), потом запоминается группа из четырёх цифр (1212), потом ноль нецифровых символов, потом группа цифр из нуля символов, потом конец строки.
              • ④ Вот где регулярное выражение выколупывает мне глаза тупым предметом. Почему этот номер не совпал? Потому что 1 находится до кода города, но вы допускали что все лидирующие символы до кода города не цифры (\D*).

              Давайте вернёмся назад на секунду. До сих пор регулярное выражение совпадало с началом строки. Но сейчас вы видите что в начале могут быть непредсказуемые символы которые мы хотели бы проигнорировать. Лучше не пытаться подобрать совпадение для них, а просто пропустить их все, давайте сделаем другое допущение: не пытаться совпадать с началом строки вообще. Этот подход показан в следующем примере.

              >>> phonePattern = re.compile(r'(\d{3})\D*(\d{3})\D*(\d{4})\D*(\d*)$')  ①
              >>> phonePattern.search('work 1-(800) 555.1212 #1234').groups()         ②
              ('800', '555', '1212', '1234')
              >>> phonePattern.search('800-555-1212')                                 ③
              ('800', '555', '1212', '')
              >>> phonePattern.search('80055512121234')                               ④
              ('800', '555', '1212', '1234')

              • ① Заметьте отсутствие ^ в регулярном выражении. Вы больше не совпадаете с началом строки. Ничего теперь не подсказывает как следует поступать с введёнными данными вашему регулярному выражению. Обработчик регулярного выражения будет выполнять тяжелую работу чтобы разобраться где же введённая строка начнёт совпадать.
              • ② Теперь вы можете успешно обработать телефонный номер который включает лидирующие символы и цифры, плюс разделители любого типа между частями номера.
              • ③ Простая проверка. Всё работает.
              • ④ И даже это работает тоже.

              Видите как быстро регулярное выражение выходит из под контроля? Бросим взгляд на предыдущие итерации. Можете ли вы объяснить разницу между одним и другим?

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

              >>> phonePattern = re.compile(r'''
                              # don't match beginning of string, number can start anywhere
                  (\d{3})     # area code is 3 digits (e. g. '800')
                  \D*         # optional separator is any number of non-digits
                  (\d{3})     # trunk is 3 digits (e.g. '555')
                  \D*         # optional separator
                  (\d{4})     # rest of number is 4 digits (e.g. '1212')
                  \D*         # optional separator
                  (\d*)       # extension is optional and can be any number of digits
                  $           # end of string
                  ''', re.VERBOSE)
              >>> phonePattern.search('work 1-(800) 555.1212 #1234').groups()  ①
              ('800', '555', '1212', '1234')
              >>> phonePattern.search('800-555-1212')                          ②
              ('800', '555', '1212', '')

              • ① Кроме того что оно разбито на множество строк, это совершенно такое же регулярное выражение как было в последнем шаге, и не будет сюрпризом что оно обрабатывает такие же входные данные.
              • ② Финальная простая проверка. Да, всё ещё работает. Вы сделали это.

              Итоги

              Это всего лишь верхушка айсберга того что могут делать регулярные выражения. совпадение с началом строки.
              $ совпадение с концом строки.
              \b совпадает с границей слова.
              \d совпадает с цифрой.
              \D совпадает с не цифрой.
              x? совпадает с опциональным символом x (другими словами ноль или один символов x).
              x* совпадает с ноль или более x.
              x+ совпадает с один или более x.
              x{n, m} совпадает с x не менее n раз, но не более m раз.
              (a|b|c) совпадает с a или b или c.
              (x) группа для запоминания. Вы можете получить значение используя метод groups() на объекте который возвращает re.search.

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

              Замена строк в Python (replace, translate, re.sub, re.subn)

              В этой статье описывается, как заменить строки в Python.

              • Заменить подстроки: replace ()
                • Укажите максимальное количество замен: count
                • Заменить несколько разных подстрок
                • Заменить символ новой строки
              • Заменить несколько разных символов: translate ()
              • Заменить регулярным выражением: re.sub () , re.subn ()
                • Заменить несколько подстрок одной строкой
                • Заменить на подходящую деталь
                • Узнать количество замененных деталей
              • Заменить позицией: срез

              В любом случае вы можете удалить исходную строку, указав пустую строку '' в качестве строки замены.

              Заменить подстроки:

              replace ()

              Используйте replace () для замены подстрок.

              Укажите старую строку old для первого аргумента и новую строку new для второго аргумента.

                s = 'один два один два один'
              
              print (s. replace ('', '-'))
              # один-два-один-два-один
                

              Если указать пустую строку '' как новый , будет удалено старое .

                печать (s.replace ('', ''))
              # onetwoonetwoone
                

              Укажите максимальное количество замен:

              количество

              Вы можете указать максимальное количество замен в третьем аргументе count .Если задан аргумент count , заменяются только первые count вхождений.

                печать (s.replace ('one', 'XXX'))
              # ХХХ два ХХХ два ХХХ
              
              print (s.replace ('один', 'XXX', 2))
              # XXX два XXX два один
                

              Заменить несколько разных подстрок

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

              Не существует способа заменить несколько разных строк разными, но можно повторно применить replace () .

                print (s.replace ('one', 'XXX').  Replace ('two', 'YYY'))
              # XXX ГГГ XXX ГГГ XXX
                

              Он просто вызывает replace () по порядку, поэтому, если первые новые содержат следующие старые , первые новые также заменяются. Для порядка нужно быть осторожным.

                печать (s.replace ('one', 'XtwoX'). Replace ('two', 'YYY'))
              # XYYYX YYY XYYYX YYY XYYYX
              
              print (s.replace ('два', 'ГГГ'). replace ('один', 'XtwoX'))
              # XtwoX YYY XtwoX YYY XtwoX
                

              При замене нескольких символов (строка длиной 1 ) вы можете использовать метод translate () , описанный ниже.

              Заменить символ новой строки

              Если существует только один тип символа новой строки, вы можете указать его в качестве первого аргумента replace () .

                s_lines = 'один \ nдва \ nтри'
              печать (s_lines)
              # один
              # два
              # три
              
              print (s_lines.replace ('\ n', '-'))
              # один два три
                

              Будьте осторожны, если \ n (LF), используемый в ОС Unix, включая Mac, и \ r \ n (CR + LF), используемый в ОС Windows, смешаны.

              Поскольку \ n входит в \ r \ n , желаемый результат не может быть получен в зависимости от заказа.В следующем примере также показан результат repr () , который выводит \ n и \ r в виде строки.

                s_lines_multi = 'один \ nдва \ r \ nтри'
              печать (s_lines_multi)
              # один
              # два
              # три
              
              печать (repr (s_lines_multi))
              # 'one \ ntwo \ r \ nthree'
              
              print (s_lines_multi.replace ('\ r \ n', '-'). replace ('\ n', '-'))
              # один два три
              
              print (repr (s_lines_multi.replace ('\ r \ n', '-'). replace ('\ n', '-')))
              # 'один два три'
              
              print (s_lines_multi.replace ('\ n', '-'). replace ('\ r \ n', '-'))
              # -трио
              
              печать (repr (s_lines_multi.replace ('\ n', '-'). replace ('\ r \ n', '-')))
              # 'один-два \ r-три'
                

              Также можно использовать splitlines () , который возвращает разделенный список с различными символами новой строки, и join () , который объединяет списки со строками.

              Этот способ безопасен и рекомендуется, особенно если вы не знаете, какие символы новой строки включены.

                печать (s_lines_multi.splitlines ())
              # ['один два три']
              
              print ('-'. join (s_lines_multi.splitlines ()))
              # один два три
                

              В следующих статьях описаны другие операции, связанные с разделением строк и переносами строк.

              Заменить несколько разных символов:

              translate ()

              Используйте метод translate () для замены нескольких разных символов.

              Таблица преобразования, указанная в translate () , создается с помощью str.maketrans () .

              Укажите словарь, ключ которого - старый символ, а значение - новая строка в str.maketrans () .

              Старый символ должен быть символом (строка длиной 1 ).Новая строка - это строка или Нет , где Нет удаляет старые символы.

                s = 'один два один два один'
              
              print (s. translate (str.maketrans ({'o': 'O', 't': 'T'})))
              # One TwO One TwO One
              
              print (s.translate (str.maketrans ({'o': 'XXX', 't': None})))
              # XXXne wXXX XXXne wXXX XXXne
                

              str.maketrans () также может принимать три строки в качестве аргументов вместо словаря.

              Первый аргумент - это строка, в которой объединены старые символы, второй аргумент - это строка, в которой объединены новые символы, а третий аргумент - это строка, в которой объединены символы, подлежащие удалению.

                печать (s.translate (str.maketrans ('ow', 'XY', 'n')))
              # Xe tYX Xe tYX Xe
                

              В этом случае длины первого и второго аргументов должны совпадать.

                # print (s.translate (str.maketrans ('ow', 'XXY', 'n')))
              # ValueError: первые два аргумента maketrans должны иметь одинаковую длину
                

              Заменить регулярным выражением:

              re.sub () , re.subn ()

              Если вы используете replace () или translate () , они будут заменены, если они полностью соответствуют старой строке.

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

              В re.sub () укажите шаблон регулярного выражения в первом аргументе, новую строку во втором аргументе и строку для обработки в третьем аргументе.

                импорт ре
              
              s ='[email protected] [email protected] [email protected] '
              
              print (re.sub ('[a-z] * @', 'ABC @', s))
              # [email protected] [email protected] [email protected]
                

              Как и в случае с replace () , вы можете указать максимальное количество замен в четвертом аргументе count .

                print (re.sub ('[a-z] * @', 'ABC @', s, 2))
              # [email protected] [email protected] [email protected]
                

              Заменить несколько подстрок одной строкой

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

              Заключите строку с [] , чтобы соответствовать любому одиночному символу в ней. Его можно использовать для замены нескольких разных символов одной и той же строкой.

                печать (re.sub ('[xyz]', '1', s))
              # ааа @ 111.com [email protected] [email protected]
                

              Если шаблоны разделены символом | , соответствует любому шаблону. Конечно, можно использовать специальные символы регулярного выражения для каждого шаблона, но это нормально, даже если обычная строка указана как есть.

              Может использоваться для замены нескольких разных строк одной и той же строкой.

                печать (re.sub ('aaa | bbb | ccc', 'ABC', s))
              # [email protected] [email protected] [email protected]
                

              Заменить на подходящую деталь

              Если часть шаблона заключена в () , вы можете использовать строку, которая соответствует части, заключенной в () в новой строке.

                print (re.sub ('([a-z] *) @', '\\ 1-123 @', s))
              # [email protected] [email protected] [email protected]
              
              print (re.sub ('([a-z] *) @', r '\ 1-123 @', s))
              # [email protected] com [email protected] [email protected]
                

              \ 1 соответствует части, которая соответствует () . Если () несколько, используйте их как \ 2 , \ 3 ....

              Необходимо экранировать \ , например \\ 1 , если это обычная строка, окруженная '' или "" , но если это необработанная строка с r в начале, например r '' , можно написать \ 1 .

              Посчитайте количество замененных деталей

              re.subn () возвращает кортеж замененной строки и количество замененных частей.

                t = re.subn ('[a-z] * @', 'ABC @', s)
              печать (т)
              # ('[email protected] [email protected] [email protected] ', 3)
              
              печать (тип (т))
              # <класс 'кортеж'>
              
              печать (t [0])
              # [email protected] [email protected] [email protected]
              
              печать (t [1])
              # 3
                

              Заменить позицией: срез

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

                s = 'abcdefghij'
              
              печать (s [: 4] + 'XXX' + s [7:])
              # abcdXXXhij
                

              Длину строки (количество символов) можно получить с помощью len () , поэтому ее можно записать следующим образом:

                s_replace = 'XXX'
              я = 4
              
              print (s [: i] + s_replace + s [i + len (s_replace):])
              # abcdXXXhij
                

              Количество символов не обязательно должно совпадать, поскольку оно просто объединяет разные строки между разделенными строками.

                отпечаток (s [: 4] + '-' + s [7:])
              # abcd-hij
                

              Также возможно создать новую строку, вставив другую строку в любом месте строки.a ... s $ абс Нет совпадений псевдоним Матч бездна Матч Псевдоним Нет совпадений Счеты Нет совпадений


              В Python есть модуль с именем re для работы с RegEx. a...s $ ' test_string = 'бездна' результат = re.match (шаблон, тестовая_строка) если результат: print ("Успешный поиск.") еще: print ("Неудачный поиск.")

Здесь мы использовали функцию re.match () для поиска шаблона в строке test_string . Метод возвращает объект соответствия, если поиск успешен. В противном случае возвращается Нет .


Есть несколько других функций, определенных в модуле re для работы с RegEx. $ * + ? {} () \ |


[] — Квадратные скобки

Квадратные скобки обозначают набор символов, которые вы хотите сопоставить.

Выражение Строка Соответствуют?
[abc] а 1 совпадение
ac 2 совпадения
Привет, Джуд Нет совпадений
abc de ca 5 совпадений

Здесь [abc] будет соответствовать, если строка, которую вы пытаетесь сопоставить, содержит любое из a , b или c . используется для проверки, начинается ли строка с определенного символа.ab abc 1 совпадение акб Нет совпадений (начинается с a , но не сопровождается b )


$ Доллар

Символ доллара $ используется для проверки, заканчивается ли строка определенным символом .

Выражение Строка Соответствуют?
a $ а 1 совпадение
формула 1 совпадение
кабина Нет совпадений

* Звезда

Звездочка * соответствует нулю или более вхождений оставшегося образца.

Выражение Строка Соответствуют?
ма * н мин 1 совпадение
человек 1 совпадение
maaan 1 совпадение
основной Нет совпадений ( за не следует n )
женщина 1 совпадение

+ Плюс

Знак плюс + соответствует одному или нескольким вхождениям шаблона, оставленному ему.

Выражение Строка Соответствуют?
ма + н мин Не совпадает (нет символ )
человек 1 совпадение
maaan 1 совпадение
основной Нет совпадений (за a не следует n)
женщина 1 совпадение

? Вопросительный знак

Знак вопроса ? соответствует нулю или одному вхождению оставшегося шаблона.

Выражение Строка Соответствуют?
ма? Н мин 1 совпадение
человек 1 совпадение
maaan Нет совпадений (более одного символа - )
основной Нет совпадений (за a не следует n)
женщина 1 совпадение

{} Раскосы

Рассмотрим этот код: {n, m} .Это означает, что ему осталось не менее n и не более m повторений шаблона.

Выражение Строка Соответствуют?
a {2,3} abc dat Нет совпадений
abc daat 1 совпадение (при d aa t )
aabc daaat 2 совпадения (на aa bc и d aaa t )
aabc daaaat 2 совпадения ( aa bc и d aaa at )

Попробуем еще один пример. Это RegEx [0-9] {2, 4} соответствует не менее 2 цифрам, но не более 4 цифрам

Выражение Строка Соответствуют?
[0-9] {2,4} ab123csde 1 совпадение (совпадение в ab 123 csde )
12 и 345673 3 совпадения ( 12 , 3456 , 73 )
1 и 2 Нет совпадений

| Чередование

Вертикальная полоса | используется для чередования (оператор или ).

Выражение Строка Соответствуют?
a | b код Нет совпадений
аде 1 совпадение (совпадение по номеру и de )
acdbea 3 совпадения (на a cd b e a )

Здесь a | b соответствует любой строке, содержащей a или b


() Группа

Круглые скобки () используется для группировки подшаблонов. Например, (a | b | c) xz соответствует любой строке, которая соответствует a или b или c , за которым следует xz

Выражение Строка Соответствуют?
(a | b | c) xz ab xz Нет совпадений
abxz 1 совпадение (совпадение на a bxz )
axz cabxz 2 совпадения (на axz bc ca bxz )

\ Обратная косая черта

Люфт \ используется для экранирования различных символов, включая все метасимволы.Например,

\ $ соответствует , если строка содержит $ , за которым следует = . Здесь $ не интерпретируется механизмом RegEx особым образом.

Если вы не уверены, имеет ли символ особое значение, вы можете поставить перед ним \ . Это гарантирует, что с персонажем не будут обращаться особым образом.


Особые последовательности

Специальные последовательности упрощают написание часто используемых шаблонов.Вот список специальных последовательностей:

\ A — соответствует, если указанные символы находятся в начале строки.

Выражение Строка Соответствуют?
\ Athe солнце Матч
На солнце Нет совпадений

\ b — соответствует, если указанные символы находятся в начале или в конце слова.

Выражение Строка Соответствуют?
\ bfoo футбол Матч
футбольный мяч Матч
Футбольный мяч Нет совпадений
фоо \ б Фу Матч
тест afoo Матч
самый быстрый Нет совпадений

\ B — Напротив \ b .Соответствует, если указанные символы , а не в начале или конце слова.

Выражение Строка Соответствуют?
\ Bfoo футбол Нет совпадений
футбольный мяч Нет совпадений
Футбольный мяч Матч
foo \ B Фу Нет совпадений
тест afoo Нет совпадений
самый быстрый Матч

\ d — соответствует любой десятичной цифре. 0-9]

Выражение Строка Соответствуют?
\ D 1ab34 "50 3 совпадения (при 1 ab 34 " 50 )
1345 Нет совпадений

\ s — соответствует, где строка содержит любой символ пробела.Эквивалент [\ t \ n \ r \ f \ v] .

Выражение Строка Соответствуют?
\ с Python RegEx 1 совпадение
PythonRegEx Нет совпадений

\ S — соответствует, где строка содержит любой непробельный символ. \ t \ n \ r \ f \ v] .

Выражение Строка Соответствуют?
\ S а б 2 совпадения (на a b )
Нет совпадений

\ w — соответствует любому буквенно-цифровому символу (цифрам и алфавитам). Эквивалент [a-zA-Z0-9_] .Кстати, подчеркивание _ также считается буквенно-цифровым символом.

Выражение Строка Соответствуют?
\ w 12 & ":; c 3 совпадения (на 12 & ":; c )
% ">! Нет совпадений

\ W — соответствует любому не буквенно-цифровому символу. a-zA-Z0-9_]

Выражение Строка Соответствуют?
\ W 1a2% c 1 совпадение (на 1 a 2 % c )
Python Нет совпадений

\ Z — соответствует, если указанные символы находятся в конце строки.

Выражение Строка Соответствуют?
Python \ Z Мне нравится Python 1 совпадение
Мне нравится программирование на Python Нет совпадений
Python - это весело. Нет совпадений

Совет: Для создания и тестирования регулярных выражений вы можете использовать инструменты тестера RegEx, такие как regex101. Этот инструмент не только помогает вам создавать регулярные выражения, но и помогает вам их изучить.

Теперь вы понимаете основы RegEx, давайте обсудим, как использовать RegEx в вашем коде Python.


Python RegEx

Python имеет модуль с именем re для работы с регулярными выражениями.Чтобы использовать его, нам нужно импортировать модуль.

  импорт ре  

Модуль определяет несколько функций и констант для работы с RegEx.


re.findall ()

Метод re.findall () возвращает список строк, содержащих все совпадения.


Пример 1: re.findall ()

 
# Программа для извлечения чисел из строки

импорт ре

строка = 'привет 12 привет 89.  Привет 34'
шаблон = '\ d +'

результат = re.findall (шаблон, строка)
печать (результат)

# Вывод: ['12', '89', '34']
  

Если шаблон не найден, re.findall () возвращает пустой список.


re.split ()

Метод re.split разбивает строку, в которой есть совпадение, и возвращает список строк, в которых произошло разбиение.


Пример 2: re.split ()

 
импорт ре

string = 'Двенадцать: 12 Восемьдесят девять: 89.'
шаблон = '\ d +'

результат = re.split (шаблон, строка)
печать (результат)

# Вывод: ['Двенадцать:', 'Восемьдесят девять:', '.']
  

Если шаблон не найден, re.split () возвращает список, содержащий исходную строку.


Вы можете передать аргумент maxsplit методу re.split () . Это максимальное количество разбиений, которое произойдет.

 
импорт ре

string = 'Двенадцать: 12 Восемьдесят девять: 89 Девять: 9. '
шаблон = '\ d +'

# maxsplit = 1
# разделить только при первом вхождении
результат = re.split (шаблон, строка, 1)
печать (результат)

# Вывод: ['Двенадцать:', 'Восемьдесят девять: 89 Девять: 9.']
  

Кстати, значение по умолчанию maxsplit равно 0; имея в виду все возможные расколы.


re.sub ()

Синтаксис re.sub () :

  re.sub (шаблон, заменить, строка)  

Метод возвращает строку, в которой совпадающие вхождения заменяются содержимым переменной replace .


Пример 3: re.sub ()

 
# Программа для удаления всех пробелов
импорт ре

# многострочная строка
строка = 'abc 12 \
de 23 \ n f45 6 '

# соответствует всем пробельным символам
шаблон = '\ s +'

# пустой строки
заменить = ''

new_string = re.sub (шаблон, замена, строка)
печать (новая_строка)

# Вывод: abc12de23f456
  

Если шаблон не найден, re.sub () возвращает исходную строку.


Вы можете передать count в качестве четвертого параметра методу re.sub () . Если опущено, результат будет 0. Это заменит все вхождения.

 
импорт ре

# многострочная строка
строка = 'abc 12 \
de 23 \ n f45 6 '

# соответствует всем пробельным символам
шаблон = '\ s +'
заменить = ''

new_string = re.sub (r '\ s +', заменить, строка, 1)
печать (новая_строка)

# Вывод:
# abc12de 23
# f45 6
  

re.subn ()

re.subn () аналогичен re.sub () , за исключением того, что он возвращает кортеж из 2 элементов, содержащий новую строку и количество сделанных замен.


Пример 4: re.subn ()

 
# Программа для удаления всех пробелов
импорт ре

# многострочная строка
строка = 'abc 12 \
de 23 \ n f45 6 '

# соответствует всем пробельным символам
шаблон = '\ s +'

# пустой строки
заменить = ''

new_string = re.subn (шаблон, заменить, строка)
печать (новая_строка)

# Вывод: ('abc12de23f456', 4)
  

re.

search ()

Метод re.search () принимает два аргумента: шаблон и строку. Метод ищет первое место, в котором шаблон RegEx дает совпадение со строкой.

Если поиск успешен, re.search () возвращает соответствующий объект; в противном случае возвращается Нет .

  match = re.search (шаблон, строка)  

Пример 5: re.поиск ()

 
импорт ре

string = "Python - это весело"

# проверяем, стоит ли Python в начале
match = re.search ('\ APython', строка)

если совпадение:
  print ("шаблон найден внутри строки")
еще:
  print («шаблон не найден»)

# Вывод: шаблон найден внутри строки
  

Здесь соответствие содержит объект соответствия.


Сопоставить объект

Вы можете получить методы и атрибуты объекта соответствия с помощью функции dir ().

Некоторые из наиболее часто используемых методов и атрибутов объектов сопоставления:


совпадение.

группа ()

Метод group () возвращает часть строки, в которой есть совпадение.

Пример 6: Сопоставить объект

 
импорт ре

строка = '39801 356, 2102 1111'

# Трехзначное число, за которым следует пробел, за которым следует двузначное число
шаблон = '(\ d {3}) (\ d {2})'

# Переменная соответствия содержит объект Match.
match = re.search (шаблон, строка)

если совпадение:
  печать (match.group ())
еще:
  print («шаблон не найден»)

# Вывод: 801 35
  

Здесь совпадение Переменная содержит объект совпадения.

Наш шаблон (\ d {3}) (\ d {2}) имеет две подгруппы (\ d {3}) и (\ d {2}) . Вы можете получить часть строки этих подгрупп в скобках. Вот как:

  >>> match.group (1)
'801'

>>> match.group (2)
'35'
>>> match.group (1, 2)
('801', '35')

>>> match.groups ()
('801', '35')
  

match.start (), match.end () и match.

span ()

Функция start () возвращает индекс начала сопоставленной подстроки.Точно так же end () возвращает конечный индекс совпавшей подстроки.

  >>> match.start ()
2
>>> match.end ()
8  

Функция span () возвращает кортеж, содержащий начальный и конечный индексы совпадающей части.

  >>> match.span ()
(2, 8)  

match.re и match.string

Атрибут re сопоставленного объекта возвращает объект регулярного выражения. Аналогично, строка атрибут возвращает переданную строку.

  >>> match.re
re.compile ('(\\ d {3}) (\\ d {2})')

>>> match.string
'39801 356, 2102 1111'
  

Мы рассмотрели все часто используемые методы, определенные в модуле re . Если вы хотите узнать больше, посетите модуль Python 3 re.


Использование префикса r перед RegEx

Если перед регулярным выражением используется префикс r или R , это означает необработанную строку. Например, '\ n' — это новая строка, тогда как r '\ n' означает два символа: обратная косая черта \ , за которой следует n .

Люфт \ используется для экранирования различных символов, включая все метасимволы. Однако использование префикса r делает \ нормальным символом.


Пример 7: Необработанная строка с префиксом r

 
импорт ре

string = '\ n и \ r - escape-последовательности.'

result = re.findall (r '[\ n \ r]', строка)
печать (результат)

# Вывод: ['\ n', '\ r']
  

Python 3 Примечания: регулярные выражения

На этой странице: re module, re.findall (), re.sub (), re.compile (), re.search (), re.search (). group ().

Новый модуль

Итак, вы узнали все о регулярных выражениях и готовы использовать их в Python. Давайте приступим к делу! Модуль re — это стандартная библиотека Python, которая обрабатывает все регулярные выражения. Как и любой другой модуль, вы начинаете с его импорта.

Поиск всех совпадений в строке

Предположим, вы хотите найти все слова, начинающиеся с «wo» в этом очень коротком тексте ниже. Вы хотите использовать re.findall () метод. Он принимает два аргумента: (1) шаблон регулярного выражения и (2) целевая строка для поиска совпадений.
>>> wood = 'Сколько дерева проглотил бы сурок, если бы сурок мог
Чак Вуд?
>>> re.findall (r'wo \ w + ', дерево)
['дерево', 'будет', 'сурок', 'сурок', 'дерево']
>>>
 
Прежде всего, обратите внимание, что регулярное выражение r’wo \ w + ‘записывается как необработанная строка , на что указывает r’… ‘строковый префикс. Это потому, что в регулярных выражениях, как вы уже знаете, используется обратная косая черта «\» как собственный специальный escape-символ, а без «r» обратная косая черта интерпретируется как специальный escape-символ * Python *. По сути, на уровне строкового объекта Python этот «\» в «\ w» следует интерпретировать как буквальный символ обратной косой черты, чтобы позже его можно было интерпретировать как специальный escape-символ регулярного выражения, когда строка обрабатывается модулем re. Если все это звучит слишком сложно, просто не забывайте ВСЕГДА ИСПОЛЬЗОВАТЬ СВОЕ ОБЫЧНОЕ ВЫРАЖЕНИЕ с ‘r’.

Вернуться к re.findall (). Он возвращает все совпавшие части строки в виде списка. Если совпадений нет, он просто вернет пустой список:

>>> re.findall (r'o + ', дерево)
['o', 'oo', 'o', 'oo', 'oo', 'o', 'oo']
>>> re.findall (r'e + ', дерево)
[]
 
Что, если вы хотите игнорировать регистр в своих матчах? Вы можете указать его как третий необязательный аргумент: re.IGNORECASE.
>>> foo = 'То, то и то'
>>> re.findall (r'th \ w + ', foo)
['что те']
>>> re.findall (r'th \ w + ', foo, re.IGNORECASE)
["Это", "то", "те"]
>>>
 

Подстановка всех совпадений в строку

Что, если вы хотите заменить все совпадающие части чем-то другим? Это можно сделать с помощью метода re.sub (). Ниже мы находим все последовательности гласных и заменяем их на ‘-‘.Метод возвращает результат в виде новой строки.
>>> дерево
«Сколько дерева выколотил бы сурок, если бы сурок мог забить дерево?»
>>> re.sub (r '[aeiou] +', '-', дерево)
'H-w m-ch w-d w-ld - w-dch-ck ch-ck -f - w-dch-ck c-ld ch-ck w-d?'
>>>
 
Удаление совпадающих частей также может быть достигнуто с помощью re.sub (): просто сделайте строку «replacer» пустой строкой ».
>>> re.sub (r '[aeiou] +', '', дерево)
'Hw mch wd wld wdchck chck f wdchck cld chck wd?'
>>>
 

Компиляция объекта регулярного выражения

Если вам нужно сопоставить регулярное выражение с множеством разных строк, рекомендуется создать регулярное выражение как объект python. Таким образом, конечный автомат для регулярного выражения компилируется один раз и используется повторно.Поскольку построение FSA требует больших вычислительных ресурсов, это снижает нагрузку на обработку. Для этого воспользуйтесь методом re.compile ():
>>> myre = re.compile (r '\ w + ou \ w +')
>>> myre.findall (дерево)
['бы мог']
>>> myre.findall ('Бесцветные зеленые идеи яростно спят')
['яростно']
>>> myre.findall ('Тридцать три вора думали, что
трон в течение четверга.')
['мысль', 'повсюду']
 
После компиляции вы вызываете метод re непосредственно для объекта регулярного выражения. В приведенном выше примере myre — это скомпилированный объект регулярного выражения, соответствующий r ‘\ w + ou \ w +’, и вы вызываете .findall () для него как myre.findall (). При этом теперь вам нужно указать на один аргумент меньше: целевая строка myre.findall (wood) — единственное, что нужно.

Проверка наличия совпадения

Иногда нас интересует только подтверждение того, есть ли совпадение в данной строке.Для этого re.findall () является излишним, потому что он сканирует всю строку для получения * каждой * подходящей подстроки. Это нормально, когда вы имеете дело с несколькими короткими строками, как мы здесь, но в реальном мире ваши строки могут быть намного длиннее и / или вы будете выполнять сопоставление тысячи или даже миллионы раз, поэтому разница складывается.

В этом контексте хорошей альтернативой является re.search (). Этот метод находит только первое совпадение и затем завершает работу. Если совпадение найдено, возвращается «объект совпадения».Но если нет, возвращает … ничего . Ниже r’e + ‘успешно сопоставляется в строке «Бесцветный …», поэтому возвращается соответствующий объект. Как ни странно, в нашем дереве нет ни одной буквы «е», поэтому тот же поиск ничего не дает.

>>> re.search (r'e + ',' Бесцветные зеленые идеи яростно спят ')
<объект _sre.SRE_Match в 0x02D9CB48>
>>> re.search (r'e + ', дерево)
>>>
 
Если вы хотите увидеть фактическую совпадающую часть, вы можете использовать расширение.group (), определенный для объекта соответствия. Однако есть проблема: он отлично работает, когда есть совпадение, и поэтому объект совпадения был возвращен, но когда совпадения нет, нет возвращенного объекта, поэтому …
>>> re.search (r'e + ',' Бесцветные зеленые идеи яростно спят '). group ()
'е'
>>> re.search (r'e + ', дерево) .group ()

 
Следовательно, вы хотите использовать re.search () в контексте оператора if.Ниже строка if … проверяет, есть ли объект, возвращенный методом re.search, и только после этого вы переходите к распечатке согласованной части и соответствующей строки. (ПРИМЕЧАНИЕ: если someobj возвращает True, если someobj не является одним из следующих: «ничего», целое число 0, пустая строка «», пустой список [] и пустой словарь {}.)
>>> f = open ('D: \\ Lab \\ ling1330 \\ bible-kjv.txt')
>>> blines = f.readlines ()
>>> е.близко()
>>> smite = re.compile (r'sm (i | o) te \ w * ')
>>> для b в блайндах:
        matchobj = smite.search (б)
        если matchobj:
            print (matchobj.group (), '-', b, конец = '')

порази - снова порази все живое, как это сделал я.
поразили - были с ним и поразили Рефаимов в Аштероф-Карнаиме, и
порази - руку Исава: я боюсь его, чтобы он, придя, не поразил меня, и
smote - 36:35 И умер Хушам и Хадад, сын Бедада, поразивший Мадиама в
smitest - Зачем ты поражаешь ближнего своего? 2:14 И сказал он: кто тебя сделал
порази - 3:20 И простру руку мою, и поражу Египет всем моим
порази - вот, Я ударю жезлом, который в руке моей, по воде
поразил - посох и ударил по воде, которая в реке, на глазах
smite - 8: 2 И если ты не отпустишь их, вот, я поражу все твои
бей розгой и бей в прах земной, чтобы сделались вши
смутный - с тобою из старейшин Израиля; и жезл твой, которым ты пронзил
порази - и ударишь в скалу, и потечет из нее вода,
smiteth - 21:12 Тот, кто поразит человека так, что он умрет, непременно будет предан
smiteth - 21:15 И тот, кто поразит отца своего или мать свою, непременно будет
порази - 21:18 И если люди ссорятся вместе, и один бьет другого камнем,
...
 
В этом примере мы хотим вычеркнуть все строки из Библии, в которых есть слово «порази / порази …». Сначала мы загружаем файл Библии как * список строк * с помощью метода .readlines (). И затем, поскольку мы будем выполнять сопоставление много раз, мы делаем умную вещь, компилируя наше регулярное выражение. Затем, проходя цикл по строкам Библии, мы создаем объект соответствия с помощью .search () и распечатываем совпадающую часть и строку, только если объект соответствия существует.

Регулярное выражение в Python с примерами | Набор 1

Module Regular Expressions (RE) определяет набор строк (шаблон), который ему соответствует.
Чтобы понять аналогию RE, MetaCharacters полезны, важны и будут использоваться в функциях модуля re.
Всего имеется 14 метасимволов, которые мы обсудим по мере перехода к функциям:

 \ Используется для удаления специального значения символа
    после него (обсуждается ниже)
[] Представляют класс персонажей
^ Соответствует началу
$ Соответствует концу
.Соответствует любому символу кроме новой строки
? Соответствует нулю или одному вхождению.
| Означает ИЛИ (соответствует любому из символов
    разделенные им.
* Любое количество вхождений (включая 0 вхождений)
+ Одно или несколько вхождений
{} Укажите количество вхождений предыдущего RE
    чтобы соответствовать.
() Вложить группу RE



 
  • Функция compile ()
    Регулярные выражения компилируются в объекты шаблонов, которые имеют методы для различных операций, таких как поиск совпадений с шаблоном или выполнение подстановок строк.

Python

импорт re

р = р. собрать ( '[a-e]' )

print (p.findall ( "Да, сказал г-н Гибенсон Старк" ))

Выход:

 ['e', 'a', 'd', 'b', 'e', ​​'a']



 

Вывод:
Первое вхождение — это «e» в «Да», а не «A», поскольку оно чувствительно к регистру.
Next Occurrence — это «a» в слове «сказал», затем «d» в слове «сказал», за которым следуют «b» и «e» в слове «Гибенсон». Последний «a» соответствует слову «Старк».
Метасимвол обратная косая черта «\» играет очень важную роль, поскольку сигнализирует о различных последовательностях. Если обратная косая черта должна использоваться без специального значения в качестве метасимвола, используйте ’\\’

 \ d Соответствует любой десятичной цифре, это эквивалентно
     в заданный класс [0-9].
\ D Соответствует любому нецифровому символу.
\ s Соответствует любому пробельному символу.
\ S Соответствует любому непробельному символу
\ w Соответствует любому буквенно-цифровому символу, это
     эквивалент класса [a-zA-Z0-9_].\ W Соответствует любому не буквенно-цифровому символу.


 

Установить класс [\ s ,.] будет соответствовать любому символу пробела, ‘,’ или, ’.’.

Python

импорт re

р = р. составить ( '\ d' )

print (p.findall ( "Я пошел к нему в 11 А.М. 4 июля 1886 г. " ))

р = р. собрать ( '\ d +' )

print (p.findall ( «Я пошел к нему в 11 часов утра 4 июля 1886 года» ))

Вывод:


 ['1', '1', '4', '1', '8', '8', '6']
['11', '4', '1886']



 

Python

импорт re

р = р. составить ( '\ w' )

print (p.findall ( "Он сказал * на каком-то языке." ))

р = р. собрать ( '\ w +' )

print (p.findall ( «Я пошел к нему в 11 утра, он сказал *** на каком-то языке». ))

р = р. собрать ( '\ W' )

print (p.findall ( "он сказал *** на некотором_языке." ))

Выход:

 ['H', 'e', ​​'s', 'a', 'i', 'd', 'i', 'n', 's', 'o', 'm', 'e', '_', 'l', 'a', 'n', 'g']
['Я', 'пошел', 'к', 'ему', 'в', '11', 'А', 'М', 'он', 'сказал', 'в', 'some_language']
['', '', '*', '*', '*', '', '', '.']


 

Python

импорт re

р = р. собрать ( 'ab *' )

печать (p.findall ( "ababbaabbb" ))

Выход:

 ['ab', 'abb', 'a', 'abbb']


 

Понимание вывода:
Нашим RE является ab *, где «a» сопровождается любым номером.of «b’s», начиная с 0.
Выход «ab» действителен, поскольку одиночный «a» сопровождается одиночным «b».
Выход «abb», действителен, так как одиночный «a» сопровождается двумя «b».
Выход «a» действителен, поскольку одиночный «a» сопровождается 0 «b».
Выход «abbb», действителен, так как одиночный «a» сопровождается 3 «b».

  • Функция split ()
    Разделяет строку по вхождениям символа или шаблона, при нахождении этого шаблона оставшиеся символы из строки возвращаются как часть результирующего списка.
    Синтаксис:
 re.split (шаблон, строка, maxsplit = 0, flags = 0)


 

Первый параметр, pattern обозначает регулярное выражение, string — это заданная строка, в которой будет выполняться поиск шаблона и в которой происходит разделение, maxsplit, если он не указан, считается равным нулю ‘0’, и если предоставлено любое ненулевое значение, тогда происходит самое большее количество расщеплений. Если maxsplit = 1, то строка будет разделена только один раз, в результате чего получится список длиной 2.Флаги очень полезны и могут помочь сократить код, они не являются необходимыми параметрами, например: flags = re.IGNORECASE. В этом разбиении регистр будет проигнорирован.

Python

из повторно импорт сплит

печать (разделить ( '\ W +' , 'слов, слов, слов ))

печать (разделить ( '\ W +' , «Word's words Words» ))

печать (разделение ( '\ W +' , '12 января 2016 г., 11:02' ))

печать (разделение ( '\ d +' , '12 января 2016 г., 11:02' ))

Выход:

 ["слова", "слова", "слова"]
['Word', 's', 'слова', 'слова']
['On', '12th', 'Jan', '2016', 'at', '11', '02', 'AM']
['On', 'th Jan', ', at', ':', 'AM']


 

Python

импорт re

печать (re.split ( '\ d +' , '12 января 2016 г., в 11:02' , 1 ))

print (re.split ( '[a-f] +' , 'Aey, Boy oh boy, come here' , flags = re.IGNORECASE))

print (re.split ( '[a-f] +' , 'Эй, мальчик ой, иди сюда' ))

Выход:

 ['On', января 2016 г., 11:02]
['', 'y,', 'oy oh', 'oy,', 'om', 'h', 'r', '']
['A', 'y, Boy oh', 'oy,', 'om', 'h', 'r', '']


 
 re.sub (шаблон, repl, строка, count = 0, flags = 0)


 

‘sub’ в функции означает SubString, определенный шаблон регулярного выражения ищется в данной строке (3-й параметр), и при нахождении шаблона подстроки заменяется на repl (2-й параметр), count проверяет и поддерживает количество раз это происходит.

Python

импорт re

печать (re.sub ( 'ub' , '~ *' , 'Субъект уже забронировал Uber' , flags = re.IGNORECASE))

print (re.sub ( 'ub' , '~ *' , 'Субъект уже забронировал Uber' ))

print (re.sub ( 'ub' , '~ *' , 'Субъект уже забронировал Uber' , count = , = рэ.IGNORECASE))

print (re.sub (r '\ sAND \ s' , '&' , 'Baked Beans And Spam' , flags = EC re.IGNOR ))

Выход


 S ~ * объект уже забронирован
S ~ * ject уже забронировал номер через Uber
S ~ * ject уже забронировал номер через Uber
Запеченная фасоль и спам



 
 re.subn (шаблон, repl, строка, count = 0, flags = 0)


 

subn () похож на sub () во всех отношениях, за исключением способа предоставления вывода. Он возвращает кортеж с общим количеством замен и новую строку, а не только строку.

Python

импорт re

print (re.subn ( 'ub' , '~ *' , 'Субъект уже забронировал Uber' ))

т = рэ.subn ( 'ub' , '~ *' , 'Субъект уже забронировал Uber' , flags = re.IGNORECASE)

печать (т)

печать ( лен (t))

печать (т [ 0 ])

Выход

 ('S ~ * ject уже забронировал Uber', 1)
('S ~ * объект уже ~ * забронирован', 2)
Длина кортежа: 2
С ~ * объект уже забронирован


 
  • Функция escape ()
    Синтаксис:
 re.escape (строка)


 

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

Python

9376 re.MULTILINE или re.M

импорт re

print (re.escape ( «Это ужасно даже в 1 час ночи» ))

печать (re.Ух ты

Статья по теме:
https://www.geeksforgeeks.org/regular-expressions-python-set-1-search-match-find/
Ссылка:
https://docs.python.org/2 /library/re.html

Автор статьи: Piyush Doorwar . Если вам нравится GeeksforGeeks, и вы хотели бы внести свой вклад, вы также можете написать статью на сайте deposit.geeksforgeeks.org или отправить свою статью по почте @ geeksforgeeks.орг. Посмотрите свою статью на главной странице GeeksforGeeks и помогите другим гикам.
Пожалуйста, напишите комментарий, если вы обнаружите что-то неправильное, или если вы хотите поделиться дополнительной информацией по теме, обсужденной выше.

Внимание компьютерщик! Укрепите свои основы с помощью курса Python Programming Foundation и изучите основы.

Для начала подготовьтесь к собеседованию. Расширьте свои концепции структур данных с помощью курса Python DS .

Шпаргалка по регулярным выражениям Python и примеры

Визуализация выше представляет собой снимок экрана, созданный с использованием debuggex для шаблона r '\ bpar (en | ro)? T \ b'

Из docs.python: re:

Регулярное выражение (или RE) определяет набор строк, который ему соответствует; функции в этом модуле позволяют проверить, соответствует ли конкретная строка заданному регулярному выражению

В этом сообщении блога дается обзор и примеры синтаксиса регулярных выражений, реализуемого встроенным модулем re (Python 3.

ограничивает соответствие до начала строки
$ ограничивает соответствие до конца строки
\ n символ новой строки используется как разделитель строк
флаг для обработки ввода как многострочной строки
\ b ограничивает совпадение началом / концом слов
словесные символы: алфавиты, цифры , подчеркивание
\ B соответствует везде, где \ b не соответствует

^ , $ и \ являются метасимволами в приведенной выше таблице, так как эти символы имеют специальные символы смысл. символов вместо привязки.

Элемент Описание
| несколько RE объединены как условное OR
каждая альтернатива может иметь независимые якоря
(RE) групповой шаблон (ы), также группа захвата
a (b | c) d совпадает с abd | acd
(?: RE) группа без захвата
(? P pat) именованная группа захвата
. Соответствует любому символу, кроме символа новой строки \ n
[] Класс символов, соответствует одному символу из многих
Описание
Жадные квантификаторы
* Нулевое или более совпадение
+ Одно или несколько совпадений
? Нулевое или однократное совпадение
{m, n} Соответствие м n раз (включительно)
{m,} Соответствие не менее m раз
{, n} Соответствие до n раз (включая 0 раз)
{n} Совпадение ровно n раз
pat.* pat2 любое количество символов от pat1 до pat2
pat1. * pat2 | pat2. * pat1 соответствует pat1 и pat2

81 в любом порядке 8

Жадный здесь означает, что приведенные выше квантификаторы будут соответствовать в максимально возможной степени, что также будет учитывать общий RE. Добавляя ? для жадных квантификаторов делает их нежадными , то есть соответствует как минимум , насколько это возможно.\ s]

Обходы Описание
Обходы настраиваемые утверждения, нулевые значения ширины как анкеры

89 (?
(? утверждение отрицательного просмотра назад
(? = Pat) утверждение положительного просмотра вперед
(? <= Pat) положительное утверждение
(?! Pat1) (? = Pat2) несколько утверждений можно указать в любом порядке
, поскольку они отмечают совпадающее местоположение без использования символов
((?! Pat) .) * Отвергнуть группировку, аналогично классу инвертированных символов
Флаги Описание
re.IGNORECASE ore или re.I flag

89
re.DOTALL или re.S разрешить . метасимвола для соответствия символу новой строки
flags = re.S | re.I несколько флагов могут быть объединены с помощью | оператор
р. и Якоря долларов США для соответствия по строкам
re.VERBOSE или re.X позволяет использовать буквальные пробелы для выравнивания
и добавлять комментарии после символа #
escape-пробелы и # , если это необходимо как часть фактического RE
re.ASCII или re.A соответствуют только символам ASCII для \ b , \ w , \ d , \ s
и их противоположности, применимые только для шаблонов Unicode
re.LOCALE или re.L использовать настройки локали для байтовых шаблонов и 8-битных локалей
(? #Comment) другой способ добавления комментариев, а не флаг
(? flags: pat) встроенных флагов только для этого pat , переопределяет flags аргумент
flags is i для re.I , s для re.S и т. д. кроме L для re.L
(? -Flags: pat) инвертировать флаги только для этого pat
(? Flags-flags: pat) применять и отрицать определенные флаги только для этого pat
(? flags) применить флаги для всего RE, может использоваться только в начале RE
якоря, если есть, следует указывать после (? flags)
Подходящая часть Описание
re.Сопоставить объект детали, такие как сопоставленные части, местоположение и т. Д.
м [0] или м. Группа (0) вся сопоставленная часть re.Match объект м
м [n] или м. Группа (n) согласованная часть n -я группа захвата
м. Группы () кортеж из согласованных частей всех групп захвата
г.span () начало и конец + 1 индекс всей согласованной части
передайте число для получения диапазона этой конкретной группы захвата
также можно использовать м. start () и м .end ()
\ N обратная ссылка, дает согласованную часть N -й группы захвата
применяется как к разделу поиска, так и к разделу замены
\ 1 возможные значения: , \ 2 до \ 99 при условии отсутствия цифр
\ g обратная ссылка, дает согласованную часть N-й группы захвата
возможные значения: \ g < 0> , \ g <1> и т. Д. (Не ограничивается 99)
\ g <0> относится ко всей согласованной части
(? P pat) названная группа захвата
см. «имя» в re.Соответствует объекту
ссылается на (? P = name) в разделе поиска
ссылается на \ g в разделе замены
groupdict применен метод a re.Match объект
дает именованные части группы захвата как dict

\ 0 и \ 100 и далее считаются восьмеричными значениями, поэтому не могут использоваться в качестве обратных ссылок.

re функции модуля🔗

Функция Описание
re.search Проверить, присутствует ли данный шаблон где-либо во входной строке
Результатом является условный объект re.Match , используемый в выражениях
r-строки, предпочтительные для определения RE
Использовать байтовый шаблон для байтового ввода
Python также поддерживает небольшой кеш последнего RE
re.fullmatch гарантирует, что шаблон соответствует всей входной строке
re.compile Скомпилирует шаблон для повторного использования, выдает re.Pattern object
re.sub search and replace search
re.sub (r'pat ', f, s) функция f с объект re.Match в качестве аргумента
re.escape автоматически экранировать все метасимволы
рэ.split разделить строку на основе RE
текст, соответствующий группам, будет частью вывода
часть, соответствующая шаблону вне группы, не будет в выводе
re. findall возвращает все совпадения в виде списка
если используется 1 группа захвата, возвращаются только ее совпадения
1+, каждый элемент будет кортежем групп захвата
часть соответствует шаблону вне группы не будет на выходе
re.finditer итератор с объектом re.Match для каждого совпадения
re.subn дает кортеж измененной строки и количество замен

Определения функций приведены ниже:

  re.search (шаблон, строка, флаги = 0)
re.fullmatch (шаблон, строка, флаги = 0)
re.compile (шаблон, флаги = 0)
re.sub (шаблон, repl, строка, count = 0, flags = 0)
re.escape (шаблон)
re.split (шаблон, строка, maxsplit = 0, flags = 0)
ре.findall (шаблон, строка, флаги = 0)
re.finditer (шаблон, строка, флаги = 0)
re.subn (шаблон, repl, строка, count = 0, flags = 0)
  

Примеры регулярных выражений🔗

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

  >>> предложение = 'Это образец строки'

# перед использованием необходимо загрузить модуль re
>>> импорт ре
# проверяем, содержит ли 'предложение' шаблон, описанный аргументом RE
>>> bool (re.поиск (r'is ', предложение))
Правда
# игнорировать регистр при поиске совпадения
>>> bool (re.search (r'this ', предложение, flags = re.I))
Правда
>>> bool (re.search (r'xyz ', предложение))
Ложь

# вывод re.search можно напрямую использовать в условных выражениях
>>> если re.search (r'ring ', предложение):
... print ('успех миссии')
...
Миссия пройдена успешно

# использовать необработанные байтовые строки, если входные данные имеют байтовый тип
>>> bool (re.search (rb'is ', b'This is a sample string'))
Правда
  
  • Разница между анкерами для струн и строп
  # якоря струны
>>> bool (re.par $ ',' запасной \ npar \ ndare ', flags = re.M))
Правда
  
  # целое слово par с необязательным s в начале и необязательным e в конце
>>> re.findall (r '\ bs? pare? \ b', 'par spar очевидная запасная часть pare')
['par', 'spar', 'spare', 'pare']

# числа> = 100 с необязательными ведущими нулями
>>> re.findall (r '\ b0 * [1-9] \ d {2,} \ b', '0501 035 154 12 26 98234')
['0501', '154', '98234']

# если используется несколько групп захвата, каждый элемент вывода
# будет кортежем строк всех групп захвата
>>> ре./,] +),? ',' 2020 / 04,1986 / Март ')
[('2020', '04'), ('1986', 'март')]

# обычная группа захвата помешает получить весь матч
# группа без захвата спешит на помощь
>>> re.findall (r '\ b \ w * (?: st | in) \ b', 'дороже на восток')
['стоимость', 'родственный', 'восток']

# полезно также для отладки
>>> re.findall (r't. *? a ',' это выдумка ')
['tha', 't is a', 'ted ta']
  
  # разделить на основе одной или нескольких цифр
>>> re.split (r '\ d +', 'Sample123string42with777numbers')
['Образец', 'строка', 'с', 'числами']

# разделение на основе цифр или пробелов
>>> ре.разделить (r '[\ d \ s] +', '** 1 \ f2 \ n3star \ t7 77 \ r **')
['**', 'звезда', '**']

# чтобы включить соответствующие строки разделителей в вывод
>>> re.split (r '(\ d +)', 'Sample123string42with777numbers')
["Образец", "123", "строка", "42", "с", "777", "числа"]

# использовать группу без захвата, если захват не требуется
>>> re.split (r'hand (?: y | ful) ',' 123handed42handy777handful500 ')
['123handed42', '777', '500']
  
  • обратная ссылка в шаблоне поиска
  # целые слова, содержащие хотя бы один последовательный повторяющийся символ
>>> words = ['усилия', 'бегство', 'фасад', 'чудак', 'крыса', 'инструмент']

>>> [w вместо w прописью, если re.поиск (r '\ b \ w * (\ w) \ 1 \ w * \ b', w)]
["усилие", "бегство", "чудак", "инструмент"]
  
  • работа с согласованными порциями
  >>> re.search (r'b. * D ',' abc ac adc abbbc ')
<объект re.Match; span = (1, 9), match = 'bc ac ad'>
# получение всей совпавшей части, обратите внимание на использование [0]
>>> re.search (r'b. * d ',' abc ac adc abbbc ') [0]
'bc ac ad'

# пример группы захвата
>>> m = re.search (r'a (. *) d (. * a) ',' abc ac adc abbbc ')
# чтобы получить совпавшую часть второй группы захвата
>>> м [2]
'c a'
# чтобы получить кортеж всех групп захвата
>>> м.группы ()
('bc ac a', 'c a')
  
  # числа <350
>>> m_iter = re.finditer (r '[0-9] +', '45 349 651 593 4 204 ')
>>> [m [0] вместо m в m_iter, если int (m [0]) <350]
['45', '349', '4', '204']

# начало и конец + 1 индекс каждой совпадающей части
>>> m_iter = re.finditer (r'ab + c ',' abc ac adc abbbc ')
>>> для m в m_iter:
... печать (m.span ())
...
(0, 3)
(11, 16)
  
  >>> ip_lines = "катапульты \ nconcatenate \ ncat"
>>> print (re.:] +:) {2} ', r' ',' foo: 123: bar: baz ', count = 1)
'бар: баз'
  
  • обратная ссылка в разделе замены
  # удалить последовательные повторяющиеся слова, разделенные пробелом
>>> re.sub (r '\ b (\ w +) (\ 1) + \ b', r '\ 1', 'aa a a a 42 f_1 f_1 f_13.14')
'aa a 42 f_1 f_13.14'

# добавляем что-нибудь вокруг совпадающих строк
>>> re.sub (r '\ d +', r '(\ g <0> 0)', '52 яблока и 31 манго ')
'(520) яблок и (310) манго'

# меняем местами слова, разделенные запятой
>>> ре.sub (r '(\ w +), (\ w +)', r '\ 2, \ 1', 'хорошо, плохо 42,24')
'плохо, хорошо 24,42'
  
  • с использованием функций в разделе замены re.sub
  >>> from math import factorial
>>> числа = '1 2 3 4 5'
>>> def fact_num (n):
... return str (factorial (int (n [0])))
...
>>> re.sub (r '\ d +', fact_num, числа)
'1 2 6 24 120'

# используя лямбда
>>> re.sub (r '\ d +', лямбда m: str (factorial (int (m [0]))), числа)
'1 2 6 24 120'
  
  # изменяйте 'foo', только если за ним не следует цифра
# обратите внимание, что конец строки удовлетворяет заданному утверждению
# foofoo имеет 2 совпадения, поскольку утверждение не использует символы
>>> ре.sub (r'foo (?! \ d) ', r'baz', 'эй, еда! foo42 foot5 foofoo')
Эй, базд! foo42 bazt5 bazbaz '

# изменить слово целиком, только если ему не предшествует: или -
>>> re.sub (r '(? >> re.findall (r '(? <= -) \ d + (? = [:;])', 'fo-5, ba3; x-83, y-20: f12')
['20']

# слова, содержащие 'b', 'e' и 't' в любом порядке
>>> words = ['секвойя', 'сомнительный', 'экспонат', 'уравнение']
>>> [w вместо w прописью, если re.search (r '(? =. * b) (? =. * e). * t', w)]
['сомнительно', 'выставлять']

# совпадение, если между at и par нет 'do'
>>> bool (re.search (r'at ((?! do).) * par ',' лиса, кошка, собака, попугай '))
Ложь
# совпадение, если между at и par нет 'go'
>>> bool (re.search (r'at ((?! go).) * par ',' лиса, кошка, собака, попугай '))
Правда
  

Регулярные выражения могут быть скомпилированы с использованием функции re.compile , которая возвращает объект re.Pattern . Все функции модуля верхнего уровня и доступны как методы для этого объекта.Компиляция регулярного выражения помогает, если RE необходимо использовать в нескольких местах или вызывать несколько раз внутри цикла (преимущество в скорости). По умолчанию Python поддерживает небольшой список недавно использованных RE, поэтому выигрыш в скорости не распространяется на тривиальные варианты использования.

  >>> pet = re.compile (r'dog ')
>>> тип (питомец)
<класс 're.Pattern'>
>>> bool (pet.search ('Они купили собаку'))
Правда
>>> bool (pet.search ('Кошка перешла им дорогу'))
Ложь

>>> pat = re.)] * \) ')
>>> pat.sub ('', 'a + b (сложение) - foo () + c% d (#modulo)')
'a + b - foo + c% d'
>>> pat.sub ('', 'Привет (привет). Хороший день (a (b)')
'Всем привет. Хороший день'
  

Python re (gex)? книга🔗

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

Воспользуйтесь этой ссылкой на Leanpub по сниженной цене.

Python Regex Sub | Finxter

Вы хотите заменить все вхождения шаблона в строку? Вы попали в нужное место!

Функция регулярного выражения re.sub (P, R, S) заменяет все вхождения шаблона P заменой R в строке S .Возвращает новую строку. Например, если вы вызовете re.sub ('a', 'b', 'aabb') , результатом будет новая строка 'bbbb' со всеми символами 'a' , замененными на 'b .

Вы также можете посмотреть мое обучающее видео, читая эту статью:

Статья по теме: Python Regex Superpower - полное руководство

Вы хотите овладеть суперсилой регулярных выражений? Посмотрите мою новую книгу Самый разумный способ выучить регулярные выражения на Python с инновационным трехэтапным подходом для активного обучения: (1) изучить главу книги, (2) решить загадку кода и (3) ) посмотрите обучающее видео с главой.

Ответим на следующий вопрос:

Как работает re.sub () в Python?

Метод re.sub (шаблон, repl, string, count = 0, flags = 0) возвращает новую строку, в которой все вхождения шаблона в старой строке заменяются на repl .

Вот минимальный пример:

 >>> import re
>>> text = 'C ++ - лучший язык. C ++ потрясающий! '
>>> re.sub ('C \ + \ +', 'Python', текст)
«Python - лучший язык.Питон крут!
>>> 

Текст содержит два вхождения строки «C ++» . Вы используете метод re.sub () для поиска всех этих вхождений. Ваша цель - заменить все эти строки новой строкой «Python» (в конце концов, Python - лучший язык).

Обратите внимание, что вы должны экранировать символ '+' в 'C ++' , иначе это будет означать как минимум одно регулярное выражение .

Вы также можете видеть, что метод sub () заменяет все совпадающие шаблоны в строке, а не только первый.

Но это еще не все! Давайте посмотрим на формальное определение метода sub () .

Параметры

 re.sub (шаблон, repl, строка, count = 0, flags = 0) 

У метода четыре аргумента, два из которых необязательны.

  • шаблон : шаблон регулярного выражения для поиска строк, которые нужно заменить.
  • repl : замещающая строка или функция.Если это функция, она должна принимать один аргумент (объект соответствия), который передается для каждого вхождения шаблона. Возвращаемое значение функции замены - это строка, которая заменяет совпадающую подстроку.
  • строка : текст, который нужно заменить.
  • count (необязательный аргумент): максимальное количество замен, которые вы хотите выполнить. По умолчанию вы используете count = 0 , что читается как , заменяя все вхождения шаблона .
  • flags (необязательный аргумент): более продвинутый модификатор, позволяющий настраивать поведение метода. По умолчанию вы не используете никаких флагов. Хотите знать, как использовать эти флаги? Ознакомьтесь с этой подробной статьей в блоге Finxter.

Требуются три начальных аргумента. Остальные два аргумента необязательны.

Подробнее об этих аргументах вы узнаете позже.

Возвращаемое значение:

Новая строка, в которой count вхождения первых подстрок, соответствующих шаблону , заменяются строковым значением, определенным в аргументе repl .

Минимальный пример регулярного выражения

Давайте изучим еще несколько примеров - от простых до более сложных.

Самый простой вариант использования - всего три аргумента: шаблон 'sing ', замещающая строка 'program' и строка, которую нужно изменить ( text в нашем примере).

 >>> import re
>>> text = 'Учитесь петь, потому что петь - это весело.'
>>> re.sub ('петь', 'программа', текст)
«Учитесь программировать, потому что программировать - это весело.'

Просто проигнорируйте пока грамматическую ошибку. Вы понимаете: мы не поем, мы программируем.

Но что, если вы действительно хотите исправить эту грамматическую ошибку? В конце концов, программирование , а не программирование . В этом случае нам нужно заменить 'sing' на 'program' в некоторых случаях и 'sing' на 'programm' в других случаях.

Вы видите, к чему это нас ведет: аргумент sub должен быть функцией! Итак, давайте попробуем это:

 импорт ре


def sub (соответствует):
    если совпадают.group (0) == 'пение':
        вернуть 'программирование'
    еще:
        вернуть 'программу'


text = 'Учитесь петь, потому что петь весело.'
print (re.sub ('петь (петь)?', sub, text))
# Научитесь программировать, потому что программирование - это весело. 

В этом примере вы сначала определяете функцию замещения sub . Функция принимает согласованный объект в качестве входных данных и возвращает строку. Если он соответствует более длинной форме «пение» , он возвращает «программирование» . В противном случае он соответствует более короткой форме 'sing' , поэтому вместо этого возвращает более короткую заменяющую строку 'program' .

Как использовать аргумент счетчика вспомогательного метода регулярного выражения?

Что делать, если вы не хотите заменять все вхождения шаблона, а только ограниченное их количество? Просто используйте аргумент count ! Вот пример:

 >>> import re
>>> s = 'xxxxxxhelloxxxxxworld! xxxx'
>>> re.sub ('x +', '', s, count = 2)
'helloworld! xxxx'
>>> re.sub ('x +', '', s, count = 3)
'Привет мир!' 

В первой операции замены вы заменяете только два экземпляра шаблона 'x +' .Во втором вы заменяете все три.

Вы также можете использовать позиционные аргументы для сохранения некоторых символов:

 >>> re.sub ('х +', '', s, 3)
'Привет мир!' 

Но поскольку многие программисты не знают об аргументе count , вам, вероятно, следует использовать аргумент ключевого слова для удобства чтения.

Как использовать аргумент необязательного флага?

Как вы видели в спецификации, метод re.sub () имеет необязательный четвертый флаг аргумент:

 re.sub (шаблон, повтор, строка, счетчик = 0, флаги = 0) 

Какова цель аргумента flags ?

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

Синтаксис Значение
re.ASCII Если вы не используете этот флаг, специальные символы регулярных выражений Python w, W, b, B, d, D, s и S будут соответствовать символам Unicode. Если вы используете этот флаг, эти специальные символы будут соответствовать только символам ASCII - как следует из названия.
re.A То же, что и re.ASCII
re.DEBUG Если вы используете этот флаг, Python напечатает некоторую полезную информацию в оболочке, которая поможет вам отладить ваше регулярное выражение.
re.IGNORECASE Если вы используете этот флаг, механизм регулярных выражений будет выполнять сопоставление без учета регистра. Таким образом, если вы ищете [A-Z], он также будет соответствовать [a-z].
re.I То же, что и re.IGNORECASE
re.LOCALE Никогда не используйте этот флаг. Он обесценился - идея заключалась в том, чтобы выполнять сопоставление без учета регистра в зависимости от вашего текущего языкового стандарта. Но это ненадежно.’Соответствует началу каждой строки (а не только ее началу). То же самое относится и к регулярному выражению конца строки «$», которое теперь соответствует также и в конце каждой строки в многострочной строке.
re.M То же, что и re.MULTILINE
re.DOTALL Без использования этого флага регулярное выражение точки ‘.’ Соответствует всем символам, кроме символа новой строки ‘n’. Включите этот флаг, чтобы действительно соответствовать всем символам, включая символ новой строки.
re.S То же, что и re.DOTALL
re.VERBOSE Чтобы улучшить читаемость сложных регулярных выражений, вы можете разрешить комментарии и (многострочное) форматирование само регулярное выражение. Это возможно с помощью этого флага: все пробельные символы и строки, начинающиеся с символа «#», игнорируются в регулярном выражении.
re.X То же, что и re.VERBOSE

Вот как вы могли бы использовать его в минимальном примере:

 >>> import re
>>> s = 'xxxiiixxXxxxiiixXXX'
>>> ре.sub ('х +', '', s)
'iiiXiiiXXX'
>>> re.sub ('x +', '', s, flags = re.I)
'iiiiii' 

Во второй операции подстановки вы игнорируете регистр с помощью флага re.I , который является сокращением от re.IGNORECASE . Вот почему он заменяет даже 'X' символов в верхнем регистре, которые теперь соответствуют регулярному выражению 'x +' .

В чем разница между Regex Sub и String Replace?

В некотором смысле, метод re.sub () является более мощным вариантом строки .replace () , который подробно описан в этой статье блога Finxter.

Почему? Потому что вы можете заменить все вхождения шаблона регулярного выражения, а не только все вхождения строки в другой строке.

Итак, с re.sub () вы можете делать все, что можете с string.replace () - но кое-что еще!

Вот пример:

 >>> 'Python - это python is PYTHON'. Replace ('python', 'fun')
"Python - это весело, это PYTHON"
>>> ре.sub ('(Python) | (python) | (PYTHON)', 'fun', 'Python - это python - это PYTHON')
"весело, это весело, это весело" 

Метод string.replace () заменяет только строчное слово 'python' , тогда как метод re.sub () заменяет все вхождения вариантов верхнего или нижнего регистра.

Обратите внимание: то же самое можно сделать еще проще с аргументом flags .

 >>> re.sub ('python', 'fun', 'Python - это python is PYTHON', flags = re.Я)
"весело, это весело, это весело" 

Как удалить шаблон регулярного выражения в Python?

Нет ничего проще. Просто используйте пустую строку в качестве строки замены:

 >>> re.sub ('p', '', 'Python - это python is PYTHON', flags = re.I)
'ython is ython is YTHON' 

Вы заменяете все вхождения шаблона 'p' пустой строкой '' . Другими словами, вы удаляете все вхождения 'p' . Поскольку вы используете flags = re.В аргументе вы игнорируете заглавные буквы.

Связанные методы Re

Есть пять важных методов регулярных выражений, которые вы должны освоить:

  • Метод re.findall (шаблон, строка) возвращает список совпадений строк. Подробнее читайте в нашем блоге.
  • Метод re.search (шаблон, строка) возвращает объект соответствия первого совпадения. Подробнее читайте в нашем блоге.
  • re.match (pattern, string) метод возвращает объект соответствия, если регулярное выражение совпадает с началом строки. Подробнее читайте в нашем блоге.
  • Метод re.fullmatch (pattern, string) возвращает объект соответствия, если регулярное выражение соответствует всей строке. Подробнее читайте в нашем блоге.
  • Метод re.compile (pattern) подготавливает шаблон регулярного выражения и возвращает объект регулярного выражения, который вы можете многократно использовать в своем коде.Подробнее читайте в нашем блоге.
  • Метод re.split (шаблон, строка) возвращает список строк, сопоставляя все вхождения шаблона в строке и разделяя строку по ним. Подробнее читайте в нашем блоге.
  • Метод re.sub (pattern, repl, string, count = 0, flags = 0) возвращает новую строку, в которой все вхождения шаблона в старой строке заменяются на repl . Подробнее читайте в нашем блоге.

Эти семь методов - это 80% того, что вам нужно знать, чтобы начать работу с функциями регулярных выражений Python.

Сводка

Вы узнали, что метод re.sub (pattern, repl, string, count = 0, flags = 0) возвращает новую строку, в которой все вхождения шаблона в старой строке заменены на repl .

Куда идти дальше?

Хватит теории, пора практиковаться!

Чтобы добиться успеха в программировании, вам нужно выйти и решать реальные проблемы для реальных людей.Вот так легко можно получить шестизначный заработок. Именно так вы оттачиваете навыки, которые действительно нужны вам на практике. В конце концов, какой толк в теории обучения, которая никому не нужна?

Практические проекты - это как отточить пилу в кодировании!

Вы хотите стать мастером кода, сосредоточившись на практических проектах кода, которые на самом деле приносят вам деньги и решают проблемы для людей?

Тогда станьте внештатным разработчиком Python! Это лучший способ подойти к задаче улучшения ваших навыков Python, даже если вы полный новичок.

Присоединяйтесь к моему бесплатному веб-семинару «Как развить навыки работы с высоким доходом на Python» и посмотрите, как я развивал свой бизнес по программированию в Интернете и как вы тоже можете это сделать - не выходя из собственного дома.

Присоединяйтесь к бесплатному вебинару прямо сейчас!

Работая исследователем распределенных систем, доктор Кристиан Майер обнаружил свою любовь к обучению студентов, изучающих информатику.

Чтобы помочь студентам достичь более высокого уровня успеха в Python, он основал сайт обучения программированию Finxter.com.Он является автором популярной книги по программированию Python One-Liners (NoStarch 2020), соавтором серии самоизданных книг о Python для кофе-брейков, энтузиаст информатики, фрилансер и владелец одного из 10 крупнейших блогов по Python в мире.

Его страстью являются письмо, чтение и кодирование. Но его самая большая страсть - служить начинающим программистам через Finxter и помогать им повышать свои навыки. Вы можете присоединиться к его бесплатной электронной академии здесь.

Скрытая особенность Python Regex, о которой вы, возможно, не знали | автор: Кристофер Тао

Советы по программированию на Python

Расширенное использование re.sub () function

Известно, что Python предоставляет множество удобных встроенных функций для регулярного выражения (regex), которые делают манипуляции со строками чрезвычайно простыми и удобными.

В этой статье я представлю замечательную функцию, встроенную в библиотеку re в Python для замены строковых компонентов с помощью регулярного выражения.

Давайте посмотрим на следующий пример.

Фото papazachariasa на Pixabay

В некоторых программах / веб-разработках, ориентированных на клиента, нам может потребоваться скрыть некоторую конфиденциальную информацию, когда мы отображаем ее в пользовательском интерфейсе.Например, у нас есть такая строка, содержащая номер мобильного телефона пользователя:

 s = "Номер мобильного телефона пользователя 1234567890" 

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

 import res_hide = re.sub ('\ d', '*', s) 
print (s_hide)

Библиотека re - это встроенная библиотека в Python, поэтому мы можем просто импортировать ее и использовать. Здесь мы используем функцию re.sub () , которая принимает 3 аргумента:

  1. Шаблон регулярного выражения, который будет использоваться для сопоставления шаблонов в строке
  2. Строка, которую мы хотим использовать для замены каждого шаблона, найденного в строка
  3. Исходная строка

Однако в большинстве случаев мы можем не захотеть скрывать все.Например, мы можем захотеть показать только последние 3 цифры, чтобы пользователю было подсказано, какой именно номер мобильного телефона мы здесь упомянули. Это очень распространено в случае использования сброса пароля. Мы не хотим отображать все цифры номера мобильного телефона, потому что мы определенно не хотим раскрывать этот номер посторонним лицам, но в то же время мы хотим предоставить несколько цифр в качестве подсказок, если пользователь аутентифицирован. .

Это означает, что мы ожидали следующего:

 Мобильный номер пользователя ******* 890 

Давайте сделаем это еще более сложным.Что, если у нас есть несколько чисел в строке, часть из которых мы хотим скрыть?

 s = "UserID: 12345, Mobile Number: 1234567890" 

Предположим, у нас есть идентификатор пользователя и номер мобильного телефона, и мы хотим показать только последние 2 цифры идентификатора пользователя и последние 3 цифры номера мобильного телефона, как это можно достичь? Используете re.compile () , re.search () или другие функции?

Я уверен, что есть много решений, но я думаю, что использование re.sub () является наиболее интуитивно понятным и имеет лучшую читаемость.Посмотрим, как это реализовать.

Фото MarandaP на Pixabay

В официальной документации Python (см. Справочный раздел) относительно re.sub () мы можем обнаружить, что сигнатура функции выглядит следующим образом:

 re.  sub  ( pattern ,  repl ,  string ,  count = 0 ,  flags = 0 ) 

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

Возвращает строку, полученную заменой крайних левых неперекрывающихся вхождений шаблона в строке заменой на . Если шаблон не найден, строка возвращается без изменений. repl может быть строкой или функцией ;

Давайте посмотрим, что произойдет, если мы передадим функцию в качестве второго параметра.

 def replace (term): 
print ('Matched term:', term)
return '*' s = "UserID: 12345, номер мобильного телефона: 1234567890"
s_hide = re.sub ('\ d +', replace, s)

Круто! Мы определили функцию с именем replace () и передали ее в качестве второго параметра. Затем в нашей функции replace () мы можем иметь термин , которому соответствует шаблон.

Похоже, мы можем многое сделать с этим термином . На той же странице документации Python мы видим, что есть пример, который говорит нам использовать .group (0) для получения совпадающей строки, а возвращенная строка будет использоваться для замены этого согласованного термина .

Давайте проведем тест.

 def replace (term): 
print ('Matched term:', term.group (0))
return term.group (0) s = "UserID: 12345, номер мобильного телефона: 1234567890"
s_hide = re.sub ('\ d +', replace, s)
print ('Строка не должна изменяться:', s_hide)

Работает! мы нашли два совпадающих термина. Кроме того, поскольку мы возвращаем term.group (0) обратно, так что строка фактически не изменяется.

ОК. Теперь ясно, что мы можем преобразовать члены в функции и затем передать их обратно.Тогда мы должны это сделать.

 def replace (term): 
if len (term.group (0)) == 5: # UserID
return '***' + term.group (0) [- 2:]
elif len (term. group (0)) == 10: # Mobile
return '*******' + term.group (0) [- 3:]
else:
return term.group (0) s = "UserID: 12345, мобильный номер: 1234567890, возраст: 32 "
s_hide = re.sub ('\ d +', replace, s)
print (s_hide)

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

Фото Free-Photos на Pixabay

Если вы читали страницу документации, приложенную к ссылке, вы можете заметить, что есть еще одна функция, которая называется subn () . Честно говоря, я никогда не знал этой функции, пока не перепроверил документацию, но я считаю, что в некоторых случаях она очень помогает.

Функция subn () в точности совпадает с функцией sub () . Единственное отличие состоит в том, что он также вернет, сколько терминов было найдено (не обязательно заменять, потому что это наша определенная функция, решающая, как заменить).

Для тестирования повторим строку 10 раз.

 s = "UserID: 12345, номер мобильного телефона: 1234567890, возраст: 32. \ n" * 10 
print (s)

Затем давайте используем re.subn () с функцией replace () , которая мы определили ранее. Здесь нам нужно распаковать результат в две переменные, так как он вернет кортеж.

 s_hide, num_matched = re.subn ('\ d +', replace, s) print ('Строка со скрытым идентификатором и мобильным телефоном: \ n', s_hide) 
print ('Общее количество пользователей:', int (num_matched / 3))

Следовательно, если вы хотите сделать две вещи, указанные ниже, вы можете реализовать их в одной функции прямо сейчас!

  • Подсчитайте количество вхождений шаблона в строке
  • Замените все вхождения шаблона в строке

Благодарность Эрику Паскуалу, который обнаружил ошибку в моем коде и объяснении.Важно уточнить, что метод re.subn () вернет количество совпавших терминов, а не замененных, потому что способ замены терминов определяется в нашей настраиваемой функции, которая не обязательно обнаруживается re.subn () функция.

Фотография Engin_Akyurt на Pixabay

В этой статье я рассказал, как использовать функцию re.