Экранирование, специальные символы
Как мы уже видели, обратная косая черта \ используется для обозначения классов символов, например \d . Это специальный символ в регулярных выражениях (как и в обычных строках).
Есть и другие специальные символы, которые имеют особое значение в регулярном выражении. Они используются для более сложных поисковых конструкций. Вот полный перечень этих символов: [ ] \ ^ $ . | ? * + ( ) .
Не надо пытаться запомнить этот список: мы разберёмся с каждым из них по отдельности, и таким образом вы выучите их «автоматически».
Экранирование символов
Допустим, мы хотим найти буквально точку. Не «любой символ», а именно точку.
Чтобы использовать специальный символ как обычный, добавьте к нему обратную косую черту: \. .
Это называется «экранирование символа».
alert( "Глава 5.1".match(/\d\.\d/) ); // 5.1 (совпадение!) alert( "Глава 511".match(/\d\.\d/) ); // null ("\." - ищет обычную точку)
Круглые скобки также являются специальными символами, поэтому, если нам нужно использовать именно их, нужно указать \( . В приведённом ниже примере ищется строка «g()» :
alert( "function g()".match(/g\(\)/) ); // "g()"
Если мы ищем обратную косую черту \ , это специальный символ как в обычных строках, так и в регулярных выражениях, поэтому мы должны удвоить её.
alert( "1\\2".match(/\\/) ); // '\'
Косая черта
Символ косой черты ‘/’ , так называемый «слэш», не является специальным символом, но в JavaScript он используется для открытия и закрытия регулярного выражения: /. шаблон. / , поэтому мы должны экранировать его.
Вот как выглядит поиск самой косой черты ‘/’ :
alert( "/".match(/\//) ); // '/'
С другой стороны, если мы не используем короткую запись /. / , а создаём регулярное выражение, используя new RegExp , тогда нам не нужно экранировать косую черту:
alert( "/".match(new RegExp("/")) ); // находит /
new RegExp
Если мы создаём регулярное выражение с помощью new RegExp , то нам не нужно учитывать / , но нужно другое экранирование.
Например, такой поиск не работает:
let regexp = new RegExp("\d\.\d"); alert( "Глава 5.1".match(regexp) ); // null
Аналогичный поиск в примере выше с /\d\.\d/ вполне работал, почему же не работает new RegExp(«\d\.\d») ?
Причина в том, что символы обратной косой черты «съедаются» строкой. Как вы помните, обычные строки имеют свои специальные символы, такие как \n , и для экранирования используется обратная косая черта.
Вот как воспринимается строка «\d.\d»:
alert("\d\.\d"); // d.d
Строковые кавычки «съедают» символы обратной косой черты для себя, например:
- \n – становится символом перевода строки,
- \u1234 – становится символом Юникода с указанным номером,
- …А когда нет особого значения: как например для \d или \z , обратная косая черта просто удаляется.
Таким образом, new RegExp получает строку без обратной косой черты. Вот почему поиск не работает!
Чтобы исправить это, нам нужно удвоить обратную косую черту, потому что строковые кавычки превращают \\ в \ :
let regStr = "\\d\\.\\d"; alert(regStr); // \d\.\d (теперь правильно) let regexp = new RegExp(regStr); alert( "Глава 5.1".match(regexp) ); // 5.1
Итого
- Для поиска специальных символов [ ] \ ^ $ . | ? * + ( ) , нам нужно добавить перед ними \ («экранировать их»).
- Нам также нужно экранировать / , если мы используем /. / (но не new RegExp ).
- При передаче строки в new RegExp нужно удваивать обратную косую черту: \\ для экранирования специальных символов, потому что строковые кавычки «съедят» одну черту.
инициализировать слэш «\»
Spring MVC. Контроллер, убирающий слэш
В проекте Spring MVC есть страница, лежащая в папке /pages/index1.jsp. В конфиге есть такие.
Где лучше инициализировать
Есть ли разница, и в чем: инициализировать объекты класса после объявления или в конструкторе?
Инициализировать поля значениями по-умолчанию
В Хорстмане написанно, что это плохая практика полагаться на автоматическую инициализацию полей.
Инициализировать переменную в статитческом блоке
Как проинициализировать переменную в статическом блоке.\ Пример public class Solution < .
Почему необходимо инициализировать переменную?
Программа по нахождению последней ненулевой цифры факториала. import java.util.Scanner; public.
Java: Экранирующие последовательности
Мы хотим показать диалог Матери Драконов со своим ребенком:
- Are you hungry? - Aaaarrrgh!
Если вывести на экран строку с таким текстом:
System.out.println("- Are you hungry?- Aaaarrrgh!");
- Are you hungry?- Aaaarrrgh!
Не то, что мы хотели. Строки расположены друг за другом, а не одна ниже другой. Нам нужно как-то сказать интерпретатору «нажать на Enter» — сделать перевод строки после вопросительного знака. Это можно сделать, используя символ перевода строки: \n :
System.out.println("- Are you hungry?\n- Aaaarrrgh!");
- Are you hungry? - Aaaarrrgh!
\n — это специальный символ. В литературе его часто обозначают как LF (Line Feed). Возможно, вы сейчас подумали, что это опечатка, ведь здесь мы видим два символа \ и n , но это не так. С точки зрения компьютера — это один невидимый символ перевода строки:
// Мы это не изучали, но вы должны знать правду // Ниже код, который возвращает длину строки "a".length(); // 1 "\n".length(); // 1 . "\n\n".length(); // 2 .
Почему так сделано? \n — всего лишь способ записать символ перевода строки, но сам перевод строки по своему смыслу – это один символ, правда, невидимый.
Именно поэтому и возникла такая задача. Нужно было как-то представить его на клавиатуре. А поскольку количество знаков на клавиатуре ограничено и отдано под самые важные, то все специальные символы реализуются в виде таких обозначений.
Символ перевода строки не является чем-то специфичным для программирования. Все, кто хоть раз печатал на компьютере, использовал перевод строки, нажимая на Enter.
Во многих редакторах есть опция, позволяющая включить отображение невидимых символов. Эта опция помогает понять, где они находятся, хотя это всего лишь схематичное отображение, ведь у этих невидимых символов нет графического представления:
- Привет!¶ - О, привет!¶ - Как дела?
Устройство, которое выводит соответствующий текст, учитывает этот символ. Например, принтер при встрече с LF протаскивает бумагу вверх на одну строку, а текстовый редактор переносит весь последующий текст ниже, также на одну строку.
\n — это пример экранирующей последовательности (escape sequence). Их еще называют управляющими конструкциями. Хотя таких символов не один десяток, в программировании часто встречаются всего несколько.
Кроме перевода строки, к таким символам относятся:
- Табуляция — разрыв, получаемый при нажатии на кнопку Tab
- Возврат каретки (только в Windows)
Программистам часто нужно использовать перевод строки \n для правильного форматирования текста:
System.out.println("Gregor Clegane\nDunsen\nPolliver\nChiswyck");
На экран выведется:
Gregor Clegane Dunsen Polliver Chiswyck
Обратите внимание на следующие моменты:
- Не имеет значения, что стоит перед или после \n : символ или пустая строка. Перевод будет обнаружен и выполнен в любом случае
- Помните, что строка может содержать один символ или вообще ноль символов. А еще строка может содержать только \n . Проанализируйте следующий пример:
System.out.println("\n"); System.out.println("Dunsen");
Здесь мы сначала выводим строку «перевод строки», а потом делаем вывод обыкновенной строки. Программа выведет на экран:
Dunsen
Почему перед строкой Dunsen появилось две пустые строки, а не одна? Дело в том, что System.out.println() при выводе значения автоматически добавляет в конец символ перевода строки. Таким образом, один перевод строки мы указали явно, передав этот символ экранирующей последовательности аргументом в функцию, а второй перевод строки добавлен самой функцией автоматически. Еще пример кода:
System.out.println("Polliver"); System.out.println("Gregor Clegane"); System.out.println(); System.out.println("Chiswyck\n"); System.out.println("Dunsen");
Вывод будет таким:
Polliver Gregor Clegane Chiswyck Dunsen
System.out.println("Joffrey loves using \\n");
на экран выйдет:
Joffrey loves using \n
Небольшое, но важное замечание про Windows. В Windows для перевода строк по умолчанию используется \r\n — это связано с историческими причинами. Такая комбинация хорошо работает только в Windows, но создает проблемы при переносе в другие системы: например, когда в команде разработчиков есть пользователи как Windows, так и Linux.
Дело в том, что последовательность \r\n имеет разную трактовку в зависимости от выбранной кодировки. Поэтому в среде разработчиков принято всегда использовать \n без \r , так как LF всегда трактуется одинаково и отлично работает в любой системе. Не забудьте настроить ваш редактор на использование \n .
Задание
Напишите программу, которая выводит на экран:
- Did Joffrey agree? - He did. He also said "I love using \n".
При этом программа использует только один System.out.println() , но результат на экране должен выглядеть в точности как показано выше.
Упражнение не проходит проверку — что делать?
Если вы зашли в тупик, то самое время задать вопрос в «Обсуждениях». Как правильно задать вопрос:
- Обязательно приложите вывод тестов, без него практически невозможно понять что не так, даже если вы покажете свой код. Программисты плохо исполняют код в голове, но по полученной ошибке почти всегда понятно, куда смотреть.
В моей среде код работает, а здесь нет
Тесты устроены таким образом, что они проверяют решение разными способами и на разных данных. Часто решение работает с одними входными данными, но не работает с другими. Чтобы разобраться с этим моментом, изучите вкладку «Тесты» и внимательно посмотрите на вывод ошибок, в котором есть подсказки.
Мой код отличается от решения учителя
Это нормально , в программировании одну задачу можно выполнить множеством способов. Если ваш код прошел проверку, то он соответствует условиям задачи.
В редких случаях бывает, что решение подогнано под тесты, но это видно сразу.
Прочитал урок — ничего не понятно
Создавать обучающие материалы, понятные для всех без исключения, довольно сложно. Мы очень стараемся, но всегда есть что улучшать. Если вы встретили материал, который вам непонятен, опишите проблему в «Обсуждениях». Идеально, если вы сформулируете непонятные моменты в виде вопросов. Обычно нам нужно несколько дней для внесения правок.
Кстати, вы тоже можете участвовать в улучшении курсов: внизу есть ссылка на исходный код уроков, который можно править прямо из браузера.
Полезное
Slash и backslash: вехи на пути
Возникновение слеша относят к временам Римской империи. На ранних стадиях современности, во Фрактуре [1], которая была широко распространена по всей Европе в средневековье, слеш (/) использовался вместо запятой, в то время как двойной слеш (//) использовался вместо тире. Двойной слеш, в конечном счете, превратился в символ похожий на знак равенства (=), а позже был еще больше упрощен до тире или дефиса [2].
Backslash
Боб Бемер ввел обратный слеш (\) в набор символов ASCII, 18 сентября 1961 года, как результат изучения частоты использования символов встречающихся в частности в программах на ALGOL’е. Тогда же вместе с обратным слешем в стандарт были включены и квадратные скобки.
В частности \ был введен, чтобы булевы операторы ALGOL’a AND и OR могли быть представлены с помощью ASCII символов как «/\» и «\/» соответственно [3,4].
Как же вышло, что исторически православный слеш заменился на свое зеркальное отображение, введенное как вспомогательный символ специально для уже мертвого языка?
Русскоязычная Википедия по этому говорит вот что:
В операционных системах DOS и Windows фирмы Microsoft и их аналогах других разработчиков, обратная косая используется для разделений имён директорий (каталогов) при указании пути к файлу. Прямая косая, применяемая для этого в Unix не могла быть использована в MS-DOS, потому что уже была задействована для указания ключей командной строки (оставшегося в наследство от СР/M, где MS-DOS команда «dir /w» писалась как «dir/w») [5].
Так как такое объяснение меня не слишком удовлетворило, пришлось найти статью «Why is the DOS path character «\»?» [6], которая вполне утолила моё любопытство. Вольный перевод избранных частей в моем исполнении:
То что символ «/» конфликтовал с разделителем пути другой относительно популярной ОС не был связан напрямую с разработчиками – в конце концов, DOS не поддерживал директорий, просто файлы в одном корневом каталоге.
Для MS-DOS 2.0 (в котором появился поддержка каталогов), дизайнеры DOSа выбрали гибридную версию – у них уже были имена дисков в наследство от DOS 1.0, поэтому разработчикам пришлось их использовать. И в дополнение к именам дисков они решили использовать *nix-style метод определения иерархии каталогов — вместо использования каталога в имени файла (как это было сделано в VMS и DEC-20), они просто сделали каталог и имя файла неотъемлемыми частями пути. Но с этим была проблема. Невозможно было использовать разделитель пути *nix (/), по той причине что слэш уже использовался как разделитель ключей.
Что им было делать? Они конечно могли использовать «.» как в DEC, но точка уже использовалась как разделитель между именем файла и расширением. Поэтому они выбрали наилучший вариант из оставшихся — символ «\», который был визуально похож на «/».Таким вот образом и был выбран символ «\» для разделения путей в DOS.
Кстати есть небольшой секрет про MS-DOS. Разработчики DOS не были довольны таким положением дел – они использовали Xenix [7] для почты и прочих вещей, поэтому они были знакомы со структурой *nix команд. Поэтому они добавили в ОС возможность принимать в качестве разделителя путей как «/» так и «\» (это работает и сегодня, кстати – попробуйте выполнить «notepad c:/boot.ini» под XP (если ваш пользователь имеет права админа)). Дальше — больше. Они добавили недокументированный системный вызов, чтобы изменить символ разделителя ключей. И обновили утилиты, чтобы те поддерживали этот флаг. Они даже добавили в config.sys параметр, SWITCHAR, который позволит пользователю установить разделитель ключей на «-«. Таким образом можно было превратить MS-DOS в *nix-style ОС, используя «-switch», и пути с разделителем «/».
Собственно к чему это все?
Меня побудила разобраться в этой теме следующая ситуация.
Была поставлена задача — наладить систему отчетов для автоматизированных тестов. Тесты у нас используются двух видов – Selenium (функциональные) и Jmeter (нагрузочные). Собственно в этом не было ничего сложного — для этих целей существует вполне себе open-source проект под названием logging selenium [8] и plugin для maven — chronos [9]. Настроив всё и протестировав отчеты локально, принялся за интеграцию с нашей CI — TeamCity. Вот тут-то меня и ждала та самая неожиданность, которая стала поводом для написания этой статьи.
После выполнения всех тестов отчет о Selenium-тестах имел следующий вид:
Всё отлично отображалось, и никаких отличий от локальной версии не было.
Но вот отчет, который отобразился для Jmeter-тестов, воодушевления не вызывал:
Напрочь отсутствовали все изображения на странице.
После просмотра исходного кода страницы стало понятно, что во всем виноват backslash. Ссылки на изображения были указаны в таком формате:
Справедливости ради стоит заметить, что изображения отсутствовали в Firefox, но прекрасно отображались в IE. Хотя если бы IE не отображал ресурсы в URI которых встречается обратный слеш, как разделитель пути для Windows, то в, и не без того подпорченной, репутации индийских программистов образовалась бы еще одна брешь.
В общем, после недолгих раздумий стало понятно, что где-то внутри chronos’a используется File.separator, который и повинен в появлении обратного слеша в путях ресурсов и проблемах с отображением графиков в отчетах. Скачивание исходников плагина и исправление соответствующей строки, в которой формировался URL, решило проблему и дало возможность насладится прекрасными графиками, которые генерируются на основе результатов выполнения JMeter-тестов.
В заключении хотелось бы предостеречь от бездумного использования нативного File.separator – это не всегда приводит к кроссплатформенности, а в некоторых случаях даже может стать причиной появления новых багов. Обычный же слеш работает в Windows (зачастую), работает в *nix, Java и наконец его стоит уважать хотя бы по праву старшинства, так как он на полторы тысячи лет старше своего зазеркального брата.
P.S.: Спасибо хабраюзеру AlexanderYastrebov за внесение ясности относительно истории создания C.
