Миналия уикенд отидох на страхотен семинар на 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’ в низа.

.replace()

Опитайте това: 'I like apples'.replace(/apple/, 'orange')

Трябваше да получиш 'I like oranges', много готино, нали.

Случай на употреба: Имах проблем, при който бях копирал елементи от DOM и ги запазих като низ. Опитвах се да рендерирам отново този низ в DOM, но JSX в реакцията не приема низ като аргумент на стила и за съжаление, когато запазих DOM, получих малко забавно добавено style=”” в моя HTML низ. Използвах DomString.replace(/style=""/g, ''), за да заменя всички тези досадни малки момчета. g в края на /style=""/ казва на метода за замяна да замени всеки екземпляр вместо само първия открит. Повече за g в част 2.

.split()

До този уикенд изобщо не знаех, че можете да подадете регулярен израз в метода на разделяне и да го накарате да се разделя при всяко съвпадение. Например да кажем, че искаме да разделим всеки препинателен знак, за да можем да върнем само изреченията.

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 ›

Препратки:

„Антъни Ферара“