Миналия уикенд отидох на страхотен семинар на Anthony Ferrara за регулярни изрази (regex). Вместо да пазя бележките си от разговора, реших, че ще е по-добре да ги преведа в няколко публикации в блога. Има много неща за разглеждане, така че днес ще огранича тази публикация до няколко метода на Javascript regex и само малко regex. Имайте предвид, че има подобни методи, които описвам по-долу, в Ruby и PHP, така че с някои малки корекции това може да се приложи и за тези езици.
Regex.prototype методи
Има много повече методи, отколкото мога да покрия днес, но ще подчертая няколко, които намирам за доста полезни.
".съвпада()"
Опитайте това в конзолата си:'happy happy joy joy'.match(/happy/)
Това ще върне: ['happy', index: 0, input: 'happy happy joy joy']
Какво ни казва това? Първият елемент в масива е първото съвпадение, намерено в низа, вторият елемент ни казва от кой индекс на низа започва съвпадението, след това последният бит ни казва какъв е бил този първоначален вход. Имайте предвид, че ако бяхме изпълнили 'happy happy joy joy'.match(/happy/g)
, щяхме да върнем ['happy', 'happy']
. Защо? Тъй като g
‘флаг’ в края, регулярният израз казва на метода за съвпадение да върне всяко появяване на ‘happy’ в низа.
Опитайте това: 'I like apples'.replace(/apple/, 'orange')
Трябваше да получиш 'I like oranges'
, много готино, нали.
Случай на употреба: Имах проблем, при който бях копирал елементи от DOM и ги запазих като низ. Опитвах се да рендерирам отново този низ в DOM, но JSX в реакцията не приема низ като аргумент на стила и за съжаление, когато запазих DOM, получих малко забавно добавено style=””
в моя HTML низ. Използвах DomString.replace(/style=""/g, '')
, за да заменя всички тези досадни малки момчета. g
в края на /style=""/
казва на метода за замяна да замени всеки екземпляр вместо само първия открит. Повече за g
в част 2.
До този уикенд изобщо не знаех, че можете да подадете регулярен израз в метода на разделяне и да го накарате да се разделя при всяко съвпадение. Например да кажем, че искаме да разделим всеки препинателен знак, за да можем да върнем само изреченията.
var annoyingString='I am! am I? Who am I.' annoyingString.split(/[!?\.]\s*/) => ["I am", "am I", "Who am I", ""]
Вижте по-долу обяснение какво, по дяволите, означава /[!?\.]\s*/
.
Нека да разбием това[!?\.]
е клас на знаци, който основно съвпада с някой от знаците !
, ?
или .
(имайте предвид, че трябваше да използвам екраниращ знак, \
, пред точката, защото .
сам по себе си е запазен за съвпадащи с всеки знак с изключение на завършващи редове ). Следва знакът \s
, който казва, че съответства на всяко празно пространство, но задръжте, последната точка няма бяло пространство, така че как ще съпоставим това? Това е мястото, където се появява квантификаторът *
; той казва на функцията да съответства на нула или всякакви бели интервали. Ще забележите в масива по-горе, че последният елемент в масива е празен низ, не забравяйте, че тъй като използвахме метода .split(), той се раздели в края на annoyingString
и какво е след .
на досадния низ? Нищо. Така че получаваме празен низ.
".тест()"
Тестовият метод наистина е подобен на метода .match()
с изключение на това, че връща true или false. За този нека го изпробваме с метода .filter()
, не забравяйте, че филтърът връща всичко в масива, което връща true за дадените условия. Нека да го изпробваме на нашия annoyingArray
.
var annoyingArray = annoyingString.split(/[!?\.]\s*/) annoyingArray => ["I am", "am I", "Who am I", ""] annoyingArray.filter(string => /^I/.test(string)) =>["I am"]
И така, какво означава /^I/
? Ами ^
в този случай означава връщане на всеки низ, който започва с главно I
и тъй като изпълняваме текст върху него. Тестът връща true само за първия низ в annoyingArray
, което означава, че филтърният метод ще изплюе само този първи низ.
Regex Anchors
/^a/ match any string that begins with 'a' ['abc', 'bca', 'aba'].filter(str => /^a/.test(str)) => ['abc'] /a$/ match any string that ends with 'a' ['abc', 'bca', 'aba'].filter(str => /a$/.test(str)) => ['bca', 'aba'] /^a$/ match any string that begins and ends with the same a ['abc', 'bca', 'aba', 'a'].filter(str => /^a$/.test(str)) =>['a']
Чакай малко, защо последният не се върна ['aba']
?! Ами ние казваме на Regex да съвпада въз основа на това дали низът започва и завършва с едно и също 'a'
. Така че да, 'aba'
има две различни. Нека видим как можем да сравним и двете наведнъж:
['abc', 'bca', 'aba', 'a', 'abba'].filter(str => /^a$|^a.*a$/.test(str)) => ['aba', 'a', 'abba']
Вероятно има няколко неща, които не разпознаваме в този момент. Първо знакът |
, който изглежда наистина подобен на ||
, който използваме в много езици за програмиране, за да кажем „или“, така че съвпада или с първия бит, или с втория бит. Следващото нещо е .
само по себе си. Спомнете си как преди казахме, че ще съответства на всеки знак, с изключение на знак за край на ред. Комбинирах го с квантора *
, защото „ти си кралицата на Regex“. Вторият бит ^a.*a$
казва върни ми всичко, което започва и завършва с две различни а и има нула или повече знаци вътре. Така получаваме и двете ['aba', 'abba']
.
В следващата си публикация ще се гмурна малко по-задълбочено относно регулярния израз, след като се чувстваме добре с различните методи на JavaScript, които можем да използваме с тях. По-долу има списък с неща, които ще разгледаме, и ще ви дам още няколко примера за това как работят в част 2.
Character 'Classes': [...] list of acceptable characters any of them [^...] list of unacceptable characters. [0-9] is the same as [0123456789] [a-z] any letter [A-Z] any capital letter [a-zA-Z] any letter without case. Quantifiers: * matches 0 or more. ? matches 0 or 1 + matches 1 or more occurrences {4} matches 4 occurrences {2,4} matches between 2 and 4 occurrences. Flags: g global match, find all matches i ignore the case of the matches m multiline match
Вижте някои от препратките по-долу. Горещо препоръчвам Regex101, ако искате да си поиграете.
Продължете към част 2 ›
Препратки:
„Антъни Ферара“