前天寫了一個"JScript版的String.Format方法",本來都已經使用遍曆法來替換格式化字串了,結果卻使用了RegExp和substr之類的憋腳方法。後來
問題男很熱心的給出了一個全掃描的方案,更鬱悶的是由於自己對測試的認識不足,居然只使用了期望資料來測試代碼,搞得bug一大坨。
於是在問題男的建議基礎上,自己又作了一些最佳化,把整個替換操作一次掃描高定。代碼如下:
// StringHelper.Format('{0}, {2}, {1}', 'abc', 'def', 'ghi');
// return "abc, ghi, def".
StringHelper.Format = function(format)
{
if ( arguments.length == 0 )
{
return '';
}
if ( arguments.length == 1 )
{
return String(format);
}
var strOutput = '';
for ( var i=0 ; i < format.length-1 ; )
{
if ( format.charAt(i) == '{' && format.charAt(i+1) != '{' )
{
var index = 0, indexStart = i+1;
for ( var j=indexStart ; j <= format.length-2 ; ++j )
{
var ch = format.charAt(j);
if ( ch < '0' || ch > '9' ) break;
}
if ( j > indexStart )
{
if ( format.charAt(j) == '}' && format.charAt(j+1) != '}' )
{
for ( var k=j-1 ; k >= indexStart ; k-- )
{
index += (format.charCodeAt(k)-48)*Math.pow(10, j-1-k);
}
var swapArg = arguments[index+1];
strOutput += swapArg;
i += j-indexStart+2;
continue;
}
}
strOutput += format.charAt(i);
i++;
}
else
{
if ( ( format.charAt(i) == '{' && format.charAt(i+1) == '{' )
|| ( format.charAt(i) == '}' && format.charAt(i+1) == '}' ) )
{
i++
}
strOutput += format.charAt(i);
i++;
}
}
strOutput += format.substr(i);
return strOutput;
}
相對上一版本的改進:
1、不再使用RegExp和substr|substring;
2、一次掃描完成所有替換和轉義;
3、修複了對"}}"掃描未做正確處理的bug;
4、修複了取格式化條目編號可能出錯的bug。
新的測試資料:
alert(StringHelper.Format('{0}', 'abc'));
alert(StringHelper.Format('{0}}{0}, {{2}, {1}}', 'abc', 'def', 'ghi'));
alert(StringHelper.Format('{000}, {{{{2}}}}, {001}', 'abc', 'def', 'ghi'));
alert(StringHelper.Format('{{0}}\r\n2, {2}\r\n, {1}', 'abc', 'def', 'ghi'));
alert(StringHelper.Format('{0}{0}{0}, {0{1}0}, {{{{{2}}}', 'abc', 'def'));
測試所得結果:
No.1 alert: abc
No.2 alert: {0}abc, {2}, {1}
No.3 alert: abc, {{2}}, def
No.4 alert: {0}
2, ghi
, def
No.5 alert: abcabcabc, {0def0}, {{{2}}
繼續徵集更最佳化方案:)
BTW: 代碼中使用了一個JavaScript的Syntax Sugar來減少代碼,你看迴圈:
for ( var i=0 ; i < format.length-1 ; )
{
if ( ... )
{
// . . .
}
else
{
format.charAt(i+i)
}
}
顯然i+1已經溢出了字串的長度了,不過這時根本不用管它,JavaScript會返回一個undefined,這個值完全不會影響我們的程式邏輯。如果是C#就需要囉裡囉唆的去判斷i+1是不是小於format.length,否則就Index Out of Range Exception了。