{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Регулярные выражения\n", "\n", "Регулярные выражения (regular expressions, regex) - это формальный язык поиска подстроки в строке, основанный на использовании метасимволов (таких, например, как *, # и $). Для поиска используется особая строка-образец, состоящая из символов и метасимволов и задающая правило поиска.\n", "\n", "Для того, чтобы использовать regex при работе с Python, нужно импортировать соответсвующий модуль:\n" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "import re # импортируем библиотеку для работы с регулярными выражениями" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Рассмотрим несколько наиболее распространённых метасимволов и их использование в регулярных выражениях.\n", "### . (точка)\n", "Обозначает любой символ кроме переноса строки. " ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['Hello', 'Hepno']\n" ] } ], "source": [ "str = \"Hello world! Hepno Hepo\"\n", "x = re.findall(\"He..o\", str)\n", "print(x)" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['Hello']\n" ] } ], "source": [ "str = \"Hello world! Helno Hepo\"\n", "x = re.findall(\"Hel?l?o\", str)\n", "print(x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### * (звёздочка)\n", "Звёздочка после символа означает, что символ может встретиться ноль или более раз. " ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Совпадения: ['ai', 'ai', 'ai', 'ai']\n" ] } ], "source": [ "str = \"The rain in Spain falls mainly in the plain!\"\n", "x = re.findall(\"aix*\", str)\n", "print('Совпадения:',x)" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Совпадения: ['aix', 'ai', 'ai', 'aixxxxx']\n" ] } ], "source": [ "str = \"The raixn in Spain falls mainly in the plaixxxxxn!\"\n", "x = re.findall(\"aix*\", str)\n", "print('Совпадения:',x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Звёздочку часто используют в сочетании с точкой: .* \n", "Такая комбинация обозначает любое количество символов и удобна для того, чтобы разделить две части регулярного выражения. " ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Совпадения: ['rainSpain']\n" ] } ], "source": [ "str = \"The rain in Spain falls mainly in the plain! Spain\"\n", "x = re.findall(\"rain.*Spain\", str)\n", "print('Совпадения:',x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### \\s\n", "Обозначает пробел. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### + (плюс)\n", "Плюс после символа означает, что символ должен встретиться хотя бы один раз." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Совпадения: []\n" ] } ], "source": [ "str = \"The rain in Spain falls mainly in the plain!\"\n", "x = re.findall(\"aix+\", str)\n", "print('Совпадения:',x)" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Совпадения: ['aix', 'aixx']\n" ] } ], "source": [ "str = \"The raixn in Spain falls mainly in the plaixxn!\"\n", "x = re.findall(\"aix+\", str)\n", "print('Совпадения:',x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### {n} (фигурные скобки)\n", "Комбинация вида {n}, идущая после символа, означает, что символ должен встретиться ровно n раз: " ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Совпадения: ['aix', 'aix']\n" ] } ], "source": [ "str = \"The raixn in Spain falls mainly in the plaixxn!\"\n", "x = re.findall(\"aix{1}\", str)\n", "print('Совпадения:',x)" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Совпадения: ['aixx']\n" ] } ], "source": [ "str = \"The raixn in Spain falls mainly in the plaixxn!\"\n", "x = re.findall(\"aix{2}\", str)\n", "print('Совпадения:',x)" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Совпадения: ['aixxx', 'aixx', 'aixxxx']\n" ] } ], "source": [ "str = \"The raixxxn in Spaixxn faixlls mainly in the plaixxxxn!\"\n", "x = re.findall(\"aix{2,5}\", str)\n", "print('Совпадения:',x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### ^ (уголок вверх)\n", "Начало текста. Совпадение будет только в том случае, если строка начинается с того, что идёт за ^:\n" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Строка не начинается с \"Hello\"\n" ] } ], "source": [ "str = \" Hello world\"\n", "x = re.search(\"^Hello\", str)\n", "if (x):\n", " print('Строка начинается с \"Hello\"')\n", "else:\n", " print('Строка не начинается с \"Hello\"')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### \\$ (доллар)\n", "Конец текста. Совпадение будет только в том случае, если строка заканчивается тем, что идёт перед $:" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Строка не заканчивается на \"world\"\n" ] } ], "source": [ "str = \"Hello world\"\n", "x = re.search(\"world$\", str)\n", "if (x):\n", " print('Строка заканчивается на \"world\"')\n", "else:\n", " print('Строка не заканчивается на \"world\"')" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Строка начинается с \"The\" и заканчивается на \"Spain\"\n" ] } ], "source": [ "txt = \"The rain in Spain\"\n", "x = re.search(\"^The.*Spain$\", txt)\n", "if (x):\n", " print('Строка начинается с \"The\" и заканчивается на \"Spain\"')\n", "else:\n", " print('Строка не начинается с \"The\" или не заканчивается на \"Spain\"')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### \\b\n", "Обозначает начало или конец слова. " ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "2\n" ] } ], "source": [ "str = 'The house on the hill'\n", "x = re.findall(r\"\\bh\", str)\n", "print(len(x))" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['ain', 'ain', 'ain']\n" ] } ], "source": [ "str = \"The rain in Spain falls mainly in the plain!\"\n", "x = re.findall(r\"ain\\b\", str)\n", "print(x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## () \n", "Заключенное в круглых скобках регулярное подвыражение означает группировку несколких символов." ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['', '123', '', '123']\n" ] } ], "source": [ "str = '23a 123a 1223a123ab b1e2e3'\n", "x = re.findall(r\"(123)?a\", str) # ? - ровно 1 раз\n", "print(x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### [] (символьные классы)\n", "Набор символов в квадратных скобках \\[\\] именуется символьным классом и позволяет указать интерпретатору регулярных выражений, что на данном месте в строке может стоять один из перечисленных символов. Несколько примеров:\n", "* \\[abc\\] - совпадение, если в строке встречается любой из перечисленных символов\n", "* \\[^abc\\] - совпадение, если в строке встречается любой символ КРОМЕ перечисленных\n", "* \\[a-m\\] - совпадение, если в строке встречается любой из символов от a до m\n", "* \\[2-6\\] - совпадение, если в строке встречается любая цифра от 2 до 6\n", "* \\[a-zA-Z\\] - совпадение, если в строке встречается любая буква в любом регистре\n", "* \\[0-9\\] - совпадение, если в строке встречается любая цифра" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Совпадения: ['10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '10', '20', '30', '41', '70']\n" ] } ], "source": [ "str = \"01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 100 200 300 4170\"\n", "x = re.findall(\"[1-9][0-9]\", str)\n", "print('Совпадения:',x)" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Совпадения: ['12-67-82', '98-32-11']\n" ] } ], "source": [ "str = \"Обращайтесь по одному из следующих номеров: 12-67-82, 98-32-11\"\n", "x = re.findall(\"[0-9][0-9]-[0-9][0-9]-[0-9][0-9]\", str)\n", "print('Совпадения:',x)" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Совпадения: ['rain', 'in', 'Spain', 'in', 'plain']\n" ] } ], "source": [ "txt = \"The rain in Spain falls mainly in the n plain\"\n", "x = re.findall(r\"\\b[a-zA-Z]+n\\b\", txt)\n", "print('Совпадения:',x)" ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Совпадения: ['Spain', 'falls', 'plain']\n" ] } ], "source": [ "txt = \"The rain in Spain falls mainly in the plain\"\n", "x = re.findall(r\"\\b[a-zA-Z]{5}\\b\", txt)\n", "print('Совпадения:',x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Функции, которые можно использовать в модуле re\n", "\n", "* #### findall:\n", "возвращает список из всех возможных совпадений. ВАЖНО: найденные подстроки НЕ МОГУТ пересекаться!" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "str = \"123456\"\n", "x = re.findall(\"[0-9][0-9]\", str)\n", "print('Совпадения:',x) # в качестве результата возвращает 12, 34, 56, но не 23 и 45" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['Lorem', 'ipsum', 'dolor', 'sit', 'amet', 'consectetur', 'adipisicing', 'elit']\n" ] } ], "source": [ "text = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit.' # зададим рандомный текст\n", "\n", "regExp = r\"\\w+\" # задаем регулярное выражение. Перед выражением нужно написать букву r\n", "# при помощи регулярного выражения \\w+ можно найти все слова в тексте, игнорируя пробелы, запятые и другие символы\n", "words = re.findall(regExp, text) # находим все совпадения с данным регулярным выражением\n", "\n", "print(words)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Таким же образом, при помощи выражения (`\\d+`), можно **найти все числа**. Важно учитывать, что числа могут быть и отрицителными. Составим регулярное выражение:\n", "1. Для нахождения любой цифры используем `\\d`\n", "2. Для того, чтобы находило не только цифру, а полное число, достаточно дописать `+` - `\\d+`. Так будут искаться все цифры идущие подряд\n", "3. Для того, чтобы учитывать и отрицительные числа, нужно проверять наличие минуса. Для этого нужно добавить `-?` к регулярному выражению. Без `?` в данном случае будут учитываться только отрицательные числа" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Найденные значения: ['5', '3', '4', '32', '-2']\n", "Найденные числа: [5, 3, 4, 32, -2]\n" ] } ], "source": [ "text = 'Numbers: 5 3 4 32 -2'\n", "\n", "regExp = r\"-?\\d+\" # задаем регулярное выражение. Перед выражением нужно написать букву r\n", "numbers = re.findall(regExp, text) # находим все совпадения с данным регулярным выражением\n", "\n", "print(rf'Найденные значения: {numbers}') # важно учитывать, что все найденные совпадения являются строковыми значениями\n", "\n", "numbers = list(map(int, numbers)) # переведем все найденные числа из строкового значения в числовые\n", "\n", "print(rf'Найденные числа: {numbers}')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* #### search:\n", "возвращает объект, в котором содержится информация о результатах поиска. Его методы start() и end() позволяют найти начальную и конечную позицию первого вхождения. Кроме того, объект может использоваться в условии, так как при отсутствии совпадений он эквивалентен False." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "str = \"The rain in Spain falls mainly in the plain!\"\n", "x = re.search('ain', str)\n", "print(x.start(), x.end())" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "str = \"Hello world\"\n", "x = re.search(\"^Hello\", str)\n", "if (x):\n", " print('Строка начинается с \"Hello\"')\n", "else:\n", " print('Строка не начинается с \"Hello\"')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* #### sub:\n", "возвращает строку, в которой все совпадения были заменены на другие подстроки" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Я**!**люблю**!**программирование!\n" ] } ], "source": [ "str = \"Я люблю программирование!\"\n", "x = re.sub(\"\\s\", \"**!**\", str)\n", "print(x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Бывают случаи, когда нужно **заменить значение на другое, используя часть этого значения**. Например, заменим все отрицательные числа на положительные:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "text = '1 -2 3 -46 55'\n", "\n", "regExp = r'-(\\d+)'\n", "result = re.sub(regExp, '\\g<1>', text)\n", "\n", "print(rf'Было: {text}')\n", "print(rf'Стало: {result}')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Данное регулярное выражение (`-(\\d+)`) состоит из двух групп:\n", "0. `-(\\d+)` - отрицательное число\n", "1. `\\d+` - число, не учитывая знак `-`\n", "\n", "В строке `result = re.sub(regExp, '\\g<1>', text)` в исходном тексте ищутся все совпадения по группе 0 (группа 0 - все регулярное выражение). Под нулевую группу попадает `-2`. Под первую группу попадает `2`. Таким образом, полное совпадение будет заменяться на результат первой группы." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "* #### split:\n", "делит строку по указанному разделителю и возвращает список" ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "['The', 'rain', 'in', 'Spain.']\n" ] } ], "source": [ "str = \"The rain in Spain.\"\n", "x = re.split(\"\\s\", str)\n", "print(x)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Больше информации здесь: https://www.w3schools.com/python/python_regex.asp" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.5" } }, "nbformat": 4, "nbformat_minor": 2 }