JS字符串格式化函数 string.format

时间:2021-05-30 02:00:19

原生JS写的仿C#的字符串format函数,在此基础上又增加了便于JS使用的字面量对象参数,且字面量对象可以嵌套调用。

参照C#中的规则,调用的时候会检测字符串格式,如果字符串格式不规范,或者传入的参数为null或undefined,则抛出异常,并且加入了console.trace,方便查找错误。

有了这个format函数,js拼接字符串的时候就方便多了。

源代码:

!function () {
if (typeof String.prototype.format === 'function') {
return false;
}
var isUndefined = function(o){
return typeof o === 'undefined';
},
isNullOrUndefined = function(o){
return typeof o === 'undefined' || o === null;
},
throwFormatError = function (msg, str, args) {
try {
console.trace();
if (typeof str !== 'undefined') {
console.log('str:\r\n\t', str, '\r\nargs:\r\n\t', args);
}
} catch (e) { }
throw new Error(msg);
},
formatNumberZero = function (arv, arn) {
var arr = [], idx = arn.length - 1;
for (var i = arv.length - 1; i >= 0; i--) {
arr.push(arv[i] === '0' ? (idx >= 0 ? arn[idx] : arv[i]) : (function () { ++idx; return arv[i]; })());
idx--;
}
for (var i = idx; i >= 0; i--) {
arr.push(arn[i]);
}
arr = arr.reverse();
return arr.join('');
},
formatNumberSwitch = function (v, f, n, dn, err, str, args) {
var fu = f.toUpperCase();
if (typeof dn[1] === 'undefined' && (fu === 'C' || fu === 'F')) {
dn[1] = '';
v += n > 0 ? '.' : '';
}
switch (fu) {
case 'C':
vc = '¥' + v;
var m = dn[0].length % 3;
if (dn[0].length > 3) {
v = vc.substr(0, m + 1);
var pos = m + 1;
while (pos < dn[0].length - 1) {
v += ',' + vc.substr(pos, 3);
pos += 3;
}
v += vc.substr(pos);
} else {
v = vc;
}
for (var i = 0, c = n - (dn[1]).length; i < c; i++) { v += '0'; }
break;
case 'D':
if (/([.])/g.test(v)) {
throwFormatError(err[3], str, args);
}
for (var i = 0, c = n - ('' + v).length; i < c; i++) { v = '0' + v; }
break;
case 'E':
var n = dn[0].length - 1, fn = parseInt(('' + v).substr(0, 1), 10), num = Math.pow(10, n), e = Math.pow(10, 5);
var cn = (Math.round((v - num) / num * e) / e + '').split('.')[1]||'', ln = '';
for (var i = ('' + cn).length; i < 5; i++) { cn += '0'; }
for (var i = ('' + n).length; i < 3; i++) { n = '0' + n; }
v = fn + '.' + cn + f + '+' + n;
break;
case 'F':
for (var i = 0, c = n - dn[1].length; i < c; i++) { v += '0'; }
break;
}
return v;
},
formatNumber = function (mv, v, err, str, args) {
if (!/[:]/g.test(mv)) {
return v;
}
var isNum = typeof v === 'number', sc = mv.match(/(:)/g).length;
if (sc > 1) {
if (isNum) {
var nv = Math.round(v, 10), pos = mv.indexOf(':'), arv = mv.substr(pos + 1).split(''), arn = ('' + nv).split('');
v = formatNumberZero(arv, arn);
} else {
v = mv.substr(mv.indexOf(':') + 1);
}
} else if (isNum) {
var ss = mv.split(':')[1], p1 = /([CDEFG])/ig, p2 = /([A-Z])/ig, p3 = /^([CDEFG][\d]+)$/ig, p4 = /^([A-Z]{1}[\d]+)$/ig;
if ((ss.length === 1 && p1.test(ss)) || (ss.length >= 2 && p3.test(ss))) {
var f = ss.substr(0, 1), n = parseInt(ss.substr(1), 10) || (f.toUpperCase() === 'D' ? 0 : 2), dn = ('' + v).split('.');
v = formatNumberSwitch(v, f, n, dn, err, str, args);
} else if ((ss.length === 1 && p2.test(ss)) || (ss.length >= 2 && p4.test(ss))) {
throwFormatError(err[3], str, args);
} else if (/([0]+)/g.test(ss)) {
var nv = Math.round(v, 10), arv = ss.split(''), arn = ('' + nv).split('');
v = formatNumberZero(arv, arn);
} else {
v = ss;
}
}
return v;
},
distillObjVal = function (key, obj, err, vals) {
var v;
if (typeof obj[key] !== 'undefined') {
v = obj[key];
} else if (key.indexOf('.') > 0 || key.indexOf('|') > 0) {
//嵌套对象,格式: obj.key.key|dv(默认值,因某些key可能不存在或允许为空)
var arr = key.split('|'), dv = arr[1], ks = arr[0].split('.'), o = obj;
//console.log('o: ', o, ', ks: ', ks, ', dv: ', dv);
for (var i in ks) {
if(typeof o === 'object'){
o = o[ks[i]], v = o;
}
if (typeof o === 'undefined') {
v = typeof dv != 'undefined' ? dv : throwFormatError(err[0], s, vals);
}
}
} else {
throwFormatError(err[0], s, vals);
}
return v;
}; String.prototype.format = function (args) {
var s = this, vals = [], rst = [], pattern = /({|})/g, ms = s.match(pattern);
if (null === ms) {
return s.toString() || s;
}
var err = ['输入字符串的格式不正确。', '索引(从零开始)必须大于或等于零,且小于参数列表的大小。',
'值不能为null(或undefined)。', '格式说明符无效。']; if (arguments.length > 1) {
for (var i = 0, c = arguments.length; i < c; i++) {
if (arguments[i] !== undefined && arguments[i] !== null) {
vals.push(arguments[i]);
} else {
var err = err[2] + '第' + (i + 1) + '个参数值为:' + arguments[i];
throwFormatError(err, s, args);
}
}
} else if (Object.prototype.toString.call(args) === '[object Array]') {
vals = args;
} else if (args != undefined && args != null) {
vals.push(args);
}
if (ms.length % 2 !== 0) {
throwFormatError(err[0], s, vals);
}
var matchs = s.match(/({+[-\d]+(:[\D\d]*?)*?}+)|({+([\D]*?|[:\d]*?)}+)|({+([\w\.\|]*?)}+)|([{]{1,2}[\w]*?)|([\w]*?[}]{1,2})/g);
if (null === matchs) {
return s.toString() || s;
}
var len = vals.length, mc = matchs.length, isObject = typeof vals[0] === 'object', obj = isObject ? vals[0] : {}; for (var i = 0; i < mc; i++) {
var m = matchs[i], mv = m.replace(pattern, ''), p = s.indexOf(m), idx = parseInt(mv, 10);
var c = /{/g.test(m) ? m.match(/{/g).length : 0, d = /}/g.test(m) ? m.match(/}/g).length : 0;
if ((c + d) % 2 != 0) {
throwFormatError(err[0], s, vals);
}
var m2 = m.replace(/{{/g, '{').replace(/}}/g, '}');
var odd = c % 2 != 0 || d % 2 != 0, single = c <= 2 && d <= 2; if (!isNaN(idx)) {
var v = formatNumber(mv, vals[idx], err, s, vals);
if (typeof v === 'boolean' && !v) {
return false;
}
if (/^-\d$/g.test(mv) && odd) {
throwFormatError(err[0], s, vals);
} else if (idx >= len) {
throwFormatError(err[1], s, vals);
} else if (typeof v === 'undefined' || v === null) {
throwFormatError(err[2], s, vals);
}
rst.push(s.substr(0, p) + (c > 1 || d > 1 ? (c % 2 != 0 || d % 2 != 0 ? m2.replace('{' + idx + '}', v) : m2) : v));
} else if (odd) {
if (c === 1 && d === 1) {
if (!isObject || !single) {
throwFormatError(err[0], s, vals);
}
v = distillObjVal(mv, obj, err, vals);
rst.push(s.substr(0, p) + (c > 1 || d > 1 ? (c % 2 !== 0 || d % 2 !== 0 ? m2.replace('{' + idx + '}', v) : m2) : v));
} else {
var mcs = m2.match(/({[\w\.\|]+})/g);
if (mcs != null && mcs.length > 0) {
rst.push(s.substr(0, p) + m2.replace(mcs[0], distillObjVal(mcs[0].replace(/({|})/g, ''), obj, err)));
} else {
throwFormatError(err[0], s, vals);
}
}
} else {
rst.push(s.substr(0, p) + m2);
}
s = s.substr(p + m.length);
}
rst.push(s); return rst.join('');
}; String.Format = String.format = function (s) {
if (typeof s === 'string') {
var a = [], c = arguments.length;
for (var i = 1; i < c; i++) {
a.push(arguments[i]);
}
return s.format(a);
}
throwFormatError((typeof o) + '.format is not a function');
};
}();

压缩后的代码:

eval(function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('!y(){6(q 11.14.T===\'y\'){u 1x}4 l=y(o){u q o===\'G\'},1M=y(o){u q o===\'G\'||o===L},x=y(a,b,c){1I{1r.1D();6(q b!==\'G\'){1r.1B(\'1P:\\r\\n\\t\',b,\'\\r\\1R:\\r\\n\\t\',c)}}1Q(e){}1N 1A 1S(a);},16=y(a,b){4 c=[],B=b.7-1;H(4 i=a.7-1;i>=0;i--){c.I(a[i]===\'0\'?(B>=0?b[B]:a[i]):(y(){++B;u a[i]})());B--}H(4 i=B;i>=0;i--){c.I(b[i])}c=c.1O();u c.1s(\'\')},1p=y(v,f,n,a,b,d,g){4 h=f.1o();6(q a[1]===\'G\'&&(h===\'C\'||h===\'F\')){a[1]=\'\';v+=n>0?\'.\':\'\'}1F(h){Y\'C\':U=\'¥\'+v;4 m=a[0].7%3;6(a[0].7>3){v=U.z(0,m+1);4 j=m+1;1H(j<a[0].7-1){v+=\',\'+U.z(j,3);j+=3}v+=U.z(j)}9{v=U}H(4 i=0,c=n-(a[1]).7;i<c;i++){v+=\'0\'}W;Y\'D\':6(/([.])/g.J(v)){x(b[3],d,g);}H(4 i=0,c=n-(\'\'+v).7;i<c;i++){v=\'0\'+v}W;Y\'E\':4 n=a[0].7-1,1k=1a((\'\'+v).z(0,1),10),15=Q.1q(10,n),e=Q.1q(10,5);4 k=(Q.17((v-15)/15*e)/e+\'\').K(\'.\')[1]||\'\',1E=\'\';H(4 i=(\'\'+k).7;i<5;i++){k+=\'0\'}H(4 i=(\'\'+n).7;i<3;i++){n=\'0\'+n}v=1k+\'.\'+k+f+\'+\'+n;W;Y\'F\':H(4 i=0,c=n-a[1].7;i<c;i++){v+=\'0\'}W}u v},1t=y(a,v,b,c,d){6(!/[:]/g.J(a)){u v}4 e=q v===\'1G\',1v=a.P(/(:)/g).7;6(1v>1){6(e){4 g=Q.17(v,10),1w=a.R(\':\'),V=a.z(1w+1).K(\'\'),12=(\'\'+g).K(\'\');v=16(V,12)}9{v=a.z(a.R(\':\')+1)}}9 6(e){4 h=a.K(\':\')[1],1i=/([1j])/X,1h=/([A-Z])/X,1n=/^([1j][\\d]+)$/X,1l=/^([A-Z]{1}[\\d]+)$/X;6((h.7===1&&1i.J(h))||(h.7>=2&&1n.J(h))){4 f=h.z(0,1),n=1a(h.z(1),10)||(f.1o()===\'D\'?0:2),1m=(\'\'+v).K(\'.\');v=1p(v,f,n,1m,b,c,d)}9 6((h.7===1&&1h.J(h))||(h.7>=2&&1l.J(h))){x(b[3],c,d);}9 6(/([0]+)/g.J(h)){4 g=Q.17(v,10),V=h.K(\'\'),12=(\'\'+g).K(\'\');v=16(V,12)}9{v=h}}u v},18=y(a,b,c,d){4 v;6(q b[a]!==\'G\'){v=b[a]}9 6(a.R(\'.\')>0||a.R(\'|\')>0){4 e=a.K(\'|\'),19=e[1],13=e[0].K(\'.\'),o=b;H(4 i 1z 13){6(q o===\'1b\'){o=o[13[i]],v=o}6(q o===\'G\'){v=q 19!=\'G\'?19:x(c[0],s,d);}}}9{x(c[0],s,d);}u v};11.14.T=y(a){4 s=1C,8=[],O=[],1c=/({|})/g,1d=s.P(1c);6(L===1d){u s.1e()||s}4 b=[\'输入字符串的格式不正确。\',\'索引(从零开始)必须大于或等于零,且小于参数列表的大小。\',\'值不能为L(或G)。\',\'格式说明符无效。\'];6(M.7>1){H(4 i=0,c=M.7;i<c;i++){6(M[i]!==G&&M[i]!==L){8.I(M[i])}9{4 b=b[2]+\'第\'+(i+1)+\'个参数值为:\'+M[i];x(b,s,a);}}}9 6(1J.14.1e.1K(a)===\'[1b 1L]\'){8=a}9 6(a!=G&&a!=L){8.I(a)}6(1d.7%2!==0){x(b[0],s,8);}4 e=s.P(/({+[-\\d]+(:[\\D\\d]*?)*?}+)|({+([\\D]*?|[:\\d]*?)}+)|({+([\\w\\.\\|]*?)}+)|([{]{1,2}[\\w]*?)|([\\w]*?[}]{1,2})/g);6(L===e){u s.1e()||s}4 f=8.7,1u=e.7,1f=q 8[0]===\'1b\',1g=1f?8[0]:{};H(4 i=0;i<1u;i++){4 m=e[i],S=m.N(1c,\'\'),p=s.R(m),B=1a(S,10);4 c=/{/g.J(m)?m.P(/{/g).7:0,d=/}/g.J(m)?m.P(/}/g).7:0;6((c+d)%2!=0){x(b[0],s,8);}4 g=m.N(/{{/g,\'{\').N(/}}/g,\'}\');4 h=c%2!=0||d%2!=0,1y=c<=2&&d<=2;6(!1T(B)){4 v=1t(S,8[B],b,s,8);6(q v===\'1U\'&&!v){u 1x}6(/^-\\d$/g.J(S)&&h){x(b[0],s,8);}9 6(B>=f){x(b[1],s,8);}9 6(q v===\'G\'||v===L){x(b[2],s,8);}O.I(s.z(0,p)+(c>1||d>1?(c%2!=0||d%2!=0?g.N(\'{\'+B+\'}\',v):g):v))}9 6(h){6(c===1&&d===1){6(!1f||!1y){x(b[0],s,8);}v=18(S,1g,b,8);O.I(s.z(0,p)+(c>1||d>1?(c%2!==0||d%2!==0?g.N(\'{\'+B+\'}\',v):g):v))}9{4 j=g.P(/({[\\w\\.\\|]+})/g);6(j!=L&&j.7>0){O.I(s.z(0,p)+g.N(j[0],18(j[0].N(/({|})/g,\'\'),1g,b)))}9{x(b[0],s,8);}}}9{O.I(s.z(0,p)+g)}s=s.z(p+m.7)}O.I(s);u O.1s(\'\')};11.1V=11.T=y(s){6(q s===\'1W\'){4 a=[],c=M.7;H(4 i=1;i<c;i++){a.I(M[i])}u s.T(a)}x((q o)+\'.T 1X 1Y a y\');}}();',62,123,'||||var||if|length|vals|else|||||||||||||||||typeof||||return|||throwFormatError|function|substr||idx|||||undefined|for|push|test|split|null|arguments|replace|rst|match|Math|indexOf|mv|format|vc|arv|break|ig|case|||String|arn|ks|prototype|num|formatNumberZero|round|distillObjVal|dv|parseInt|object|pattern|ms|toString|isObject|obj|p2|p1|CDEFG|fn|p4|dn|p3|toUpperCase|formatNumberSwitch|pow|console|join|formatNumber|mc|sc|pos|false|single|in|new|log|this|trace|ln|switch|number|while|try|Object|call|Array|isNullOrUndefined|throw|reverse|str|catch|nargs|Error|isNaN|boolean|Format|string|is|not'.split('|'),0,{}))

  

示例:

//常规格式化
console.log('abc'.format(20));

console.log('{0:D4}'.format(20));

console.log(String.format('{0:D4}', 20));
console.log(String.Format('{0:e4}', 20));

var str = '你好:{0},这是用{0}写的一个仿C#的{1}函数';

console.log(str.format(['JS', 'format']));
console.log(str.format('JS', 'format'));

console.log("{0}{{正则{0:F5}{{表达式}}{1:F4}".format(123.5, 12.5))

console.log("{0}{{正则{0:F5}{{表达式}}{1:0D12}".format(123.5, 2))

console.log("{0:00:00:00}".format(1234567));

console.log("{0:0000年00月00日 00:00:00}".format(20160824172215));

console.log("{0:0000年00月00日}".format(20160824));

console.log("{0:0DF5}".format(1234567));
console.log("{0:0000}".format(2));

console.log('<tr lang="{{id:{0},pid:{1},level:{2}}}">'.format([1, 2, 0]));

//增加了字面量对象参数,这个字面量对象参数必须放在参数的第0个位置,如果加了字面量对象参数,则数字参数索引必须从1开始

var data = { id: 123, name: '张三', obj: { num: 321, con: { val: 'acc' } } };
var s = 'Id:{id}, Val:{obj.con.val3|asd}, Code:{1}, Name:{name}'.format(data, 'Test');
console.log(s);
var s = 'Id:{id}, Val:{obj.con.val3|12}, Code:{1}, Name:{name}'.format(data, 'Test');
console.log(s);
var s = 'Id:{id}, Val:{obj.con.val|ABC}, Code:{1}, Num:{obj.num}, Name:{name}'.format(data, 'Test');
console.log(s);

var s = 'lang="{{id:{obj.id},pid:{obj.pid},level:{obj.level}}}"'.format({ obj: { id: 12, pid: 1, level: 1 } });
console.log(s);
var s = 'lang="{{id:{id},pid:{pid},level:{level}}}"'.format({ id: 12, pid: 1, level: 1 });
console.log(s);
var s = 'lang="{{id:{id},pid:{pid},level:{level|0}}}"'.format({ id: 12, pid: 0 });
console.log(s);

//增加 String.format 和 String.Format 方法
var str = '{0:D4},{1}';
console.log(String.format(str, 20, 'abc'));
console.log(String.Format(str, 20, 'abc'));

示例结果:

abc
0020
0020
2.00000e+001
你好:JS,这是用JS写的一个仿C#的format函数
你好:JS,这是用JS写的一个仿C#的format函数
123.5{正则123.50000{表达式}12.5000
123.5{正则123.50000{表达式}2D12
123:45:67
2016年08月24日 17:22:15
2016年08月24日
1234567DF5
0002
<tr lang="{id:1,pid:2,level:0}">

Id:123, Val:asd, Code:Test, Name:张三
Id:123, Val:12, Code:Test, Name:张三
Id:123, Val:acc, Code:Test, Num:321, Name:张三
lang="{id:12,pid:1,level:1}"
lang="{id:12,pid:1,level:1}"
lang="{id:12,pid:0,level:0}"
0020,abc
0020,abc