Ръководство за избягване на символи в Java RegExps

1. Общ преглед

API за регулярни изрази в Java, java.util.regex се използва широко за съвпадение на шаблони. За да откриете повече, можете да следвате тази статия.

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

2. Специални символи RegExp

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

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

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

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

Този тест показва, че за даден входен низ foof, когато шаблонът foo . ( foo, завършващ с точков знак) е съчетан, той връща стойност true, което показва, че съвпадението е успешно.

@Test public void givenRegexWithDot_whenMatchingStr_thenMatches() { String strInput = "foof"; String strRegex = "foo."; assertEquals(true, strInput.matches(strRegex)); }

Може би се чудите защо съвпадението е успешно, когато във входния String няма символ на точка (.) ?

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

Да кажем, че не искаме да третираме символа точка (.) С уникалното му значение. Вместо това искаме да се тълкува като точков знак. Това означава, че в предишния пример не искаме да оставим шаблона foo. да има съвпадение във входния низ.

Как бихме се справили с подобна ситуация? Отговорът е: трябва да избягаме от точката (.), Така че специалното му значение да бъде игнорирано.

Нека да го разгледаме по-подробно в следващия раздел.

3. Избягали герои

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

Нека да видим какви са те:

  1. Предшествайте метасимвола с наклонена черта (\)
  2. Приложете метасимвол с \ Q и \ E

Това просто означава, че в примера, който видяхме по-рано, ако искаме да избягаме от точковия знак, трябва да поставим обратна наклонена черта преди точковия знак. Като алтернатива можем да поставим точковия знак между \ Q и \ E.

3.1. Избягване с помощта на обратна наклонена черта

Това е една от техниките, които можем да използваме, за да избягаме от метасимволите в регулярен израз. Обаче знаем, че обратната наклонена черта е изходен знак и в Java String литерали. Следователно трябва да удвоим обратната наклонена черта, когато го използваме, за да предшества който и да е символ (включително самия символ \).

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

@Test public void givenRegexWithDotEsc_whenMatchingStr_thenNotMatching() { String strInput = "foof"; String strRegex = "foo\\."; assertEquals(false, strInput.matches(strRegex)); }

Тук точният знак е избягван, така че съвпадението просто го третира като точка и се опитва да намери шаблон, който завършва с точката (т.е. foo. ).

В този случай той връща false, тъй като във входния String няма съвпадение за този модел.

3.2. Избягване с помощта на \ Q & \ E

Като алтернатива можем да използваме \ Q и \ E, за да избягаме от специалния символ. \ Q показва, че всички символи до \ E трябва да се избяга и \ E означава, че трябва да се сложи край на екраниране, която стартира с \ Q .

Това просто означава, че каквото и да е между \ Q и \ E, ще бъде избегнато.

В теста, показан тук, split () на класа String прави съвпадение, използвайки предоставения му регулярен израз.

Нашето изискване е да разделим входния низ от символа pipe (|) на думи. Следователно, ние използваме шаблон на регулярен израз, за ​​да го направим.

Символът на тръбата е метасимвол, който трябва да бъде екраниран в регулярния израз.

Тук избягването се извършва чрез поставяне на символа на тръбата между \ Q и \ E :

@Test public void givenRegexWithPipeEscaped_whenSplitStr_thenSplits() \\E"; assertEquals(4, strInput.split(strRegex).length); 

4. Методът Pattern.quote (String S)

Методът Pattern.Quote (String S) в клас java.util.regex.Pattern преобразува даден модел на регулярен израз String в буквален модел String. Това означава, че всички метасимволи във входния низ се третират като обикновени символи.

Използването на този метод би било по-удобна алтернатива от използването на \ Q & \ E, тъй като обгръща дадения низ с тях.

Нека видим този метод в действие:

@Test public void givenRegexWithPipeEscQuoteMeth_whenSplitStr_thenSplits() bar

В този бърз тест се използва методът Pattern.quote () , за да се избегне дадения модел на регулярно изражение и да се трансформира в Stral литерал. С други думи, той избягва всички метасимволи, присъстващи в модела на регулярния израз за нас. Тя се прави подобна работа, за да \ Q & \ E .

Символът на тръбата се избягва от метода Pattern.quote () и split () го интерпретира като String литерал, с който разделя входа.

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

Трябва да отбележим, че Pattern.quote затваря целия блок с една изходна последователност. Ако искахме да избягаме от символи поотделно, ще трябва да използваме алгоритъм за замяна на символи.

5. Допълнителни примери

Нека да разгледаме как работи методът replaceAll () на java.util.regex.Matcher .

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

Представете си, че имаме вход с множество повторения на символа $ . Резултатът, който искаме да получим, е същият низ със символа $, заменен с £.

Този тест показва как се предава моделът $, без да се избягва:

@Test public void givenRegexWithDollar_whenReplacing_thenNotReplace() { String strInput = "I gave $50 to my brother." + "He bought candy for $35. Now he has $15 left."; String strRegex = "$"; String strReplacement = "£"; String output = "I gave £50 to my brother." + "He bought candy for £35. Now he has £15 left."; Pattern p = Pattern.compile(strRegex); Matcher m = p.matcher(strInput); assertThat(output, not(equalTo(m.replaceAll(strReplacement)))); }

Тестът твърди, че $ не е правилно заменен с £ .

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

@Test public void givenRegexWithDollarEsc_whenReplacing_thenReplace() { String strInput = "I gave $50 to my brother." + "He bought candy for $35. Now he has $15 left."; String strRegex = "\\$"; String strReplacement = "£"; String output = "I gave £50 to my brother." + "He bought candy for £35. Now he has £15 left."; Pattern p = Pattern.compile(strRegex); Matcher m = p.matcher(strInput); assertEquals(output,m.replaceAll(strReplacement)); }

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

6. Заключение

В тази статия разгледахме избягащите символи в регулярни изрази в Java.

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

Както винаги, изходният код, свързан с тази статия, може да бъде намерен в GitHub.