我正在寻找一个与C/PHP printf()或C#/Java程序员String.Format()(IFormatProvider for.NET)相当的JavaScript。
目前,我的基本要求是数字的千位分隔符格式,但处理大量组合(包括日期)的格式会很好。
我意识到Microsoft的Ajax库提供了String.Format()的一个版本,但我们不希望该框架的全部开销。
我正在寻找一个与C/PHP printf()或C#/Java程序员String.Format()(IFormatProvider for.NET)相当的JavaScript。
目前,我的基本要求是数字的千位分隔符格式,但处理大量组合(包括日期)的格式会很好。
我意识到Microsoft的Ajax库提供了String.Format()的一个版本,但我们不希望该框架的全部开销。
当前回答
这很有趣,因为Stack Overflow实际上有自己的格式化函数,用于名为formatUnicorn的String原型。试试看!进入控制台并键入以下内容:
"Hello, {name}, are you feeling {adjective}?".formatUnicorn({name:"Gabriel", adjective: "OK"});
您将获得以下输出:
你好,加布里埃尔,你感觉还好吗?
您可以使用对象、数组和字符串作为参数!我得到了它的代码并对其进行了修改,以生成String.prototype.format的新版本:
String.prototype.formatUnicorn = String.prototype.formatUnicorn ||
function () {
"use strict";
var str = this.toString();
if (arguments.length) {
var t = typeof arguments[0];
var key;
var args = ("string" === t || "number" === t) ?
Array.prototype.slice.call(arguments)
: arguments[0];
for (key in args) {
str = str.replace(new RegExp("\\{" + key + "\\}", "gi"), args[key]);
}
}
return str;
};
请注意,巧妙的Array.prototype.slice.call(arguments)调用——这意味着如果你输入的参数是字符串或数字,而不是一个JSON样式的对象,那么你几乎可以得到C#的String.Format行为。
"a{0}bcd{1}ef".formatUnicorn("FOO", "BAR"); // yields "aFOObcdBARef"
这是因为Array的切片会将参数中的任何内容强制到Array中,无论它最初是不是这样,并且关键字将是每个数组元素的索引(0,1,2…),该索引被强制到字符串中(例如,“0”,因此第一个正则表达式模式为“\\{0\\}”)。
整洁的
其他回答
旧答案的修改代码https://stackoverflow.com/a/18234317/19531844效率更高(没有较慢的RegExp)且更短
String.prototype.formatUnicorn = function () {
let str = this.toString();
if(!arguments.length) {
return;
};
const [args] = arguments;
for (const key of Object.keys(args)) {
str = str.replaceAll(`{${key}}`, args[key]);
};
return str;
};
用法:
"{test} {test_2} {test}".formatUnicorn({"test": "hello", "test_2": "world"}); // yields hello world hello
新旧基准:https://jsben.ch/BRovx
String.prototype.repeat = function(n) {
return new Array(++n).join(this);
};
String.prototype.pad = function(requiredLength, paddingStr, paddingType) {
var n = requiredLength - this.length;
if (n) {
paddingType = paddingType ? paddingType.toLowerCase() : '';
paddingStr = paddingStr || ' ';
paddingStr = paddingStr.repeat( Math.ceil(n / paddingStr.length) ).substr(0, n);
if (paddingType == 'both') {
n /= 2;
return paddingStr.substr( 0, Math.ceil(n) ) + this + paddingStr.substr( 0, Math.floor(n) );
}
if (paddingType == 'left') {
return paddingStr + this;
}
return this + paddingStr;
}
return this;
};
// синтаксис аналогичен printf
// 'Привет, %s!'.format('мир') -> "Привет, мир!"
// '%.1s.%.1s. %s'.format('Иван', 'Иванович', 'Иванов') -> "И.И. Иванов"
String.prototype.format = function() {
var i = 0,
params = arguments;
return this.replace(/%(?:%|(?:(|[+-]+)(|0|'.+?)([1-9]\d*)?(?:\.([1-9]\d*))?)?(s|d|f))/g, function(match, sign, padding, width, precision, type) {
if (match == '%%') {
return '%';
}
var v = params[i++];
if (type == 'd') {
v = Math.round(v);
}
else if (type == 'f') {
v = v.toFixed(precision ? precision : 6);
}
if (/\+/.test(sign) && v > 0) {
v = '+' + v;
}
v += '';
if (type != 'f' && precision) {
v = v.substr(0, precision);
}
if (width) {
v = v.pad(width, padding == '' ? ' ' : padding[0] == "'" ? padding.substr(1) : padding, /-/.test(sign) ? 'right' : 'left');
}
return v;
});
};
// this.name = 'Вася';
// console.log( 'Привет, ${name}!'.template(this) );
// "Привет, Вася!"
String.prototype.template = function(context) {
return this.replace(/\$\{(.*?)\}/g, function(match, name) {
return context[name];
});
};
我使用一个名为String.format for JavaScript的小型库,它支持大多数格式字符串功能(包括数字和日期格式),并使用.NET语法。脚本本身小于4kB,因此不会产生太多开销。
一个稍有不同的版本,我更喜欢的版本(这个版本使用{xxx}标记,而不是{0}编号的参数,这更易于自我记录,更适合本地化):
String.prototype.format = function(tokens) {
var formatted = this;
for (var token in tokens)
if (tokens.hasOwnProperty(token))
formatted = formatted.replace(RegExp("{" + token + "}", "g"), tokens[token]);
return formatted;
};
一种变化是:
var formatted = l(this);
它首先调用l()本地化函数。
我想分享我对“问题”的解决方案。我没有重新发明轮子,但试图根据JavaScript已经做的事情找到解决方案。优点是,您可以免费获得所有隐式转换。设置字符串的原型属性$提供了一个非常好和紧凑的语法(参见下面的示例)。这可能不是最有效的方法,但在大多数情况下,处理输出时不必进行超级优化。
String.form = function(str, arr) {
var i = -1;
function callback(exp, p0, p1, p2, p3, p4) {
if (exp=='%%') return '%';
if (arr[++i]===undefined) return undefined;
exp = p2 ? parseInt(p2.substr(1)) : undefined;
var base = p3 ? parseInt(p3.substr(1)) : undefined;
var val;
switch (p4) {
case 's': val = arr[i]; break;
case 'c': val = arr[i][0]; break;
case 'f': val = parseFloat(arr[i]).toFixed(exp); break;
case 'p': val = parseFloat(arr[i]).toPrecision(exp); break;
case 'e': val = parseFloat(arr[i]).toExponential(exp); break;
case 'x': val = parseInt(arr[i]).toString(base?base:16); break;
case 'd': val = parseFloat(parseInt(arr[i], base?base:10).toPrecision(exp)).toFixed(0); break;
}
val = typeof(val)=='object' ? JSON.stringify(val) : val.toString(base);
var sz = parseInt(p1); /* padding size */
var ch = p1 && p1[0]=='0' ? '0' : ' '; /* isnull? */
while (val.length<sz) val = p0 !== undefined ? val+ch : ch+val; /* isminus? */
return val;
}
var regex = /%(-)?(0?[0-9]+)?([.][0-9]+)?([#][0-9]+)?([scfpexd%])/g;
return str.replace(regex, callback);
}
String.prototype.$ = function() {
return String.form(this, Array.prototype.slice.call(arguments));
}
以下是几个示例:
String.format("%s %s", [ "This is a string", 11 ])
console.log("%s %s".$("This is a string", 11))
var arr = [ "12.3", 13.6 ]; console.log("Array: %s".$(arr));
var obj = { test:"test", id:12 }; console.log("Object: %s".$(obj));
console.log("%c", "Test");
console.log("%5d".$(12)); // ' 12'
console.log("%05d".$(12)); // '00012'
console.log("%-5d".$(12)); // '12 '
console.log("%5.2d".$(123)); // ' 120'
console.log("%5.2f".$(1.1)); // ' 1.10'
console.log("%10.2e".$(1.1)); // ' 1.10e+0'
console.log("%5.3p".$(1.12345)); // ' 1.12'
console.log("%5x".$(45054)); // ' affe'
console.log("%20#2x".$("45054")); // ' 1010111111111110'
console.log("%6#2d".$("111")); // ' 7'
console.log("%6#16d".$("affe")); // ' 45054'