Бързо и просто ръководство за регулярни изрази на JavaScript

Искате да научите JavaScript? Вземете безплатната ми електронна книга на jshandbook.com

Въведение в регулярните изрази

Редовният израз (наричан също regex за кратко) е бърз начин за работа с низове от текст.

Формулирайки редовен израз със специален синтаксис, можете:

  • търсене на текст в низ
  • заменете подредовете в низ
  • и извличате информация от низ

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

Регулярните изрази датират от 50-те години на миналия век, когато са формализирани като концептуален модел за търсене на алгоритми за обработка на струни.

Реализирани в UNIX инструменти като grep, sed и в популярни текстови редактори, регексите нараснаха все по-популярно. Те бяха въведени в езика за програмиране на Perl, а по-късно и в много други.

JavaScript, заедно с Perl, е един от езиците за програмиране, който има поддръжка за регулярни изрази, директно вградени в езика.

Трудно, но полезно

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

Криптичните регулярни изрази са трудни за писане, трудно се четат и трудно се поддържат / променят.

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

Този урок има за цел да ви запознае с регулярните изрази на JavaScript по прост начин и да ви даде цялата информация за четене и създаване на регулярни изрази.

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

Как изглежда Regular Expression?

В JavaScript регулярният израз е обект, който може да бъде дефиниран по два начина.

Първият е чрез създаване на нов обект RegExp с помощта на конструктора:

const re1 = new RegExp ('ей')

Втората е използването на буквалната форма на регулярния израз

const re1 = / хей /

Знаете, че JavaScript има обектни литерали и масиви от масиви? Той също има регекс литерали.

В горния пример хей се нарича модел. В буквалната форма тя се разграничава от наклонени черти напред, докато при конструктора на обекти не е така.

Това е първата важна разлика между двете форми, но други ще видим по-късно.

Как работи?

Редовният израз, който дефинирахме като re1 по-горе, е много прост. Той търси низа хей, без ограничение. Низът може да съдържа много текст и хей в средата, а реджексът е удовлетворен. Той може също да съдържа просто ей, и регексът също ще бъде удовлетворен.

Това е доста просто.

Можете да тествате регекса с помощта на RegExp.test (String), който връща булева информация:

re1.test ('ей') // 
re1.test ('blablabla hey blablabla') // 
re1.test ('той') // 
re1.test ('blablabla') // 

В горния пример ние просто проверихме дали „хей“ удовлетворява модела на регулярния израз, съхраняван в re1.

Това е най-простото, което може да бъде, но сега вече знаете много концепции за реджексите.

Котва

/Хей/

съвпада ей, където и да е било поставено вътре в низ.

Ако искате да съответствате на низове, които започват с хей, използвайте оператора ^:

/^hey/.test('hey ') // 
/^hey/.test('bla hey ') // 

Ако искате да съвпадате с низовете, които завършват с хей, използвайте оператора $:

/hey$/.test('hey ') // 
/hey$/.test('bla hey ') // 
/hey$/.test('hey you ') // 

Комбинирайте тези и съпоставете низовете, които точно съвпадат с ей, и точно този низ:

/^hey$/.test('hey ') // 

За да съответствате на низ, който започва с подреда и завършва с друга, можете да използвате. *, Който съответства на всеки символ, повторен 0 или повече пъти:

/^hey.*joe$/.test('hey joe ') // 
/^hey.*joe$/.test('heyjoe ') // 
/^hey.*joe$/.test('hey как си Джо ') // 
/^hey.*joe$/.test('hey joe! ') // 

Съпоставете елементи в диапазони

Вместо да съответствате на определен низ, можете да изберете да съвпадате с всеки символ в диапазон, като например:

/ [a-z] / // a, b, c, ..., x, y, z
/ [A-Z] / // A, B, C, ..., X, Y, Z
/ [a-c] / // a, b, c
/ [0-9] / // 0, 1, 2, 3, ..., 8, 9

Тези реджекси съвпадат с низове, които съдържат поне един от знаците в тези диапазони:

/ evidencea-zSense/.test('a ') // 
/ evidencea-zSense/.test('1 ') // 
/ evidencea-zSense/.test('A ') // 
/ evidencea-cSense/.test('d ') // 
/ evidencea-cSense/.test('dc ') // 

Обхватът може да се комбинира:

/ [A-Za-z0-9] /
/ EvidenceA-Za-z0-9Sense/.test('a ') // 
/ EvidenceA-Za-z0-9Sense/.test('1 ') // 
/ EvidenceA-Za-z0-9Sense/.test('A ') // 

Съчетаване на елемент от диапазон няколко пъти

Можете да проверите дали низът съдържа един и само един знак в диапазон, като използвате - char:

/ ^ [A-ZA-Z0-9] $ /
/^ evidenceA-Za-z0-9Sense$/.test('A ') // 
/^ evidenceA-Za-z0-9Sense$/.test('Ab ') // 

Отхвърляне на модел

Символът ^ в началото на образец го закотвя към началото на низ.

Използван в рамките на диапазон, той го отрича, така че:

/ Evidence^A-Za-z0-9Sense/.test('a ') // 
/ Evidence^A-Za-z0-9Sense/.test('1 ') // 
/ Evidence^A-Za-z0-9Sense/.test('A ') // 
/ Evidence^A-Za-z0-9Sense/.test('@ ') // 
  • \ d съответства на всяка цифра, еквивалентна на [0-9]
  • \ D съответства на всеки символ, който не е цифра, еквивалентен на [^ 0-9]
  • \ w съответства на всеки буквено-цифров символ, еквивалентен на [A-Za-z0-9]
  • \ W съответства на всеки не буквено-цифров символ, еквивалентен на [^ A-Za-z0-9]
  • \ s съответства на всеки символ на бялото пространство: интервали, раздели, нови редове и Unicode
  • \ S съответства на всеки символ, който не е бяло пространство
  • \ 0 съвпада с нула
  • \ n съвпада с нов ред
  • \ t съответства на символа на раздел
  • \ uXXXX съвпада с unicode символ с код XXXX (изисква флага u)
  • , съвпада с всеки символ, който не е знак за нов ред (напр. \ n) (освен ако не използвате знамето s, обяснено по-нататък)
  • [^] съответства на всеки символ, включително символи за нов ред Полезно е на многоредови низове.

Редовен избор на изрази

Ако искате да търсите един или друг низ, използвайте | оператор.

/hey|ho/.test('hey ') // 
/hey|ho/.test('ho ') // 

Quantifiers

Кажете, че имате този регекс, който проверява дали низ има една цифра в него и нищо друго:

/ ^ \ Г $ /

Можете ли да използвате? количествен показател, за да го направи незадължителен, като по този начин се изисква нула или едно:

/ ^ \ Г? $ /

но какво ще стане, ако искате да съответствате на няколко цифри?

Можете да го направите по 4 начина, като използвате +, *, {n} и {n, m}. Нека ги разгледаме един по един.

+

Съпоставете един или повече (> = 1) елемента

/ ^ \ Г + $ /
/^\d+$/.test('12 ') // 
/^\d+$/.test('14 ') // 
/^\d+$/.test('144343 ') // 
/ ^ \ d + $ /. тест ('') // 
/^\d+$/.test('1a ') // 

*

Съвпадат 0 или повече (> = 0) елемента

/ ^ \ Г + $ /
/^\d*$/.test('12 ') // 
/^\d*$/.test('14 ') // 
/^\d*$/.test('144343 ') // 
/ ^ \ d * $ /. тест ('') // 
/^\d*$/.test('1a ') // 

{н}

Съпоставете точно n елемента

/ ^ \ Г {3} $ /
/^\d{3}$/.test('123 ') // 
/^\d{3}$/.test('12 ') // 
/^\d{3}$/.test('1234 ') // 
/^ evidenceA-Za-z0-9clear{3}$/.test('Abc ') // 

{П, т}

Съвпадение между n и m пъти:

/ ^ \ Г {3,5} $ /
/^\d{3,5}$/.test('123 ') // 
/^\d{3,5}$/.test('1234 ') // 
/^\d{3,5}$/.test('12345 ') // 
/^\d{3,5}$/.test('123456 ') // 

m може да се пропусне да има отворен край, така че да имате поне n елемента:

/ ^ \ Г {3} $ /
/^\d{3,}$/.test('12 ') // 
/^\d{3,}$/.test('123 ') // 
/^\d{3,}$/.test('12345 ') // 
/^\d{3,}$/.test('123456789 ') // 

По желание елементи

Следвате артикул с? прави по избор:

/ ^ \ Г {3} \ w? $ /
/^\d{3}\w?$/.test('123 ') // 
/^\d{3}\w?$/.test('123a ') // 
/^\d{3}\w?$/.test('123ab ') // 

Групи

С помощта на скоби можете да създавате групи от знаци: (...)

Този пример съвпада точно с 3 цифри, последвани от един или повече буквено-цифрови знаци:

/ ^ (\ Г {3}) (\ w +) $ /
/^(\d{3})(\w+)$/.test('123 ') // 
/^(\d{3})(\w+)$/.test('123s ') // 
/^(\d{3})(\w+)$/.test('123something ') // 
/^(\d{3})(\w+)$/.test('1234 ') // 

Символите за повторение, поставени след скоби за затваряне на групата, се отнасят за цялата група:

/ ^ (\ Г {2}) + $ /
/^(\d{2})+$/.test('12 ') // 
/^(\d{2})+$/.test('123 ') // 
/^(\d{2})+$/.test('1234 ') // 

Заснемане на групи

Досега видяхме как да тестваме низовете и да проверяваме дали съдържат определен модел.

Много готина характеристика на регулярните изрази е възможността за улавяне на части от низ и поставянето им в масив.

Можете да го направите, като използвате Групи и по-специално Заснемане на групи.

По подразбиране групата е група за улавяне. Сега, вместо да използваме RegExp.test (String), който просто връща булева, ако моделът е удовлетворен, използваме или String.match (RegExp), или RegExp.exec (String).

Те са абсолютно същите и връщат масив с целия съвпадащ низ в първия елемент, след което всяко съвпадащо съдържание на групата.

Ако няма съвпадение, тя връща нула:

"123s'.match (/ ^ (\ г {3}) (\ w +) $ /)
// Array ["123s", "123", "s"]
/^(\d{3})(\w+)$/.exec('123s ")
// Array ["123s", "123", "s"]
"Hey'.match (/ (хей | хо) /)
// Array ["хей", "хей"]
/(hey|ho)/.exec('hey ")
// Array ["хей", "хей"]
/(hey|ho)/.exec('ha! ")
//нула

Когато дадена група се съпостави няколко пъти, в масива от резултати се поставя само последното съвпадение:

"123456789'.match (/ (\ г) + /)
// Array ["123456789", "9"]

Незадължителни групи

Групата за заснемане може да бъде направена по избор, като се използва (...) ?. Ако не бъде намерен, полученият слот за масив ще съдържа неопределен:

/^(\d{3})(\s)?(\w+)$/.exec('123 s ')
// Array ["123 s", "123", "", "s"]
/^(\d{3})(\s)?(\w+)$/.exec('123s ")
// Array ["123s", "123", undefined, "s"]

Референтни съвпадащи групи

Всяка съвпаднала група получава номер. $ 1 се отнася за първото, $ 2 за втория и т.н. Това ще бъде полезно, когато поговорим по-късно за подмяна на части от низ.

Назовани групи за заснемане

Това е нова функция на ES2018.

Групата може да бъде присвоена на име, а не просто да бъде назначен слот в получения масив:

const re = / (? <година> \ d {4}) - (? <месец> \ d {2}) - (? <ден> \ d {2}) /
const резултат = re.exec („02.01.2015“)
// result.groups.year === '2015';
// result.groups.month === '01';
// резултат.groups.day === '02';

Използване на match и exec без групи

Има разлика между използването на match и exec без групи: първият елемент в масива не е целият съвпадащ низ, а съответствието директно:

/hey|ho/.exec('hey ")
// [ "Хей" ]
/(hey).(ho)/.exec('hey ho ')
// [„хей хо“, „хей“, „хо“]

Групи без заснемане

Тъй като по подразбиране групите са „Заснемане на групи“, имате нужда от начин да игнорирате някои групи в получения масив. Това е възможно, като се използват групите за заснемане, които започват с (?: ...)

"123s'.match (/ ^ (\ г {3}) (: \ а) (\ w +) $ /)
//нула
'123 s'.match (/ ^ (\ d {3}) (?: \ S) (\ w +) $ /)
// Array ["123 s", "123", "s"]

Знамена

Можете да използвате следните знамена за всеки редовен израз:

  • g: съвпада с шаблона няколко пъти
  • аз: прави случая на регекс нечувствителен
  • m: активира многоредов режим. В този режим ^ и $ съответстват на началото и края на целия низ. Без това с многоредови низове те съответстват на началото и края на всеки ред.
  • u: активира поддръжка за unicode (въведен в ES6 / ES2015)
  • s: (ново в ES2018) късо за един ред, причинява. да съвпада и с нови символи на линия.

Флаговете могат да се комбинират и те се добавят в края на низ в регекс литерали:

/hey/ig.test('HEy ') // 

или като втори параметър с конструкторите на обекти RegExp:

нов RegExp ('hey', 'ig'). тест ('HEy') // 

Проверка на регекс

Имайки регекс, можете да проверите неговите свойства:

  • източник на низ на шаблона
  • многолинейно вярно с флага на m
  • глобално вярно с флага на g
  • ignoreCase true с i знамето
  • lastIndex
/^(\w{3})$/i.source // "^ (\\ d {3}) (\\ w +) $"
/^(\w{3})$/i.multiline // false
/^(\w{3})$/i.lastIndex // 0
/^(\w{3})$/i.ignoreCase // вярно
/^(\w{3})$/i.global // false

Бягството

Тези знаци са специални:

  • \
  • /
  • []
  • ()
  • {}
  • ?
  • +
  • *
  • |
  • ,
  • ^
  • $

Те са специални, защото са контролни знаци, които имат значение в модела на регулярния израз. Ако искате да ги използвате в рамките на шаблона като съвпадащи знаци, трябва да ги избягате, като предварите предварително наклонена черта:

/ ^ \\ $ /
/ ^ \ ^ $ / // /^\^$/.test('^ ') 
/ ^ \ $$ / // /^\$$/.test('$ ') 

Граници на струните

\ b и \ B ви позволяват да проверите дали низ е в началото или в края на дума:

  • \ b съответства на набор от знаци в началото или в края на думата
  • \ B съвпада с набор от знаци не в началото или в края на думата

Пример:

'Видях мечка'. Match (/ \ bbear /) // Array ["bear"]
'Видях брада'. Match (/ \ bbear /) // Array ["bear"]
'Видях брада'.match (/ \ bbear \ b /) // null
'cool_bear'.match (/ \ bbear \ b /) // null

Заменете, като използвате регулярни изрази

Вече видяхме как да проверим дали низ съдържа шаблон.

Видяхме също как да извлечем части от низ в масив, съответстващ на модел.

Нека да видим как да заменим части от низ на базата на модел.

Обектът String в JavaScript има метод за подмяна (), който може да се използва без регулярни изрази за извършване на единична подмяна на низ:

"Здравей, свят!", Се заменя ("свят", "куче")
// Здравей куче!
„Моето куче е добро куче!“ Заменете („куче“, „котка“)
// Моята котка е добро куче!

Този метод също приема редовен израз като аргумент:

"Здравей, свят!". Замести (/ свят /, "куче") // Здравей куче!

Използването на флага е единственият начин да замените няколко събития в низ във ванилов JavaScript:

„Моето куче е добро куче!“ Заменете (/ dog / g, „cat“)
// Моята котка е добра котка!

Групите ни позволяват да правим по-фантастични неща, като например да се движим около части от низ

"Здравей, свят!". Замести (/ (\ w +), (\ w +)! /, "$ 2: $ 1 !!!")
// "свят: Здравей !!!"

Вместо да използвате низ, можете да използвате функция, за да правите дори по-причудливи неща. Той ще получи редица аргументи като този, върнат от String.match (RegExp) или RegExp.exec (String), с редица аргументи, които зависят от броя на групите:

"Здравей, свят!". Замести (/ (\ w +), (\ w +)! /, (MatchedString, първо, второ) => {
  console.log (първи);
  console.log (втори);
  връщане `$ {second.toUpperCase ()}: $ {първи} !!!`
})
// "СВЕТЪТ: Здравей !!!"

лакомия

Редовни изрази се казва, че по подразбиране са алчни.

Какво означава?

Вземете този регекс:

/\$(.+)\s?/

Предполага се, че се извлича сума от долар от низ:

/\$(.+)\s?/.exec('Това струва $ 100 ') [1]
// 100

но ако имаме повече думи след числото, това изроди:

/\$(.+)\s?/.exec('Това струва $ 100 и е по-малко от $ 200 ') [1] // 100 и е по-малко от $ 200

Защо? Тъй като реджексът след знака $ съвпада с който и да е символ с. +, И той няма да спре, докато стигне до края на низа. Тогава тя приключва, защото \ s? прави крайното пространство по избор.

За да коригираме това, трябва да кажем на регекса да е мързелив и да извършим възможно най-малкото съвпадение. Можем ли да го направим с помощта на? символ след количествения показател:

/\$(.+?)\s/.exec('Това струва $ 100 и е по-малко от $ 200 ') [1]
// 100
Премахнах? след \ s. В противен случай той съвпада само с първото число, тъй като пространството не е задължително

Така, ? означава различни неща въз основа на позицията му, защото може да бъде както количествен показател, така и индикатор за мързелив режим.

Lookaheads: съвпада с низ в зависимост от това, което го следва

Използвайте? =, За да свържете низ, който е последван от конкретна подреда:

/ Roger (= Waters) /
/ Роджър (? = Води) /. Тест („Роджър е моето куче“) // невярно
/ Роджър (? = Уотърс) /. Тест („Роджър е моето куче, а Роджър Уотърс е известен музикант“)
//вярно

?! изпълнява обратната операция, като съвпада, ако низ не е последван от конкретна подреда:

/ Роджър (?! Waters) /
/ Роджър (?! Води) /. Тест ("Роджър е моето куче") // вярно
/ Роджър (?! Уотърс) /. Тест ("Роджър Уотърс е известен музикант")
// фалшив

Lookbehinds: съвпадение на низ в зависимост от това, което му предхожда

Това е функция на ES2018.

Lookaheads използват символа? = Използвайте Lookbehinds? <=.

/ (? <= Роджър) Води /
/ (? <= Роджър) Уотърс / .test ("Розовите води е моето куче")
// фалшив
/ (? <= Роджър) Уотърс / .test ("Роджър е моето куче, а Роджър Уотърс е известен музикант")
//вярно

Погледът отзад се отрича с помощта на?

/ (? 
/ (? 
/ (? 

Редовни изрази и Unicode

Знамето u е задължително при работа с Unicode низове. По-специално, това се прилага, когато може да се наложи да обработвате знаци в астрални равнини (тези, които не са включени в първите 1600 символа Unicode).

Емоджиите са добър пример, но те не са единствените.

Ако не добавите този флаг, този прост регекс, който трябва да съвпада с един знак, няма да работи, защото за JavaScript този емоджи е представен вътрешно от два знака (вижте Unicode в JavaScript):

/^.$/.test('a ') // 
/^.$/.test(' ') // 
/^.$/u.test(' ') // 

Така че, винаги използвайте флага.

Unicode, както нормалните знаци, обработва диапазоните:

/ evidencea-zSense/.test('a ') // 
/ Evidence1-9Sense/.test('1 ') // 
/ Evidence-Sense/u.test(' ') // 
/ Evidence-Sense/u.test(' ') // 

JavaScript проверява представянето на вътрешния код, така че < <, защото \ u1F436 <\ u1F43A <\ u1F98A. Проверете пълния списък на Emoji, за да получите тези кодове и да разберете реда (съвет: избраникът на macOS Emoji има някои емоджи в смесен ред, така че не разчитайте на него).

Свойството Unicode се избягва

Както видяхме по-горе, в модела на обикновен израз можете да използвате \ d, за да съответствате на която и да е цифра, \ s, за да съответствате на всеки символ, който не е бяло пространство, \ w, за да съответствате на всеки буквено-цифров символ и т.н.

Избягва се свойството Unicode е функция ES2018, която въвежда много готина функция, разширявайки тази концепция до всички символи на Unicode, въвеждащи \ p {} и отрицанието му \ P {}.

Всеки символ на Unicode има набор от свойства. Например Script определя езиковото семейство, ASCII е булева, която е вярна за ASCII символите и т.н. Можете да поставите това свойство в скобите на графиката и регексът ще провери дали това е вярно:

/^\p{ASCII}+$/u.test('abc ') // 
/^\p{ASCII}+$/u.test('ABC@ ') // 
/^\p{ASCII}+$/u.test('ABC ') // 

ASCII_Hex_Digit е друго булно свойство, което проверява дали низът съдържа само валидни шестнадесетични цифри:

//

Има много други булеви свойства, които просто проверявате, като добавите името им в скобите на графиката, включително главни, малки букви, White_Space, азбучни, емотикони и други:

/^\p{Lowercase}$/u.test('h ') // 
/^\p{Uppercase}$/u.test('H ') // 
/^\p{Emoji}+$/u.test('H ') // 
/^\p{Emoji}+$/u.test(' ') // 

В допълнение към тези двоични свойства можете да проверите някое от свойствата на unicode символи, за да съответства на конкретна стойност. В този пример проверявам дали низът е написан на гръцка или латинска азбука:

/^\p{Script=Greek}+$/u.test('ελληνικά ') // 
/^\p{Script=Latin}+$/u.test('hey ') // 

Прочетете повече за всички имоти, които можете да използвате директно върху предложението.

Примери

Да предположим, че низ има само едно число, което трябва да извлечете, / \ d + / трябва да го направите:

'Тест 123123329'.match (/ \ d + /)
// масив [„123123329“]

Съпоставете имейл адрес

Опростен подход е да проверявате символи, които не са интервали преди и след знака @, като използвате \ S:

/(\S+)@(\S+)\.(\S+)/
/(\S+)@(\S+)\.(\S+)/.exec('copesc@gmail.com ")
// evidence"copesc@gmail.com "," copesc "," gmail "," com "]

Това е опростен пример, тъй като много невалидни имейли все още са удовлетворени от този регекс.

Заснемане на текст между двойни кавички

Да предположим, че имате низ, който съдържа нещо в двойни кавички и искате да извлечете това съдържание.

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

В резултат ще намерим това, от което се нуждаем [1]:

const hello = 'Здравей "хубаво цвете"'
const result = /"( evidence^' kome*)"/.exec(hello)
// Array ["\" хубаво цвете \ "", "хубаво цвете"]

Вземете съдържанието в HTML таг

Например вземете съдържанието в span маркер, което позволява произволен брой аргументи вътре в маркера:

/]*>(.*?)<\/span>/
/]*>(.*?)<\/span>/.exec('test ")
// нула
/ <Педя \ б [^>] *> (. *?) <\ / Педя> /. EXEC ( '<педя> тест ')
// [" тест ", "тест"]
/Sense*>(.*?)<\/span>/.exec(' тест  ')
// [" тест ", "тест"]
Искате да научите JavaScript? Вземете безплатната ми електронна книга на jshandbook.com