Pull to refresh

Прививка от реальности: розовые очки для браузера

Reading time7 min
Views26K
Почему вокруг так много матерятся? Одно дело, когда на ногу падает молоток, или когда надо срочно сообщить коллеге, что он не успевает сделать макет сайта. Но в Интернете-то у автора всегда должно быть достаточно времени, чтобы подобрать красивую фразу и показать себя грамотным интеллигентным человеком с большим словарным запасом. К сожалению, редок тот случай, когда обсценная лексика действительно уместна — навскидку, один на сотню.

Некоторые владельцы форумов, чатов и блогов борются с обилием мата организационными мерами (устанавливая правила) или техническими (используя парсеры), но самый большой недостаток существующих антимат-систем — это многочисленные ложные срабатывания, порождающие удивительные неологизмы вроде заштричлен, застрапенис и скигей (кто не догадался — в оригинале было слово «скипидар»). Также скрипты (а зачастую — и сами авторы текстов) иногда заменяют буквы из середины бранных слов на звёздочки (***) или символы "#$%^", из-за чего у меня возникает подозрение, что у этих людей вместо половых органов чёрные квадратики.

Мы пойдём иным путём: пусть читатель сам решает, что он хочет видеть на экране: красочный русский мат или не менее красочный литературный русский язык. Мы разработаем расширение для браузера «У нас не матерятся», заменяющее ненормативную лексику на синонимичные литературные выражения. Основным и решающим требованием к расширению является естественность и читабельность текста после замены. Мы не хотим обеднять язык, просто изымая из него мат — мы обогащаем его, предлагаем взамен нечто большее.

В нижеследующей статье я привожу поверхностное лингвистическое исследование русского мата, а также даю краткий курс регулярных выражений в JavaScript и руководство по созданию расширений для браузера Chrome.


Часть 1. Лингвистика. Русский мат по полочкам


В русском языке есть три основных словообразующих матерных корня и несколько слов, изначально не являвшихся матерными, но в современном языке находящихся под табу. Знаменитое слово из трёх букв является наиболее продуктивным в плане словообразования словом русского мата и, вероятно, самым богатым на словоформы и производные слова в русском языке вообще. Также интересен тот факт, что выражения, образованные от него, покрывают практически все семантические поля языка. А для меня, как для разработчика, это означает, что в разном контексте эти выражения придётся заменять на разные аналоги.

Напомню, что слово в русском языке может включать в себя следующие морфемы: приставка, корень, интерфикс, суффикс, окончание. Для тех, кто не знает, интерфикс — часть слова, связывающая два или более корней (водопой, коловорот). Обязательным является наличие как минимум одного корня в слове, остальное — опционально.

Уникальной особенностью русского мата является то, что изменение одной морфемы (например, приставки) может кардинально изменить семантику слова. Более того, даже слова с одной и той же приставкой (или без неё) могут нести разную смысловую нагрузку в зависимости от суффиксов, стоящих после корня. Окончание никак не влияет на семантику, а служит лишь для обозначения отношения слова к другим членам предложения.

Всего русский язык насчитывает 70 приставок, 20 из которых — заимствованные из греческого и латинского языков (экс-, пост-, гипер-, и т.д.), а 50 — исконно русские. Заимствованные приставки с матерными словами не употребляются, либо употребляются, но настолько редко, что этим можно пренебречь. Заметим также, что каждый из рассматриваемых корней употребляется не со всеми существующими русскими приставками, что значительно сокращает количество существующих комбинаций приставка-корень. Аналогично отсеиваются многие суффиксы.

Ввиду большой разницы в семантике слов и словосочетаний, образованных из нецензурных корней с помощью различных приставок и суффиксов, каждое выражение нужно рассматривать в отдельности. Для поиска и замены мощных языковых конструкций мы будем использовать мощный инструмент — регулярные выражения.

Часть 2. Регулярные выражения


В общей сложности в текущей версии расширения получилось около 100 регулярных выражений, многие из которых похожи друг на друга, так что, приводить их все считаю бессмысленным. Подробно рассмотрю лишь некоторые шаблоны, но этого будет вполне достаточно, чтобы проиллюстрировать использование регулярок в JS вообще и мой ход мысли в данном конкретном случае. Полный набор используемых регулярок вы можете увидеть у меня в GitHub.

Введение в регулярные выражения

Регулярные выражения в JavaScript работают через специальный объект RegExp. Краткая запись регулярного выражения имеет следующий синтаксис:
/выражение/флаги

В самом простом случае между слешами может стоять жёстко заданный набор символов. Например:
/Колбаса/

Такая конструкция найдёт все вхождения подстроки «Колбаса» в тексте. Важно: регистр имеет значение, то есть, строку «колбаса» такое выражение уже не найдёт. Чтобы не учитывать регистр символов, нужно использовать флаг i:
/колбаса/i

Такая конструкция уже соответствует всем вхождениям строк «Колбаса», «колбаса», «КОЛБАСА» и «КоЛбАсА».

Мы, конечно же, хотим, чтобы все предложения в обработанном тексте начинались с заглавной буквы, а слова внутри предложения — со строчной. Поэтому мы последовательно применим к тексту оба регулярных выражения, заменив их на одно из синонимичных и столь же эмоционально окрашенных выражений. Для примера, возьмём слово «Страх».
text = text.replace(/Страх/g, randomWord(["Ужас", "Кошмар"]));
text = text.replace(/страх/i, randomWord(["ужас", "кошмар"]));

Метод строкового объекта replace() принимает два аргумента:
  1. Регулярное выражение или искомая подстрока — шаблон, по которому будет производиться поиск в документе;
  2. Строка (или функция, возвращающая строку), которой будут заменены все вхождения шаблона


Рассмотрим более сложный и более близкий к предмету обсуждения случай, когда слово имеет несколько словоформ, которые описывают одно и то же состояние/явление/объект.
Пишем:
text = text.replace(/(О|А|При)фигеть/g, "С ума сойти");
text = text.replace(/(о|а|при)фигеть/i, "с ума сойти");

Конструкция вида (A|B)C соотвествует подстрокам AC и BC. С помощью вышеприведённых строчек кода мы находим все вхождения подстрок «Офигеть», «Афигеть», «Прифигеть», а затем — делаем то же самое, но без учёта регистра.

Конструкцию (A|B) можно использовать сколько угодно раз, на любом уровне вложенности и в любой части выражения. Рассмотрим это на ещё чуть более сложном примере с множественными суффиксами: распространённое наречие «хреново». Это слово имеет огромное количество суффиксных производных: «хреновасто», «хреновенько» и даже «хреновастенько». При составлении регулярного выражения для более грубого аналога мы должны учитывать чередование е/ё в корне и тот факт, что «ё» на письме часто заменяется буквой «е». Сейчас нам это не надо, так что, составим регулярное выражение, учитывающее все эти формы:
/Хренов(аст|)(еньк|)о/

и произведём соответствующую замену:
text = text.replace(/Хренов(аст|)(еньк|)о/g, randomWord(["Плохо", "Печально", "Ужасно", "Кошмарно", "Уныло"]));
text = text.replace(/Хренов(аст|)(еньк|)о/i, randomWord(["плохо", "печально", "ужасно", "кошмарно", "уныло"]));

Аналогично, но учитывая возможные окончания поступим с прилагательным «хреновый» — негативным оценочным суждением по отношению к качеству объекта речи:
text = text.replace(/Хренов(аст|)(еньк|)(ы(й|х|е|м)|о(е|го|й|му)|ая|ий)/g, "Низкого качества");
text = text.replace(/хренов(аст|)(еньк|)(ы(й|х|е|м)|о(е|го|й|му)|ая|ий)/i, "низкого качества");

Путём нехитрого сложения и умножения мы можем сосчитать, что приведённое в примере регулярное выражение
/хренов(аст|)(еньк|)(ы(й|х|е|м)|о(е|го|й|му)|ая|ий)/

может найти 40 возможных форм слова. А в случае с использованием матерного корня и, как следствие, чередованием е/ё — 80.

Если мы хотим найти и заменить слово «Балет» на «Опера» или «Мюзикл», но при этом не изменять слова «Кордебалет», «Арбалет» и другие, то мы должны сделать это так:
text = text.replace(/(\s|^)Балет/g, randomWord(["Опера", " Мюзикл"]));
text = text.replace(/(\s|^)балет/i, randomWord([" опера", "мюзикл"]));

спецсимвол ^ означает начало входных данных. Таким образом, мы находим слово «Балет», идущее после пробела или в самом начале обыскиваемой строки.
Да, сюрприз, конструкция \b, использующаяся для обозначения границы слова, не работает с кириллицей, хотя границу слова, написанного латинскими буквами, определяет на отлично.
Также полезным может оказаться спецсимвол $, обозначающий конец входных данных.

Часть 3. Расширение Google Chrome


Расширение будет состоять из трёх основных частей:
  1. обязательный файл манифеста manifest.json, в котором описаны основные параметры расширения;
  2. собственно, javascript-файл, который будет выполнять всю работу;
  3. Иконки размера 128х128, 48х48 и 16х16;

Манифест прост донельзя.
{
	"manifest_version": 2,
	"name": "У нас не матерятся",
	"version": "1.0",
	"icons": { "16": "icon32.png",
           "48": "icon128.png",
          "128": "icon128.png" },
	"description": "Заменяет нецензурную брань на синонимичные литературные выражения.",
	"content_scripts": 
	[
		{
			"matches": ["*://*/*"],
			"js": ["content_script.js"],
			"run_at": "document_end"
		}
	]
}

О синтаксисе лучше всего узнать всё, что надо, из первоисточника.

Скрипт запускается сразу после загрузки страницы (мы явно указали в манифесте «run_at»: «document_end»), его тело состоит из трёх функций:
  • walk(node) — функция, осуществляющая рекурсивный обход нодов HTML-документа. Если принятый ею нод содержит текст, она передаёт нод в функцию makeItCultural();
  • makeItCultural(textNode) — функция, осуществляющая замену подстрок по шаблонам согласно регулярным выражениям. В случае, если существует несколько вариантов замены, эти варианты передаются в виде массива в функцию randomWord();
  • randomWord(words) — функция, принимающая массив и возвращающая его случайный элемент;


На странице chrome://extensions/ ставим галочку «Developer mode», жмём кнопку «Load unpacked extension...» и выбираем папку с нашим расширением. После этого тестируем его, правим, после каждой правки не забываем нажать на ссылку Reload возле нашего расширения.

Чтобы публиковать расширения в Webstore, нужно один раз заплатить 5 долларов. Вообще, весь этот процесс отлично описан в другой хорошей статье на Хабре, так что, не вижу смысла повторяться.

Заключение


Получившееся в итоге расширение заставляет играть новыми красками ЖЖ Артемия Лебедева и интервью Сергея Шнурова, но ему пока не по зубам Большой Петровский Загиб (ссылку приводить не буду — погуглите сами, оно того стоит). Тема русского мата бесконечно глубока и по ней написан не один десяток академических работ. Однажды физики докажут (или опровергнут) теорию струн, инженеры построят персональные квантовые компьютеры, у каждого встречного в кармане будет гаджет, питающийся от холодного ядерного реактора, а русский мат так и не будет изучен до конца.

Постарался написать максимально доступно, так, чтобы было понятно и нескучно всем читателям, а получилось как обычно — для школьников младших и средних классов, сидящих на героине.

Готовое расширение можно установить из Chrome Webstore, исходники найти на GitHub.

Список источников


Javascript.ru — Регулярные выражения;
Chrome Web Store — Google Developers
Алексей Плуцер-Сарно. Большой словарь мата, Том I и Том II

P.S. Прошу не материться в комментариях. Не хочу становиться виновником массовых хабрарепрессий.
Tags:
Hubs:
Total votes 67: ↑51 and ↓16+35
Comments43

Articles