RegEx: 20 коротких шагов для освоения регулярных выражений. Часть 1
Оригинал здесь
В прошлой части мы освоили простейшие регулярные выражения, и уже кое-чему научились. В этой части мы изучим чуть более сложные конструкции, но, поверьте, это будет не так трудно, как могло-бы показаться.
Итак, продолжим!
Шаг 8: звездочка
До сих пор нам более или менее удавалось сопоставлять только строки заданной длины. Но в последних задачах мы приблизились к пределу того, что мы можем сделать с помощью обозначений, которые мы видели до сих пор.
Предположим, например, что мы не ограничены 3-символьными идентификаторами Java, а у нас могут быть идентификаторы любой длины. Решение, которое могло работать в предыдущем примере, не будет работать в следующем примере:
Шаг 9: "optional" вопросительный знак
Вы уже написали regex для решения последней задачи? Это сработало?
Теперь попробуйте применить его здесь:
Шаг 10: знак "or" (или)
На шаге 8 у нас возникли некоторые трудности с нахождением различных типов чисел с плавающей точкой:

Шаг 8: звездочка *
и знак "плюс" +

pattern: [a-zA-Z_$]\w\w string: __e $123 3.2 fo Barr a23mm ab x matches: ^^^ ^^^ ^^^ ^^^(Пример) Обратите внимание, что когда идентификатор действителен, но длиннее 3 символов, сопоставляются только первые три символа. И когда идентификатор действителен, но содержит менее 3 символов, то regex его вообще не находит! Проблема в том, что выражения в квадратных скобках
[]
соответствуют ровно одному символу, как и классы символов, такие как \w
. Это означает, что любые совпадения в приведенном выше регулярном выражении должны быть длиной ровно в три символа. Так что это не работает, как мы могли-бы надеяться.
Здесь могут помочь специальные символы *
и +
. Это модификаторы, которые могут быть добавлены справа от любого выражения, чтобы искать соответствия этому выражению более одного раза.
Звезда Клини (или "звездочка") *
укажет, что нужно искать соответствия предыдущему токену любое количество раз, включая ноль раз. Знак "плюс" +
укажет, что нужно искать один или несколько раз. Таким образом, выражение, которое предшествует +
, является обязательным (по крайней мере, один раз), в то время как выражение, которое предшествует *
, является необязательным, но когда оно появляется, оно может появляться любое количество раз.
Теперь, с этим знанием мы можем исправить приведенное выше регулярное выражение:
pattern: [a-zA-Z_$]\w* string: __e $123 3.2 fo Barr a23mm ab x matches: ^^^ ^^^^ ^^ ^^^^ ^^^^^ ^^ ^(Пример) Теперь мы сопоставляем действительные идентификаторы любой длины! Бинго! Но что-бы произошло, если бы мы в примере выше использовали
+
вместо *
?
pattern: [a-zA-Z_$]\w+ string: __e $123 3.2 fo Barr a23mm ab x matches: ^^^ ^^^^ ^^ ^^^^ ^^^^^ ^^(Пример) Мы пропустили последнее совпадение,
х
. Это связано с тем, что для +
требуется сопоставление хотя бы одного символа, но поскольку выражение в скобках []
, предшествующее \w+
, уже 'съело' символ x
, то доступных символов больше не осталось, поэтому сопоставление не удалось.
Когда мы можем использовать +
? Когда нам нужно найти хотя бы одно совпадение, но не важно, сколько раз должно совпасть данное выражение. Например, если мы хотим найти любые числа, содержащие десятичную точку:
pattern: \d*\.\d+ string: 0.011 .2 42 2.0 3.33 4.000 5 6 7.89012 matches: ^^^^^ ^^ ^^^ ^^^^ ^^^^^ ^^^^^^^(Пример) Обратите внимание, что сделав числа слева от десятичной точки необязательными, мы смогли найти как 0.011 так и .2. Для этого нам нужно было сопоставить ровно одну десятичную точку при помощи
\.
и минимум одну цифру справа от десятичной точки при помощи \d+
. Вышеупомянутое регулярное выражение не будет соответствовать числу, подобному 3.
, потому что для соответствия нам требуется по крайней мере одна цифра справа от десятичной точки.
По традиции, решим пару простых задачек:
Найдите все английские слова в отрывке ниже.pattern: string: 3 plus 3 is six but 4 plus three is 7 matches: ^^^^ ^^ ^^^ ^^^ ^^^^ ^^^^^ ^^(Решение) Найдите все обозначения размеров файлов в списке ниже. Размеры файлов будут состоять из числа (с десятичной точкой или без нее), за которым следуют
KB
, MB
, GB
или TB
:
pattern: string: 11TB 13 14.4MB 22HB 9.9GB TB 0KB matches: ^^^^ ^^^^^^ ^^^^^ ^^^(Решение)
Шаг 9: "optional" вопросительный знак ?

pattern: string: 1..3KB 5...GB ..6TB matches:Очевидно, что ни одно из этих обозначений не является допустимым размером файла, поэтому хорошее регулярное выражение не должно находить ни одно из них. Решение, которое я написал для решения последней задачи, соответствует им всем, по крайней мере частично:
pattern: \d+\.*\d*[KMGT]B string: 1..3KB 5...GB ..6TB matches: ^^^^^^ ^^^^^^ ^^^(Пример) Так в чем-же проблема? На самом деле, нам нужно найти только одну десятичную точку, если она есть. Но
*
допускает любое количество совпадений, включая ноль. Есть ли способ сопоставлять только ноль раз или один раз? Но не более одного раза? Конечно есть.
"optional" ?
это модификатор, который соответствует нулю или одному из предыдущих символов, но не более:
pattern: \d+\.?\d*[KMGT]B string: 1..3KB 5...GB ..6TB matches: ^^^ ^^^(Пример) Мы здесь приблизились к решению, но это еще не совсем то, что нам надо. Чуть позже мы увидим, как это исправить, через несколько шагов.
А пока решим такую задачу:
В некоторых языках программирования (например, Java) за некоторыми целыми числами и числами с плавающей запятой (точкой) могут следоватьl
/ L
и f
/ F
, чтобы указать, что они должны рассматриваться как long / float (соответственно), а не как обычные int / double.
Найдите все действительные "long" числа в строке ниже:
pattern: string: 13L long 2l 19 L lL 0 matches: ^^^ ^^ ^^ ^(Решение)
Шаг 10: знак "or" (или) |

pattern: \d*\.\d+ string: 0.011 .2 42 2.0 3.33 4.000 5 6 7.89012 matches: ^^^^^ ^^ ^^^ ^^^^ ^^^^^ ^^^^^^^Приведенный выше шаблон сопоставляет числа с десятичной точкой и минимум одну цифру справа от десятичной точки. Но что, если мы также хотим сопоставить такие строки, как
0.
? (Без цифр справа от десятичной точки.)
Мы могли бы написать такое регулярное выражение:
pattern: \d*\.\d* string: 0.011 .2 42 2.0 3.33 4.000 5 6 7.89012 0. . matches: ^^^^^ ^^ ^^^ ^^^^ ^^^^^ ^^^^^^^ ^^ ^(Пример) Это соответствует
0.
, но это также соответствует одиночной точке .
, как вы можете видеть выше.
На самом деле то, что мы пытаемся сопоставить, это два разных класса строк:
- числа с минимум одной цифрой справа от десятичной точки
- числа с хотя бы одной цифрой слева от десятичной точки
pattern: \d*\.\d+ string: 0.011 .2 42 2.0 3.33 4.000 5 6 7.89012 0. . matches: ^^^^^ ^^ ^^^ ^^^^ ^^^^^ ^^^^^^^
pattern: \d+\.\d* string: 0.011 .2 42 2.0 3.33 4.000 5 6 7.89012 0. . matches: ^^^^^ ^^^ ^^^^ ^^^^^ ^^^^^^^ ^^Мы видим, что ни в одном из этих случаев подстроки
42
, 5
, 6
или .
не находятся движком. Для получения необходимого результата, нам не помешало-бы объединить эти регулярки. Как мы можем этого достичь?
Знак "or" |
позволяет нам указать в регулярном выражении сразу несколько возможных последовательностей совпадений. Подобно тому, как []
позволяет нам указывать альтернативные одиночные символы, с помощью знака "or" |
мы можем указывать альтернативные многосимвольные выражения.
Например, если мы хотим найти "собаку" или "кошку", мы могли-бы написать как-то так:
pattern: \w\w\w string: Obviously, a dog is a better pet than a cat. matches: ^^^^^^^^^ ^^^ ^^^^^^ ^^^ ^^^ ^^^(Пример) ... но это соответствует всем тройным последовательностям символов класса "word". Но "dog" и "cat" даже не имеют общих букв, поэтому и квадратные скобки не помогут нам здесь. Вот самое простое регулярное выражение, которое мы могли бы использовать, оно соответствует обоим и только этим двум словам:
pattern: dog|cat string: Obviously, a dog is a better pet than a cat. matches: ^^^ ^^^(Пример) Механизм регулярных выражений сначала пытается сопоставить всю последовательность слева от знака
|
, но если ему это не удается, то затем он пытается сопоставить последовательность справа от знака |
. Несколько знаков |
также могут быть объединены в цепочки для соответствия более чем двум альтернативным последовательностям:
pattern: dog|cat|pet string: Obviously, a dog is a better pet than a cat. matches: ^^^ ^^^ ^^^(Пример)
Теперь решим очередную пару задачек, чтобы лучше усвоить этот шаг:
Используйте знак|
, чтобы исправить десятичное регулярное выражение, указанное выше, и получить такой результат:
pattern: string: 0.011 .2 42 2.0 3.33 4.000 5 6 7.89012 0. . matches: ^^^^^ ^^ ^^^ ^^^^ ^^^^^ ^^^^^^^ ^^(Решение) Используйте знак
|
, классы символов, "optional" ?
и т.д., чтобы создать одно регулярное выражение, соответствующее как целым числам, так и числам с плавающей запятой (точкой), как обсуждалось в задаче в конце предыдущего шага (эта задачка немного посложнее, да ;))
pattern: string: 42L 12 x 3.4f 6l 3.3 0F L F .2F 0. matches: ^^^ ^^ ^^^^ ^^ ^^^ ^^ ^^^ ^^(Решение) 20 коротких шагов для освоения регулярных выражений. Часть 3 RegEx: 20 коротких шагов для освоения регулярных выражений. Часть 4
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
\d*\.\d+
а для второго так:\d+\.\d*