16.05.2009Эволюция алгоритма замены в строке ActionScript
В последнее время моя работа в текущем проекте заключается в отладке, оптимизации и отлове багов. Поэтому в самой работе практически не встречается того, о чём можно было бы здесь написать. Но на помощь пришла гугл-группа ruFlash и комьюнити молодых программистов. :)
Задача
Один из участников попросил составить выражение для удаления из текста ссылок с определенным текстом внутри целиком. Например, в выражении:
var str:String = '<a href="somelink">_some text_</a> ';
str += 'More text! ';
str += '<a href="anotherlink">**remove me**</a> ';
str += '<a href="yetanotherlink"><s>another text</s></a>';
Нужно удалить целиком ссылку, содержащую фразу remove me.
Понятно, что первое приходящее в голову выражение /<a.+?remove me.*?</a>/ захватит две первые ссылки. И «жадность» не поможет, т.к. поиск осуществляется по порядку, и, найдя первый <a, выражение не остановится до самого remove me.
Решение номер один
Поскольку к концу недели отладки и пересмотра одного и того же кода голова моя не была готова что-то изобретать, я последовал пути, предлагавшемуся одним из ответивших, слегка его доделав:
var re0:RegExp = /<a[^>]+>[^a]*remove me.*?<\/a>/g;
trace(str.replace(re0, "!removed!"));
Недостаток его очевиден. Хотя для приведенного примера он работает, но всё-таки, может и отказать, если встретит a между > и remove me. Например:
str += '<a href="anotherlink">eh! ah? **remove me**</a> ';
Решение номер два (рекурсивное)
Поскольку к этому моменту мозг ещё не расслабился окончательно и не готов был отказаться от выбранного способа думать о задаче, второе решение, пришедшее сразу за первым, было значительно сложнее.
Оно использовало возможность подсовывать функцию в качестве аргумента. Вот оно:
var re1:RegExp = /<a(.+?remove me.*?<\/a>)/g;
var replacer1:Function = function():String {
var s:String = arguments[1].toString();
if (s.indexOf("<a") > 0) {
return "<a" + s.replace(re1, replacer1);
} else {
return "!removed!";
}
}
trace(str.replace(re1, replacer1));
Здесь речь идёт о том, чтобы в группе (см. скобки), следующей после <a проверять наличие ещё одного <a. И в случае его наличия запускать ту же процедуру замены, но уже на группе.
Довольный собой, я запостил своё решение в ruFlash и поехал домой. По дороге домой мозг окончательно расслабился, и я смог увидеть задачу в отрыве от способа думать, который я выбрал изначально. И мне пришло
Решение номер три
Зачем городить рекурсию, когда можно просто перебирать все ссылки и заменять (удалять) только те, что нужно?
var re2:RegExp = /<a[^>]+>(.+?)<\/a>/g;
var replacer2:Function = function():String {
var s:String = arguments[1].toString();
if (s.indexOf("remove me") > 0) {
return "!removed!";
} else {
return arguments[0];
}
}
trace(str.replace(re2, replacer2));
Это ли не чудесно?
Выводы
- Решайте задачи.
- Решив (или не решив), записывайте то, что получилось, покажите кому-нибудь. Это позволит выкинуть решение из головы.
- Если есть решение лучше, то оно придет на освободившееся место.