В заключение главы рассмотрим задачу с использованием регулярок. Представьте, что мы пишем программу, собирающую сведения о наших врагах через интернет в автоматическом режиме. (Всю программу писать не будем, только ту часть, которая читает файл с настройками. Извините.) Файл выглядит так:
>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), следующий:
• Пустые строки и строки, начинающиеся с точки с запятой, игнорируются.
• Строки, заключённые в квадратные скобки, начинают новую секцию.
• Строки, содержащие алфавитно-цифровой идентификатор, за которым следует >=
, добавляют настройку в данной секции.
• Всё остальное – неверные данные.
Наша задача – преобразовать такую строку в массив объектов, каждый со свойством >name
и массивом настроек. Для каждой секции нужен один объект, и ещё один – для глобальных настроек сверху файла.
Так как файл надо разбирать построчно, неплохо начать с разбиения файла на строки. Для этого в главе 6 мы использовали >string.split("\n")
. Некоторые операционки используют для перевода строки не один символ >\n
, а два — >\r\n
. Так как метод >split
принимает регулярки в качестве аргумента, мы можем делить линии при помощи выражения >/\r?\n/
, разрешающего и одиночные >\n
и >\r\n
между строками.
>function parseINI(string) {
> // Начнём с объекта, содержащего настройки верхнего уровня
> var currentSection = {name: null, fields: []};
> var categories = [currentSection];
> string.split(/\r?\n/).forEach(function(line) {
> var match;
> if (/^\s*(;.*)?$/.test(line)) {
> return;
> } else if (match = line.match(/^\[(.*)\]$/)) {
> currentSection = {name: match[1], fields: []};
> categories.push(currentSection);
> } else if (match = line.match(/^(\w+)=(.*)$/)) {
> currentSection.fields.push({name: match[1],
> value: match[2]});
> } else {
> throw new Error("Строчка '" + line + "' содержит неверные данные.");
> }
> });
> return categories;
>}
Код проходит все строки, обновляя объект текущей секции (current section). Сначала он проверяет, можно ли игнорировать строчку, при помощи регулярки