Выбрать главу

var name = "dea+hl[]rd";

var text = "Этот dea+hl[]rd всех достал.";

var escaped = name.replace(/[^\w\s]/g, "\\$&");

var regexp = new RegExp("\\b(" + escaped + ")\\b", "gi");

console.log(text.replace(regexp, "_$1_"));

// → Этот _dea+hl[]rd_ всех достал.

Метод search

Метод indexOf нельзя использовать с регулярками. Зато есть метод search, который как раз ожидает регулярку. Как и indexOf, он возвращает индекс первого вхождения, или -1, если его не случилось.

console.log(" word".search(/\S/));

// → 2

console.log(" ".search(/\S/));

// → -1

К сожалению, никак нельзя задать, чтобы метод искал совпадение, начиная с конкретного смещения (как это можно сделать с indexOf). Это было бы полезно.

Свойство lastIndex

Метод exec тоже не даёт удобного способа начать поиск с заданной позиции в строке. Но неудобный способ даёт.

У объекта регулярок есть свойства. Одно из них – source, содержащее строку. Ещё одно – lastIndex, контролирующее, в некоторых условиях, где начнётся следующий поиск вхождений.

Эти условия включают необходимость присутствия глобальной опции g, и то, что поиск должен идти с применением метода exec. Более разумным решением было бы просто допустить дополнительный аргумент для передачи в exec, но разумность – не основополагающая черта в интерфейсе регулярок JavaScript.

var pattern = /y/g;

pattern.lastIndex = 3;

var match = pattern.exec("xyzzy");

console.log(match.index);

// → 4

console.log(pattern.lastIndex);

// → 5

Если поиск был успешным, вызов exec обновляет свойство lastIndex, чтобы оно указывало на позицию после найденного вхождения. Если успеха не было, lastIndex устанавливается в ноль – как и lastIndex у только что созданного объекта.

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

var digit = /\d/g;

console.log(digit.exec("here it is: 1"));

// → ["1"]

console.log(digit.exec("and now: 1"));

// → null

Ещё один интересный эффект опции g в том, что она меняет работу метода match. Когда он вызывается с этой опцией, вместо возврата массива, похожего на результат работы exec, он находит все вхождения шаблона в строке и возвращает массив из найденных подстрок.

console.log("Банан".match(/ан/g));

// → ["ан", "ан"]

Так что поосторожнее с глобальными переменными-регулярками. В случаях, когда они необходимы – вызовы replace или места, где вы специально используете lastIndex – пожалуй и все случаи, в которых их следует применять.

Циклы по вхождениям

Типичная задача – пройти по всем вхождениям шаблона в строку так, чтобы иметь доступ к объекту match в теле цикла, используя lastIndex и exec.

var input = "Строчка с 3 числами в ней... 42 и 88.";

var number = /\b(\d+)\b/g;

var match;

while (match = number.exec(input))

  console.log("Нашёл ", match[1], " на ", match.index);

// → Нашёл 3 на 10

//   Нашёл 42 на 29

//   Нашёл 88 на 34

Используется тот факт, что значением присвоения является присваиваемое значение. Используя конструкцию match = re.exec(input) в качестве условия в цикле while, мы производим поиск в начале каждой итерации, сохраняем результат в переменной, и заканчиваем цикл, когда все совпадения найдены.

Разбор INI файлы

В заключение главы рассмотрим задачу с использованием регулярок. Представьте, что мы пишем программу, собирающую сведения о наших врагах через интернет в автоматическом режиме. (Всю программу писать не будем, только ту часть, которая читает файл с настройками. Извините.) Файл выглядит так:

searchengine=http://www.google.com/search?q=$1

spitefulness=9.7

; перед комментариями ставится точка с запятой

; каждая секция относится к отдельному врагу

[larry]

fullname=Larry Doe

type=бычара из детсада

website=http://www.geocities.com/CapeCanaveral/11451

[gargamel]

fullname=Gargamel

type=злой волшебник

outputdir=/home/marijn/enemies/gargamel

Точный формат файла (который довольно широко используется, и обычно называется INI), следующий: