Макс Ширшин — Регулярные выражения
DESCRIPTION
Чем могут быть полезны регулярные выражения для разработчика интерфейсов? О каких возможностях следует знать больше? Где находятся «подводные камни» и как обойти их в различных реализациях? И, наконец, что делать, если возможностей встроенной реализации регулярных выражений недостаточно?TRANSCRIPT
Школа Разработки Интерфейсов Яндекса Симферополь, 2013
Руководитель группы разработки интерфейсов Рекламной Сети Яндекса
Макс Ширшин
Регулярные выражения
Вместо предисловия
2
Виды регулярных выражений
• POSIX (BRE, ERE)
• PCRE = Perl-Compatible Regular Expressions
3
Цитата из стандарта языка JavaScript:
«Вид и функциональность регулярных выражений в JavaScript реализованы по подобию подсистемы регулярных выражений в языке программирования Perl 5»
4
JS-синтаксис (очень кратко)
var re = /^foo/;
5
JS-синтаксис (очень кратко)
var re = /^foo/; // boolean re.test('строка');
6
JS-синтаксис (очень кратко)
var re = /^foo/; // boolean re.test('строка'); // null или Array re.exec('строка');
7
Из чего состоят регэкспы
8
Из чего состоят регэкспы
1. Символы
9
Из чего состоят регэкспы
1. Символы
2. Операции
10
Из чего состоят регэкспы
1. Символы
— обычные
2. Операции
11
Из чего состоят регэкспы
1. Символы
— обычные
— специальные (метасимволы)
2. Операции
12
Из чего состоят регэкспы
1. Символы
— обычные
— специальные (метасимволы)
2. Операции
— квантификация
13
Из чего состоят регэкспы
1. Символы
— обычные
— специальные (метасимволы)
2. Операции
— квантификация
— перечисление
14
Из чего состоят регэкспы
1. Символы
— обычные
— специальные (метасимволы)
2. Операции
— квантификация
— перечисление
— группировка
Метасимволы
8
/./.test('foo'); // true /./.test('\r\n') // false
16
Любой символ
/./.test('foo'); // true /./.test('\r\n') // false Что вы хотели на самом деле: /[\s\S]/ для JS или /./s (не работает в JS)
17
Любой символ
>>> /^something$/.test('something') true
18
Границы строк
>>> /^something$/.test('something') true >>> /^something$/.test('something\nbad') false
19
Границы строк
>>> /^something$/.test('something') true >>> /^something$/.test('something\nbad') false >>> /^something$/m.test('something\nbad') true
20
Границы строк
>>> /\ba/.test('alabama) true
21
Граница слова
>>> /\ba/.test('alabama) true >>> /a\b/.test('alabama') true
22
Граница слова
>>> /\ba/.test('alabama) true >>> /a\b/.test('alabama') true >>> /a\b/.test('naïve') true
23
Граница слова
>>> /\ba/.test('alabama) true >>> /a\b/.test('alabama') true >>> /a\b/.test('naïve') true не-граница слова /\Ba/.test('alabama');
24
Граница слова
Символьные классы
12
/\s/ (инвертированный вариант /\S/)
26
Пробельные символы
/\s/ (инвертированный вариант /\S/) FF: \t \n \v \f \r \u0020 \u00a0 \u1680 \u180e \u2000 \u2001 \u2002 \u2003 \u2004 \u2005 \u2006 \u2007 \u2008 \u2009 \u200a\ u2028 \u2029\ u202f \u205f \u3000 Chrome 19, IE 9: как в FF 12 и ещё \ufeff IE 7, 8 :-( только: \t \n \v \f \r \u0020
27
Пробельные символы
/\d/ ~ цифры от 0 до 9 /\w/ ~ буквы, цифры и подчёркивание В JS не работает для русских букв! И наоборот: /\D/ ~ всё, кроме цифр /\W/ ~ всё, кроме букв и цифр
28
Буквы и цифры
Пример: /[abc123]/
29
Произвольные классы символов
Пример: /[abc123]/ Работают метасимволы и диапазоны: /[A-F\d]/
30
Произвольные классы символов
Пример: /[abc123]/ Работают метасимволы и диапазоны: /[A-F\d]/ Можно указать несколько диапазонов: /[a-cG-M0-7]/
31
Произвольные классы символов
Пример: /[abc123]/ Работают метасимволы и диапазоны: /[A-F\d]/ Можно указать несколько диапазонов: /[a-cG-M0-7]/ ВАЖНО: диапазоны берутся из Юникода. При работе с кириллическими диапазонами проверьте порядок символов в Юникоде!
32
Произвольные классы символов
символ «точка» — просто точка! /[.]/.test('anything') // false
33
Произвольные классы символов
символ «точка» — просто точка! /[.]/.test('anything') // false символы: \ ] - /[\\\]-]/
34
Произвольные классы символов
всё, кроме a, b, c: /[^abc]/ ^ как символ: /[abc^]/
35
Инвертированные символьные классы
Квантификаторы
18
/bo*/.test('b') // true
37
Ноль или более, один или более
/bo*/.test('b') // true /.*/.test('') // true
38
Ноль или более, один или более
/bo*/.test('b') // true /.*/.test('') // true /bo+/.test('b') // false
39
Ноль или более, один или более
/colou?r/.test('color'); /colou?r/.test('colour');
40
Ноль или один
41
Диапазоны повторов
/bo{7}/ точно 7
42
Диапазоны повторов
/bo{7}/ точно 7 /bo{2,5}/ от 2 до 5, x < y
43
Диапазоны повторов
/bo{7}/ точно 7 /bo{2,5}/ от 2 до 5, x < y /bo{5,}/ 5 или более
44
Диапазоны повторов
/bo{7}/ точно 7 /bo{2,5}/ от 2 до 5, x < y /bo{5,}/ 5 или более в JS не работает! /b{,5}/.test('bbbbb')
var r = /a+/.exec('aaaaa');
45
Жадные (greedy) квантификаторы
var r = /a+/.exec('aaaaa'); >>> r[0]
46
Жадные (greedy) квантификаторы
var r = /a+/.exec('aaaaa'); >>> r[0] "aaaaa"
47
Жадные (greedy) квантификаторы
var r = /a+?/.exec('aaaaa');
48
Ленивые (lazy) квантификаторы
var r = /a+?/.exec('aaaaa'); >>> r[0]
49
Ленивые (lazy) квантификаторы
var r = /a+?/.exec('aaaaa'); >>> r[0] "a"
50
Ленивые (lazy) квантификаторы
var r = /a+?/.exec('aaaaa'); >>> r[0] "a" r = /a*?/.exec('aaaaa');
51
Ленивые (lazy) квантификаторы
var r = /a+?/.exec('aaaaa'); >>> r[0] "a" r = /a*?/.exec('aaaaa'); >>> r[0]
52
Ленивые (lazy) квантификаторы
var r = /a+?/.exec('aaaaa'); >>> r[0] "a" r = /a*?/.exec('aaaaa'); >>> r[0] ""
53
Ленивые (lazy) квантификаторы
Группировки
24
с захватом
/(boo)/.test("boo");
55
Группировки
с захватом
/(boo)/.test("boo"); без захвата
/(?:boo)/.test("boo");
56
Группировки
var result = /(bo)o+(b)/.exec('the booooob');
57
Группировки и конструктор RegExp
var result = /(bo)o+(b)/.exec('the booooob'); >>> RegExp.$1 "bo"
58
Группировки и конструктор RegExp
var result = /(bo)o+(b)/.exec('the booooob'); >>> RegExp.$1 "bo" >>> RegExp.$2 "b"
59
Группировки и конструктор RegExp
var result = /(bo)o+(b)/.exec('the booooob'); >>> RegExp.$1 "bo" >>> RegExp.$2 "b" >>> RegExp.$9 ""
60
Группировки и конструктор RegExp
var result = /(bo)o+(b)/.exec('the booooob'); >>> RegExp.$1 "bo" >>> RegExp.$2 "b" >>> RegExp.$9 "" >>> RegExp.$10 undefined
61
Группировки и конструктор RegExp
var result = /(bo)o+(b)/.exec('the booooob'); >>> RegExp.$1 "bo" >>> RegExp.$2 "b" >>> RegExp.$9 "" >>> RegExp.$10 undefined >>> RegExp.$0 undefined
62
Группировки и конструктор RegExp
/((foo) (b(a)r))/
63
Порядок нумерации группировок
/((foo) (b(a)r))/ $1 ( ) foo bar
64
Порядок нумерации группировок
/((foo) (b(a)r))/ $1 ( ) foo bar $2 ( ) foo
65
Порядок нумерации группировок
/((foo) (b(a)r))/ $1 ( ) foo bar $2 ( ) foo $3 ( ) bar
66
Порядок нумерации группировок
/((foo) (b(a)r))/ $1 ( ) foo bar $2 ( ) foo $3 ( ) bar $4 ( ) a
67
Порядок нумерации группировок
var r = /best(?= match)/.exec('best match');
68
Lookahead
var r = /best(?= match)/.exec('best match'); >>> !!r true
69
Lookahead
var r = /best(?= match)/.exec('best match'); >>> !!r true >>> r[0] "best"
70
Lookahead
var r = /best(?= match)/.exec('best match'); >>> !!r true >>> r[0] "best" >>> /best(?! match)/.test('best match') false
71
Lookahead
Перечисление
30
/red|green|blue light/ /(red|green|blue) light/ >>> /var a(;|$)/.test('var a') true
73
Логическое «или»
true /(red|green) apple is \1/.test('red apple is red') true /(red|green) apple is \1/.test('green apple is green')
74
Backreferences (обратные ссылки)
Представление символов
32
\x09 === \t (не Unicode, для ASCII/ANSI) \u20AC === € (для Unicode)
76
Представление символов
\x09 === \t (не Unicode, для ASCII/ANSI) \u20AC === € (для Unicode) обратный slash убирает специальное значение у символа /\(\)/.test('()') // true /\\n/.test('\\n') // true
77
Представление символов
\x09 === \t (не Unicode, для ASCII/ANSI) \u20AC === € (для Unicode) обратный slash убирает специальное значение у символа /\(\)/.test('()') // true /\\n/.test('\\n') // true иногда верно и обратное /\f/.test('f') // false!
78
Представление символов
Флаги
34
g i m s x
80
Флаги в регулярных выражениях
g i m s x global match
81
Флаги в регулярных выражениях
g i m s x global match ignore case
82
Флаги в регулярных выражениях
g i m s x global match ignore case multiline matching for ^ and $
83
Флаги в регулярных выражениях
g i m s x global match ignore case multiline matching for ^ and $ нет поддержки в JS для: string as single line extend pattern
84
Флаги в регулярных выражениях
/(?i)foo/ /(?i-m)bar$/ /(?i-sm).x$/ /(?i)foo(?-i)bar/ Не все реализации поддерживают переключение флагов внутри regexp. JS при таком синтаксисе включает флаги на весь regexp сразу и не даёт менять.
85
Альтернативный синтаксис для флагов
RegExp в JavaScript
86
экземпляры RegExp: /regexp/.exec('строка') null или массив ['всё совпадение', $1, $2, ...] /regexp/.test('строка') false или true экземпляры String: 'str'.match(/regexp/) 'str'.match('\\w{1,3}') - эквивалент /regexp/.exec, если нет флага g; - массив всех совпадений по строке, если есть флаг g (внутренние группировки игнорируются) 'str'.search(/regexp/) 'str'.search('\\w{1,3}') позиция первого совпадения или -1
87
Методы
экземпляры String: 'str'.replace(/old/, 'new'); В строке замены поддерживаются следующие спецсимволы: $$ вставляет значок доллара "$" $& подстрока, совпавшая с регэкспом $` подстрока до $& $' подстрока после $& $1, $2, $3 и т.д.: cтрока, совпавшая с соответствующей скобочной группировкой 'str'.replace(/(r)(e)gexp/g, function(matched, $1, $2, offset, sourceString) { // чем заменить matched на этом шаге? return 'замена'; });
88
Методы
// ПЛОХО var re = new RegExp('^' + userInput + '$'); var userInput = '[abc]'; // ХОРОШО RegExp.escape = function(text) { return text.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); }; var re = new RegExp('^' + RegExp.escape(userInput) + '$');
89
RegExp injection
Что почитать
90
91
В интернете: javascript.ru/basic/regular-expression
Mastering Regular Expressions
O'Reilly Media
Книга:
Вопросы?
92
Руководитель группы разработки интерфейсов Рекламной Сети Яндекса
@ingdir
Макс Ширшин