Ранее мы уже делали редактор текста с автозаменой. Тогда исправлением текста занималась нейросеть Яндекса, а мы лишь сделали для этого оболочку. Минус подхода в том, что какой-то механизм решает за вас, какие слова правильные, а какие нужно заменить. Давайте это исправим.
На этой неделе мы делаем орфокорректор, который будет работать по вашим правилам. Вы сами решите, что и как заменять в тексте, и сами пропишете нужные правила. И при необходимости сможете их дополнять.
Это полезно, например, для деловой переписки: менеджер пишет «Засуньте себе в... свои правки, это полная чушь, я не буду это исправлять». А алгоритм заменяет это на «Коллеги! Спасибо за конструктивную критику! Нам потребуется уточнить некоторые моменты, прежде чем мы возьмем эти замечания в работу».
Логика будет такая:
- берём код прошлого проекта;
- выкидываем из него всё, что связано с нейронкой;
- на это место закидываем свой код, который меняет слова и буквы, как нужно нам;
- проверяем результат.
Регулярные выражения
Чтобы заменить одно слово на другое, в JavaScript используют функцию replace()
, в которой пишут сначала, что нужно найти, а затем — на что заменить.
Например, s = s.replace('кто', 'что');
заменит в строке слово «кто» на слово «что». Это удобно и вроде как нам подходит, но смотрите, какие есть минусы у этого подхода:
- заменяется только первое найденное слово;
- если «Кто» будет написано с большой буквы, наша функция его уже не найдёт;
- имя «Гектор» превратится в «Гечтор», а казахстанский город Актобе превратится в «Ачтобе».
Чтобы такой ерунды не происходило, используют регулярные выражения.
Регулярное выражение можно представить как маленький встроенный язык программирования, заточенный под поиск и обработку текста. У этого языка есть специальные команды, которые определяют поведение алгоритма. Эти команды встраиваются в обычный текст, и получаются регулярные выражения. Несколько примеров:
код — найдёт просто эти буквы внутри любого слова, в том числе «нашкодил».
\Bкод\B — найдёт слово «код», окружённое пробелами или в начале предложения. Маркер \b означает границу слова. И тут важное замечание: в JavaScript есть маркер \b, который обозначает границу слова на латинице и \B, который обозначает границу слова в остальных алфавитах. А, например, на Python токен \b работает на всех языках.
\B[Кк]од\B — найдёт «Код» или «код» как отдельное слово. Буквы внутри квадратных скобок он воспринимает как «или». [Кк] означает «Либо заглавная К, либо строчная к».
\B[Кк]о\W\B — найдёт «Код», «Кот», «Ком», «Кое», и так же со строчной буквы. Токен \w означает любую букву слова на латинице, а \W — на всех языках.
\B[Кк]од\w{0,3}\B — найдёт «Код», «Кода», «Коду», «Кодом», «Кодами» плюс то же самое со строчных букв. В фигурных скобках указывают число возможных повторов предыдущего знака. \w{0,3} означает «любая буква, повторённая от нуля до трёх раз».
[\w-_\.]+@([\w-]+\.)+\w{2,5} — хе-хе-хе. Эта регулярка помогает найти в тексте электронные адреса. Плюсик означает «один или больше», а скобки используются, чтобы прикрепить плюсик к набору символов. Переводится так: «Сколько-то последовательностей из букв, дефисов, подчёркиваний и точек, потом собака, потом сколько-то последовательностей из букв, дефисов и точек, потом от двух до пяти букв. Если хотите, чтобы срабатывали домены типа .apartments, придется ставить в конце десятку.
Обратите внимание: плюс в регулярках означает не «одно сложить с другим», а «то, что написано передо мной, повторить один или много раз».
Если разметка регулярных выражений вам сейчас кажется сложноватой — не печальтесь. Мы сделаем об этом отдельную статью. В реальности там всё очень легко, нужно лишь выучить несколько специальных букв.
А пока что полный список основных команд и свойств регулярных выражений можно посмотреть на Хабре или JavaScript.ru.
Регулярки в JavaScript
Чтобы JavaScript понял, что мы используем регулярные выражения вместо конкретного слова при поиске, используют две косые черты:
/регулярное выражение/флаги
Флаги — это специальные команды для выражений, которые дают им суперспособности: i — нечувствительность к регистру или g — поиск по всей строке.
Вернёмся к случаю с Гектором. Чтобы при замене «кто» на «что» не возникало таких ситуаций, добавим в функцию замены регулярное выражение:
s = s.replace(/\Bкто\B/gi, ' что ');
Вы уже знаете, что \B означает границы слова.
Флаг g означает, что мы пройдём всю строку и найдём все слова «кто», чтобы их заменить. А флаг i делает наш поиск нечувствительным к регистру — он найдёт и «Кто», и «ктО», и даже «КТО».
Теперь, когда мы это знаем, мы можем менять наш текст как угодно:
- просто менять один текст на другой:
s = s.replace(/\Bкто\B/gi, 'что');
- менять один символ на другой или на целое словосочетание
s = s.replace(/\Bя\B/gi, 'неизвестно кто');
- Делать Каждое Слово Большим:
s = s.replace(/\s[а-я]/g, u => u.toUpperCase());
Сразу оговоримся: всё это работает предельно тупо, и, если мы сказали делать замену на «что», то везде будет замена на «что» со строчной. Если мы хотим сохранить заглавные буквы, то замена будет в два шага:
s = s.replace(/\Bкто\B/g, 'что');
s = s.replace(/\BКто\B/g, 'Что');
Теперь у нас есть всё что нужно, чтобы сделать свой редактор с вежливыми автозаменами.
Готовим каркас
Чтобы было быстрее, возьмём старый код и уберём из него всё, что работало с нейрокорректором.
Страница уже готова, в ней только меняем имя скрипта.
<!DOCTYPE html>
<html>
<!-- служебная часть -->
<head>
<!-- заголовок страницы -->
<title>Корректор с автозаменой</title>
<!-- настраиваем служебную информацию для браузеров -->
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style type="text/css">
/* общие параметры страницы*/
body {
text-align: center;
margin: 10;
font-family: Verdana, Arial, sans-serif;
font-size: 16px;
}
/* заголовок */
h1 {
font-size: 48px;
text-align: center;
}
/* поле ввода */
.text {
min-height: 300px;
min-width: 500px;
border: solid;
border-width: 1px;
text-align: left;
-webkit-box-shadow: 6px 10px 9px 0px rgba(0, 0, 0, 0.75);
-moz-box-shadow: 6px 10px 9px 0px rgba(0, 0, 0, 0.75);
box-shadow: 6px 10px 9px 0px rgba(0, 0, 0, 0.75);
}
</style>
<!-- закрываем служебную часть страницы -->
</head>
<body>
<!-- подключаем jQuery -->
<script type="text/javascript" src="
http://yastatic.net/jquery/2.1.3/jquery.min.js
"></script>
<!-- подключаем наш скрипт с регулярными выражениями -->
<script type="text/javascript" src="regexp.js"></script>
<!-- заголовок на странице -->
<h1>Орфокорректор</h1>
<!-- пояснительный текст -->
<p>Напишите что угодно. Регулярные выражения сами всё исправят.</p>
<!-- поле ввода текста -->
<textarea id="text_field" class="text"></textarea>
</body>
<!-- конец всей страницы -->
</html>
Почистили скрипт:
// обработчик нажатия на клавиши
document.addEventListener('keydown', function (e) {
// если нажат пробел или энтер
if ((e.keyCode == 32) || (e.keyCode == 13)) {
// тут будет наш код
}
});
Настраиваем правила автозамены в скрипте
Первое, что нам понадобится, — переменная, чтобы хранить текст из поля ввода, который мы будем обрабатывать. Её мы поставим в самое начало скрипта:
// строковая переменная, чтобы хранить текст из поля ввода var s = '';
Теперь в обработчик нажатия вместо нейроскрипта помещаем наш код. Принцип такой: берём переменную, применяем на неё одно правило, переходим к следующему. При каждом нажатии пробела или энтера наш код пройдёт по всем правилам и заменит нужные слова и символы:
// запоминаем содержимое поля ввода текста в отдельной переменной
s = $('#text_field').val();
// блок автозамены, первое слово — что ищем, второе — на что заменяем
s = s.replace(/\Bкое кто\B/gi, 'кое-кто');
s = s.replace(/\Bкое что\B/gi, 'кое-что');
s = s.replace(/\Bв течени\W\B/gi, 'в течение');
s = s.replace(/\Bвообщем\W\B/gi, 'в общем');
s = s.replace(/\Bвобще\W\B/gi, 'вообще');
s = s.replace(/\Bдичайше\B/gi, 'в высшей степени');
s = s.replace(/\Bоху\Wть\B/gi, 'Надо же');
s = s.replace(/\Bоху\Wть\B/gi, 'подозрительно');
s = s.replace(/\Bя\sе\W+л\sтак\W+(работу|задачи)\B/gi, 'подобная постановка вопроса в высшей степени возмутительна');
s = s.replace(/\Bоху\Wл\W?\b/gi, 'позволяет себе лишнего');
s = s.replace(/\Bоху\Wвший\B/gi, 'недопустимый');
s = s.replace(/\Bоху\Wвшая\B/gi, 'вызывающая опасения');
s = s.replace(/\B[иИ]ди(те)?\s(на|в)\s?\W{3,5}\B/gi, 'заранее спасибо');
s = s.replace(/\Bп[адир]{4}с\W?/gi, 'уважаемые коллеги');
s = s.replace(/\BТЗ\B/gi, 'техническое задание');
s = s.replace(/\Bчушь\sполная\B/gi, 'недостоверные сведения');
s = s.replace(/\Bнужно\sвчера\B/gi, 'это срочно');
s = s.replace(/\Bполная\sж[аоп]{3}\B/gi, 'присутствуют существенные проблемы');
// отправляем отредактированные строки назад в поле ввода
$('#text_field').val(s);
// строковая переменная, чтобы хранить текст из поля ввода
var s = '';
// обработчик нажатия на клавиши
document.addEventListener('keydown', function (e) {
// если нажат пробел или энтер
if ((e.keyCode == 32) || (e.keyCode == 13)) {
// запоминаем содержимое поля ввода текста в отдельной переменной
s = $('#text_field').val();
// блок автозамены, первое слово — что ищем, второе — на что заменяем
s = s.replace(/\Bкое кто\B/gi, 'кое-кто');
s = s.replace(/\Bкое что\B/gi, 'кое-что');
s = s.replace(/\Bв течени\W\B/gi, 'в течение');
s = s.replace(/\Bвообщем\W\B/gi, 'в общем');
s = s.replace(/\Bвобще\W\B/gi, 'вообще');
s = s.replace(/\Bдичайше\B/gi, 'в высшей степени');
s = s.replace(/\Bоху\Wть\B/gi, 'Надо же');
s = s.replace(/\Bоху\Wть\B/gi, 'подозрительно');
s = s.replace(/\Bя\sе\W+л\sтак\W+(работу|задачи)\B/gi, 'подобная постановка вопроса в высшей степени возмутительна');
s = s.replace(/\Bоху\Wл\W?\b/gi, 'позволяет себе лишнего');
s = s.replace(/\Bоху\Wвший\B/gi, 'недопустимый');
s = s.replace(/\Bоху\Wвшая\B/gi, 'вызывающая опасения');
s = s.replace(/\B[иИ]ди(те)?\s(на|в)\s?\W{3,5}\B/gi, 'заранее спасибо');
s = s.replace(/\Bп[адир]{4}с\W?/gi, 'уважаемые коллеги');
s = s.replace(/\BТЗ\B/gi, 'техническое задание');
s = s.replace(/\Bчушь\sполная\B/gi, 'недостоверные сведения');
s = s.replace(/\Bнужно\sвчера\B/gi, 'это срочно');
s = s.replace(/\Bполная\sж[аоп]{3}\B/gi, 'присутствуют существенные проблемы');
// отправляем отредактированные строки назад в поле ввода
$('#text_field').val(s);
}
});
Что дальше
Возможности регулярок практически безграничны. На них работают автокорректоры, автотипографы и все те алгоритмы, которым нужно механически обработать текст по правилам.
Сейчас наш алгоритм очень простой: он не учитывает рода и числа, не понимает значений слов, а текст после него лучше перечитать, чтобы не слетели склонения и спряжения. Но всё это решаемо — достаточно просто написать много новых правил.