正如“我的博客文章…”中所解释的
前几天,我正在查找Javascript的字符串替换()函数的docs (因为,不,我不记得它语法的变化!)在如释重负的同时,我也注意到它可以用回调代替字符串来代替它的替换参数。多酷啊?我认为这对于CFML的
reReplace()函数也是很方便的,所以决定在提出增强请求之前先看看它是如何工作的,并在过程中编写了一个合理的UDF,它可以通过概念的证明来完成业务。我会提交replaceWithCallback()转到CFLib,但我对自我认可的东西很谨慎,所以我也会通过社区驱动的代码评审,让您对它投下更多的注意。
/**
@hint Analogous to reReplace()/reReplaceNoCase(), except the replacement is the result of a callback, not a hard-coded string
@string The string to process
@regex The regular expression to match
@callback A UDF which takes arguments match (substring matched), found (a struct of keys pos,len,substring, which is subexpression breakdown of the match), offset (where in the string the match was found), string (the string the match was found within)
@scope Number of replacements to make: either ONE or ALL
@caseSensitive Whether the regex is handled case-sensitively
*/
string function replaceWithCallback(required string string, required string regex, required any callback, string scope="ONE", boolean caseSensitive=true){
if (!isCustomFunction(callback)){ // for CF10 we could specify a type of "function", but not in CF9
throw(type="Application", message="Invalid callback argument value", detail="The callback argument of the replaceWithCallback() function must itself be a function reference.");
}
if (!isValid("regex", scope, "(?i)ONE|ALL")){
throw(type="Application", message="The scope argument of the replaceWithCallback() function has an invalid value #scope#.", detail="Allowed values are ONE, ALL.");
}
var startAt = 1;
while (true){ // there's multiple exit conditions in multiple places in the loop, so deal with exit conditions when appropriate rather than here
if (caseSensitive){
var found = reFind(regex, string, startAt, true);
}else{
var found = reFindNoCase(regex, string, startAt, true);
}
if (!found.pos[1]){ // ie: it didn't find anything
break;
}
found.substring=[]; // as well as the usual pos and len that CF gives us, we're gonna pass the actual substrings too
for (var i=1; i <= arrayLen(found.pos); i++){
found.substring[i] = mid(string, found.pos[i], found.len[i]);
}
var match = mid(string, found.pos[1], found.len[1]);
var offset = found.pos[1];
var replacement = callback(match, found, offset, string);
string = removeChars(string, found.pos[1], found.len[1]);
string = insert(replacement, string, found.pos[1]-1);
if (scope=="ONE"){
break;
}
startAt = found.pos[1] + len(replacement);
}
return string;
}测试/样本:
function reverseEm(required string match, required struct found, required numeric offset, required string string){
return reverse(match);
}
input = "abCDefGHij";
result = replaceWithCallback(input, "[a-z]{2}", reverseEm, "ALL", true);
writeOutput(input & "<br>" & result & "<br><hr>");
function oddOrEven(required string match, required struct found, required numeric offset, required string string){
var oddOrEven = match MOD 2 ? "odd" : "even";
return match & " (#oddOrEven#)";
}
input = "1, 6, 12, 17, 20";
result = replaceWithCallback(input, "\d+", oddOrEven, "ALL", true);
writeOutput(input & "<br>" & result & "<br><hr>");
function messWithUuid(required string match, required struct found, required numeric offset, required string string){
var firstEight = reverse(found.substring[2]);
var nextFour = lcase(found.substring[3]);
var secondSetOfFour = "<strong>" & found.substring[4] & "</strong>";
var lastBit = reReplace(found.substring[5], "\d", "x", "all");
return "#firstEight#-#nextFour#-#secondSetOfFour#-#lastBit#";
}
input = "#createUuid()#,XXXXXXXXX-XXXX-XXXX-XXXXXXXXXXXXXXXX,#createUuid()#";
result = replaceWithCallback(input, "([0-9A-F]{8})-([0-9A-F]{4})-([0-9A-F]{4})-([0-9A-F]{16})", messWithUuid, "ALL", true);
writeOutput(input & "<br>" & result & "<br><hr>");发布于 2014-11-29 10:25:08
如果这个想法是要在JavaScript的String.replace()上建模这个函数,那么就有一个"bug“。在JavaScript中,在执行全局替换时,传递给回调的最后一个参数始终是原始未修改的字符串,而不是可能已经对其应用了一些替换的字符串,这就是您编写的。传递给回调的偏移量也应该是这样的:它们应该指示原始位置。
记住,我是CFML…的完全新手我会对循环做一些小的调整。
通过将while (true) { … }转换为do { … } while (scope == "ALL");,可以避免不优雅的break;语句之一。
定义match时,只需重用found.substring[1],并保存对mid()的调用。
在下面的几行中,您可以重用offset而不是found.pos[1],以略微提高可读性。
var lengthDiff = 0;
var origString = string;
do {
var found = caseSensitive ? REFind(regex, string, startAt, true)
: REFindNoCase(regex, string, startAt, true);
if (!found.pos[1]) { // ie: it didn't find anything
break;
}
found.substring=[]; // as well as the usual pos and len that CF gives us, we're gonna pass the actual substrings too
for (var i=1; i <= arrayLen(found.pos); i++){
found.substring[i] = mid(string, found.pos[i], found.len[i]);
}
var match = found.substring[1];
var offset = found.pos[1];
var length = found.len[1];
var replacement = callback(match, found, offset - lengthDiff, origString);
string = removeChars(string, offset, length);
string = insert(replacement, string, offset-1);
lengthDiff += len(replacement) - length;
startAt = offset + len(replacement);
} while (scope == "ALL");https://codereview.stackexchange.com/questions/28646
复制相似问题