JavaScript等效于printf / String.Format

JavaScript equivalent to printf/String.Format

我正在寻找一个相当于J/PHP EDCOX1的0 JavaScript或C/J/Java程序员EDCOX1×1(EDCOX1,2,.net)。

我现在的基本要求是对数字使用千位分隔符格式,但是处理许多组合(包括日期)的功能会很好。

我意识到微软的Ajax库提供了String.Format()的一个版本,但我们不希望该框架的全部开销。


基于先前建议的解决方案:

1
2
3
4
5
6
7
8
9
10
11
12
// First, checks if it isn't implemented yet.
if (!String.prototype.format) {
  String.prototype.format = function() {
    var args = arguments;
    return this.replace(/{(\d+)}/g, function(match, number) {
      return typeof args[number] != 'undefined'
        ? args[number]
        : match
      ;
    });
  };
}

"{0} is dead, but {1} is alive! {0} {2}".format("ASP","ASP.NET")

输出

ASP is dead, but ASP.NET is alive! ASP {2}

如果您不想修改String的原型:

1
2
3
4
5
6
7
8
9
10
11
if (!String.format) {
  String.format = function(format) {
    var args = Array.prototype.slice.call(arguments, 1);
    return format.replace(/{(\d+)}/g, function(match, number) {
      return typeof args[number] != 'undefined'
        ? args[number]
        : match
      ;
    });
  };
}

让您更加熟悉:

String.format('{0} is dead, but {1} is alive! {0} {2}', 'ASP', 'ASP.NET');

结果相同:

ASP is dead, but ASP.NET is alive! ASP {2}


从上的ES6可以使用模板字符串:

1
2
3
let soMany = 10;
console.log(`This is ${soMany} times easier!`);
//"This is 10 times easier!

有关详细信息,请参阅下面的Kim答案。

否则:

尝试sprintf()获取javascript。

如果你真的想自己做一个简单的格式方法,不要连续做替换,而是同时做替换。

因为当先前替换的替换字符串也包含这样的格式序列时,提到的大多数其他建议都失败了:

1
"{0}{1}".format("{1}","{0}")

通常,您希望输出为{1}{0},但实际输出为{1}{1}。因此,像Fearphage的建议一样,同时进行替换。


这很有趣,因为堆栈溢出实际上对名为formatUnicornString原型有自己的格式化功能。试试看!进入控制台,键入如下内容:

1
"Hello, {name}, are you feeling {adjective}?".formatUnicorn({name:"Gabriel", adjective:"OK"});

Firebug

你得到这个输出:

Hello, Gabriel, are you feeling OK?

可以使用对象、数组和字符串作为参数!我得到了它的代码并对其进行了修改,以生成新版本的String.prototype.format

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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行为。

1
"a{0}bcd{1}ef".formatUnicorn("foo","bar"); // yields"aFOObcdBARef"

这是因为Arrayslice将强制arguments中的任何内容进入Array,不管它最初是不是,key将是每个数组元素的索引(0,1,2…)强制进入一个字符串(例如,"0",所以"\\{0\\}")用于第一个regexp模式。

整洁的


型javascript中的数字格式

我来到这个问题页面,希望找到如何用javascript格式化数字,而不引入另一个库。以下是我发现的:

四舍五入浮点数

在javascript中,与sprintf("%.2f", num)相当的似乎是num.toFixed(2),它将num格式化为2个小数位,并进行舍入(但请参见下面@ars265关于Math.round的评论)。

1
2
(12.345).toFixed(2); // returns"12.35" (rounding!)
(12.3).toFixed(2); // returns"12.30" (zero padding)

指数形式

sprintf("%.2e", num)相当的是num.toExponential(2)

1
(33333).toExponential(2); //"3.33e+4"

号十六进制和其他基

要以B为基数打印数字,请尝试num.toString(B)。javascript支持从base2到36的自动转换(此外,一些浏览器对base64编码的支持有限)。

1
2
(3735928559).toString(16); // to base 16:"deadbeef"
parseInt("deadbeef", 16); // from base 16: 3735928559

参考页

JS数字格式快速教程

tofixed()的Mozilla引用页(带有指向topRection()、toExponential()、toLocaleString()的链接,…)


从上的ES6可以使用模板字符串:

1
2
3
let soMany = 10;
console.log(`This is ${soMany} times easier!`);
//"This is 10 times easier!

请注意,模板字符串由反勾号`而不是(单)引号包围。

更多信息:

https://developers.google.com/web/updates/2015/01/es6-template-strings

https://developer.mozilla.org/en-us/docs/web/javascript/reference/template_字符串

注:检查Mozilla站点以查找支持的浏览器列表。


芝宝JSXT

这个选项更适合。

1
2
3
4
5
6
7
8
String.prototype.format = function() {
    var formatted = this;
    for (var i = 0; i < arguments.length; i++) {
        var regexp = new RegExp('\\{'+i+'\\}', 'gi');
        formatted = formatted.replace(regexp, arguments[i]);
    }
    return formatted;
};

使用此选项,我可以替换如下字符串:

1
'The {0} is dead. Don\'t code {0}. Code {1} that is open source!'.format('ASP', 'PHP');

使用您的代码,第二个0将不会被替换。;)


我使用这个简单的函数:

1
2
3
4
5
6
7
String.prototype.format = function() {
    var formatted = this;
    for( var arg in arguments ) {
        formatted = formatted.replace("{" + arg +"}", arguments[arg]);
    }
    return formatted;
};

这与string.format非常相似:

1
"{0} is dead, but {1} is alive!".format("ASP","ASP.NET")


对于node.js用户,util.format具有类似printf的功能:

1
util.format("%s world","Hello")


这里是一个用javascript实现sprintf的最小方法:它只做了"%s"和"%d",但我为它的扩展留出了空间。这对OP来说是没用的,但是其他从Google偶然发现这个线索的人可能会从中受益。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
function sprintf() {
    var args = arguments,
    string = args[0],
    i = 1;
    return string.replace(/%((%)|s|d)/g, function (m) {
        // m is the matched format, e.g. %s, %d
        var val = null;
        if (m[2]) {
            val = m[2];
        } else {
            val = args[i];
            // A switch statement so that the formatter can be extended. Default is %s
            switch (m) {
                case '%d':
                    val = parseFloat(val);
                    if (isNaN(val)) {
                        val = 0;
                    }
                    break;
            }
            i++;
        }
        return val;
    });
}

例子:

1
2
alert(sprintf('Latitude: %s, Longitude: %s, Count: %d', 41.847, -87.661, 'two'));
// Expected output: Latitude: 41.847, Longitude: -87.661, Count: 0

与之前回复中的类似解决方案相比,这个解决方案一次完成所有替换,因此它不会替换先前替换的部分值。


我很惊讶没有人使用reduce,这是一个本地简洁和强大的javascript函数。

ES6(ECMAScript2015)

1
2
3
4
5
String.prototype.format = function() {
  return [...arguments].reduce((p,c) => p.replace(/%s/,c), this);
};

console.log('Is that a %s or a %s?... No, it\'s %s!'.format('plane', 'bird', 'SOman'));

< ES6
1
2
3
4
5
6
7
function interpolate(theString, argumentArray) {
    var regex = /%s/;
    var _r=function(p,c){return p.replace(regex,c);}
    return argumentArray.reduce(_r, theString);
}

interpolate("%s, %s and %s", ["Me","myself","I"]); //"Me, myself and I"

它是如何工作的:

reduce applies a function against an accumulator and each element in the array (from left to right) to reduce it to a single value.

1
2
3
4
5
6
7
8
9
var _r= function(p,c){return p.replace(/%s/,c)};

console.log(
  ["a","b","c"].reduce(_r,"[%s], [%s] and [%s]") + '
'
,
  [1, 2, 3].reduce(_r,"%s+%s=%s") + '
'
,
  ["cool", 1337,"stuff"].reduce(_r,"%s %s %s")
);


javascript程序员可以在https://github.com/ildar-shaimordanov/jsxt/blob/master/js/string.js上使用string.prototype.sprintf。下面是示例:

1
2
3
4
5
var d = new Date();
var dateStr = '%02d:%02d:%02d'.sprintf(
    d.getHours(),
    d.getMinutes(),
    d.getSeconds());


+1 zippo,除非函数体需要如下所示,否则它会在每次迭代中附加当前字符串:

1
2
3
4
5
6
7
String.prototype.format = function() {
    var formatted = this;
    for (var arg in arguments) {
        formatted = formatted.replace("{" + arg +"}", arguments[arg]);
    }
    return formatted;
};


zippoxer的回答中,我使用以下函数:

1
2
3
4
5
6
7
8
9
10
String.prototype.format = function () {
    var a = this, b;
    for (b in arguments) {
        a = a.replace(/%[a-z]/, arguments[b]);
    }
    return a; // Make chainable
};

var s = 'Hello %s The magic number is %d.';
s.format('world!', 12); // Hello World! The magic number is 12.

我还有一个非原型版本,我经常使用它的Java类语法:

1
2
3
4
5
6
7
8
9
10
11
12
13
function format() {
    var a, b, c;
    a = arguments[0];
    b = [];
    for(c = 1; c < arguments.length; c++){
        b.push(arguments[c]);
    }
    for (c in b) {
        a = a.replace(/%[a-z]/, b[c]);
    }
    return a;
}
format('%d ducks, 55 %s', 12, 'cats'); // 12 ducks, 55 cats

号ES 2015更新

ES 2015的所有新产品都让这变得更容易:

1
2
3
4
5
6
7
8
9
function format(fmt, ...args){
    return fmt
        .split("%%")
        .reduce((aggregate, chunk, i) =>
            aggregate + chunk + (args[i] ||""),"");
}

format("Hello %%! I ate %% apples today.","World", 44);
//"Hello World, I ate 44 apples today."

我想,因为这和以前的一样,实际上并没有解析字母,所以它可能只使用一个标记%%。这样做的好处是显而易见,并且不难使用单个%。但是,如果您出于某种原因需要%%,则需要将其替换为自身:

1
2
format("I love percentage signs! %%","%%");
//"I love percentage signs! %%"


我将添加我自己的发现,这是我自问起发现的:

  • 数字格式(千位分隔符/货币格式)
  • sprintf(与上述作者相同)

遗憾的是,sprintf似乎没有处理像.NET字符串格式那样的千位分隔符格式。


非常优雅:

1
2
3
4
5
6
7
8
9
String.prototype.format = function (){
    var args = arguments;
    return this.replace(/\{\{|\}\}|\{(\d+)\}/g, function (curlyBrack, index) {
        return ((curlyBrack =="{{") ?"{" : ((curlyBrack =="}}") ?"}" : args[index]));
    });
};

// Usage:
"{0}{1}".format("{1}","{0}")

信用卡转到(broken link)https://gist.github.com/0i0/1519811


我使用一个名为string.format for javascript的小库,它支持大多数格式字符串功能(包括数字和日期的格式),并使用.NET语法。脚本本身小于4KB,因此不会产生太多开销。


我想分享我的"问题"解决方案。我还没有重新发明轮子,但是我试图找到一个基于javascript的解决方案。优点是,您可以免费获得所有隐式转换。设置字符串的原型属性$提供了一个非常好和简洁的语法(参见下面的示例)。这可能不是最有效的方法,但在大多数情况下处理输出时,它不必进行超级优化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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));
}

以下是几个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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'


如果您希望处理千位分隔符,那么应该使用javascript number类中的toLocaleString(),因为它将格式化用户区域的字符串。

javascript日期类可以格式化本地化的日期和时间。


我有一个非常接近彼得的解决方案,但它涉及数字和对象案例。

1
2
3
4
5
6
7
8
9
10
11
12
if (!String.prototype.format) {
  String.prototype.format = function() {
    var args;
    args = arguments;
    if (args.length === 1 && args[0] !== null && typeof args[0] === 'object') {
      args = args[0];
    }
    return this.replace(/{([^}]*)}/g, function(match, key) {
      return (typeof args[key] !=="undefined" ? args[key] : match);
    });
  };
}

也许处理所有的深水案件会更好,但对于我的需要,这是很好的。

1
2
"This is an example from {name}".format({name:"Blaine"});
"This is an example from {0}".format("Blaine");

PS:如果您在模板框架(如AngularJS)中使用翻译,则此函数非常酷:

1
2
 {{('hello-message'|translate).format(user)}}
 {{('hello-by-name'|translate).format( user ? user.name : 'You' )}}

其中en.json类似于

1
2
3
4
{
   "hello-message":"Hello {name}, welcome.",
   "hello-by-name":"Hello {0}, welcome."
}


phpjs项目已经为php的许多函数编写了javascript实现。由于php的sprintf()函数与c的printf()函数基本相同,因此它们的javascript实现应该满足您的需要。


我用这个:

1
2
3
4
5
6
7
String.prototype.format = function() {
    var newStr = this, i = 0;
    while (/%s/.test(newStr))
        newStr = newStr.replace("%s", arguments[i++])

    return newStr;
}

然后我称之为:

1
2
3
"%s<p>
%s
</p>"
.format("Header","Just a test!");


有一个稍有不同的版本,我更喜欢这个版本(这个版本使用xxx标记,而不是0编号的参数,这更适合自我记录,更适合本地化):

1
2
3
4
5
6
7
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;
};

一个变化是:

1
  var formatted = l(this);

首先调用l()本地化函数。


在http://www.webtoolkit.info/javascript-sprintf.html上可以找到javascript的"sprintf"。


对于那些喜欢node.js及其util.format特性的用户,我刚刚将其提取为普通的javascript形式(仅使用util.format使用的函数):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
exports = {};

function isString(arg) {
    return typeof arg === 'string';
}
function isNull(arg) {
    return arg === null;
}
function isObject(arg) {
    return typeof arg === 'object' && arg !== null;
}
function isBoolean(arg) {
    return typeof arg === 'boolean';
}
function isUndefined(arg) {
    return arg === void 0;
}
function stylizeNoColor(str, styleType) {
    return str;
}
function stylizeWithColor(str, styleType) {
    var style = inspect.styles[styleType];

    if (style) {
        return '\u001b[' + inspect.colors[style][0] + 'm' + str +
            '\u001b[' + inspect.colors[style][3] + 'm';
    } else {
        return str;
    }
}
function isFunction(arg) {
    return typeof arg === 'function';
}
function isNumber(arg) {
    return typeof arg === 'number';
}
function isSymbol(arg) {
    return typeof arg === 'symbol';
}
function formatPrimitive(ctx, value) {
    if (isUndefined(value))
        return ctx.stylize('undefined', 'undefined');
    if (isString(value)) {
        var simple = '\'' + JSON.stringify(value).replace(/^"|"$/g, '')
                .replace(/'/g,"\'")
                .replace(/\"/g, '"') + '\'';
        return ctx.stylize(simple, 'string');
    }
    if (isNumber(value)) {
        // Format -0 as '-0'. Strict equality won't distinguish 0 from -0,
        // so instead we use the fact that 1 / -0 < 0 whereas 1 / 0 > 0 .
        if (value === 0 && 1 / value < 0)
            return ctx.stylize('-0', 'number');
        return ctx.stylize('' + value, 'number');
    }
    if (isBoolean(value))
        return ctx.stylize('' + value, 'boolean');
    // For some reason typeof null is"object", so special case here.
    if (isNull(value))
        return ctx.stylize('null', 'null');
    // es6 symbol primitive
    if (isSymbol(value))
        return ctx.stylize(value.toString(), 'symbol');
}
function arrayToHash(array) {
    var hash = {};

    array.forEach(function (val, idx) {
        hash[val] = true;
    });

    return hash;
}
function objectToString(o) {
    return Object.prototype.toString.call(o);
}
function isDate(d) {
    return isObject(d) && objectToString(d) === '[object Date]';
}
function isError(e) {
    return isObject(e) &&
        (objectToString(e) === '[object Error]' || e instanceof Error);
}
function isRegExp(re) {
    return isObject(re) && objectToString(re) === '[object RegExp]';
}
function formatError(value) {
    return '[' + Error.prototype.toString.call(value) + ']';
}
function formatPrimitiveNoColor(ctx, value) {
    var stylize = ctx.stylize;
    ctx.stylize = stylizeNoColor;
    var str = formatPrimitive(ctx, value);
    ctx.stylize = stylize;
    return str;
}
function isArray(ar) {
    return Array.isArray(ar);
}
function hasOwnProperty(obj, prop) {
    return Object.prototype.hasOwnProperty.call(obj, prop);
}
function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) {
    var name, str, desc;
    desc = Object.getOwnPropertyDescriptor(value, key) || {value: value[key]};
    if (desc.get) {
        if (desc.set) {
            str = ctx.stylize('[Getter/Setter]', 'special');
        } else {
            str = ctx.stylize('[Getter]', 'special');
        }
    } else {
        if (desc.set) {
            str = ctx.stylize('[Setter]', 'special');
        }
    }
    if (!hasOwnProperty(visibleKeys, key)) {
        name = '[' + key + ']';
    }
    if (!str) {
        if (ctx.seen.indexOf(desc.value) < 0) {
            if (isNull(recurseTimes)) {
                str = formatValue(ctx, desc.value, null);
            } else {
                str = formatValue(ctx, desc.value, recurseTimes - 1);
            }
            if (str.indexOf('
'
) > -1) {
                if (array) {
                    str = str.split('
'
).map(function (line) {
                        return '  ' + line;
                    }).join('
'
).substr(2);
                } else {
                    str = '
'
+ str.split('
'
).map(function (line) {
                        return '   ' + line;
                    }).join('
'
);
                }
            }
        } else {
            str = ctx.stylize('[Circular]', 'special');
        }
    }
    if (isUndefined(name)) {
        if (array && key.match(/^\d+$/)) {
            return str;
        }
        name = JSON.stringify('' + key);
        if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) {
            name = name.substr(1, name.length - 2);
            name = ctx.stylize(name, 'name');
        } else {
            name = name.replace(/'/g,"\'")
                .replace(/\"/g, '"')
                .replace(/(^"|"$)/g,"'")
                .replace(/\\\\/g, '\');
            name = ctx.stylize(name, '
string');
        }
    }

    return name + '
: ' + str;
}
function formatArray(ctx, value, recurseTimes, visibleKeys, keys) {
    var output = [];
    for (var i = 0, l = value.length; i < l; ++i) {
        if (hasOwnProperty(value, String(i))) {
            output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
                String(i), true));
        } else {
            output.push('
');
        }
    }
    keys.forEach(function (key) {
        if (!key.match(/^\d+$/)) {
            output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
                key, true));
        }
    });
    return output;
}
function reduceToSingleString(output, base, braces) {
    var length = output.reduce(function (prev, cur) {
        return prev + cur.replace(/\u001b\[\d\d?m/g, '
').length + 1;
    }, 0);

    if (length > 60) {
        return braces[0] +
            (base === '
' ? '' : base + '
 ') +
            '
' +
            output.join('
,
  ') +
            '
' +
            braces[1];
    }

    return braces[0] + base + '
' + output.join(', ') + ' ' + braces[1];
}
function formatValue(ctx, value, recurseTimes) {
    // Provide a hook for user-specified inspect functions.
    // Check that value is an object with an inspect function on it
    if (ctx.customInspect &&
        value &&
        isFunction(value.inspect) &&
            // Filter out the util module, it'
s inspect function is special
        value.inspect !== exports.inspect &&
            // Also filter out any prototype objects using the circular check.
        !(value.constructor && value.constructor.prototype === value)) {
        var ret = value.inspect(recurseTimes, ctx);
        if (!isString(ret)) {
            ret = formatValue(ctx, ret, recurseTimes);
        }
        return ret;
    }

    // Primitive types cannot have properties
    var primitive = formatPrimitive(ctx, value);
    if (primitive) {
        return primitive;
    }

    // Look up the keys of the object.
    var keys = Object.keys(value);
    var visibleKeys = arrayToHash(keys);

    if (ctx.showHidden) {
        keys = Object.getOwnPropertyNames(value);
    }

    // This could be a boxed primitive (new String(), etc.), check valueOf()
    // NOTE: Avoid calling `valueOf` on `Date` instance because it will return
    // a number which, when object has some additional user-stored `keys`,
    // will be printed out.
    var formatted;
    var raw = value;
    try {
        // the .valueOf() call can fail for a multitude of reasons
        if (!isDate(value))
            raw = value.valueOf();
    } catch (e) {
        // ignore...
    }

    if (isString(raw)) {
        // for boxed Strings, we have to remove the 0-n indexed entries,
        // since they just noisey up the output and are redundant
        keys = keys.filter(function (key) {
            return !(key >= 0 && key < raw.length);
        });
    }

    // Some type of object without properties can be shortcutted.
    if (keys.length === 0) {
        if (isFunction(value)) {
            var name = value.name ? ': ' + value.name : '';
            return ctx.stylize('[Function' + name + ']', 'special');
        }
        if (isRegExp(value)) {
            return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
        }
        if (isDate(value)) {
            return ctx.stylize(Date.prototype.toString.call(value), 'date');
        }
        if (isError(value)) {
            return formatError(value);
        }
        // now check the `raw` value to handle boxed primitives
        if (isString(raw)) {
            formatted = formatPrimitiveNoColor(ctx, raw);
            return ctx.stylize('[String: ' + formatted + ']', 'string');
        }
        if (isNumber(raw)) {
            formatted = formatPrimitiveNoColor(ctx, raw);
            return ctx.stylize('[Number: ' + formatted + ']', 'number');
        }
        if (isBoolean(raw)) {
            formatted = formatPrimitiveNoColor(ctx, raw);
            return ctx.stylize('[Boolean: ' + formatted + ']', 'boolean');
        }
    }

    var base = '', array = false, braces = ['{', '}'];

    // Make Array say that they are Array
    if (isArray(value)) {
        array = true;
        braces = ['[', ']'];
    }

    // Make functions say that they are functions
    if (isFunction(value)) {
        var n = value.name ? ': ' + value.name : '';
        base = ' [Function' + n + ']';
    }

    // Make RegExps say that they are RegExps
    if (isRegExp(value)) {
        base = ' ' + RegExp.prototype.toString.call(value);
    }

    // Make dates with properties first say the date
    if (isDate(value)) {
        base = ' ' + Date.prototype.toUTCString.call(value);
    }

    // Make error with message first say the error
    if (isError(value)) {
        base = ' ' + formatError(value);
    }

    // Make boxed primitive Strings look like such
    if (isString(raw)) {
        formatted = formatPrimitiveNoColor(ctx, raw);
        base = ' ' + '[String: ' + formatted + ']';
    }

    // Make boxed primitive Numbers look like such
    if (isNumber(raw)) {
        formatted = formatPrimitiveNoColor(ctx, raw);
        base = ' ' + '[Number: ' + formatted + ']';
    }

    // Make boxed primitive Booleans look like such
    if (isBoolean(raw)) {
        formatted = formatPrimitiveNoColor(ctx, raw);
        base = ' ' + '[Boolean: ' + formatted + ']';
    }

    if (keys.length === 0 && (!array || value.length === 0)) {
        return braces[0] + base + braces[1];
    }

    if (recurseTimes < 0) {
        if (isRegExp(value)) {
            return ctx.stylize(RegExp.prototype.toString.call(value), 'regexp');
        } else {
            return ctx.stylize('[Object]', 'special');
        }
    }

    ctx.seen.push(value);

    var output;
    if (array) {
        output = formatArray(ctx, value, recurseTimes, visibleKeys, keys);
    } else {
        output = keys.map(function (key) {
            return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array);
        });
    }

    ctx.seen.pop();

    return reduceToSingleString(output, base, braces);
}
function inspect(obj, opts) {
    // default options
    var ctx = {
        seen: [],
        stylize: stylizeNoColor
    };
    // legacy...
    if (arguments.length >= 3) ctx.depth = arguments[2];
    if (arguments.length >= 4) ctx.colors = arguments[3];
    if (isBoolean(opts)) {
        // legacy...
        ctx.showHidden = opts;
    } else if (opts) {
        // got an"options" object
        exports._extend(ctx, opts);
    }
    // set default options
    if (isUndefined(ctx.showHidden)) ctx.showHidden = false;
    if (isUndefined(ctx.depth)) ctx.depth = 2;
    if (isUndefined(ctx.colors)) ctx.colors = false;
    if (isUndefined(ctx.customInspect)) ctx.customInspect = true;
    if (ctx.colors) ctx.stylize = stylizeWithColor;
    return formatValue(ctx, obj, ctx.depth);
}
exports.inspect = inspect;


// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
inspect.colors = {
    'bold': [1, 22],
    'italic': [3, 23],
    'underline': [4, 24],
    'inverse': [7, 27],
    'white': [37, 39],
    'grey': [90, 39],
    'black': [30, 39],
    'blue': [34, 39],
    'cyan': [36, 39],
    'green': [32, 39],
    'magenta': [35, 39],
    'red': [31, 39],
    'yellow': [33, 39]
};

// Don't use 'blue' not visible on cmd.exe
inspect.styles = {
    'special': 'cyan',
    'number': 'yellow',
    'boolean': 'yellow',
    'undefined': 'grey',
    'null': 'bold',
    'string': 'green',
    'symbol': 'green',
    'date': 'magenta',
    //"name": intentionally not styling
    'regexp': 'red'
};


var formatRegExp = /%[sdj%]/g;
exports.format = function (f) {
    if (!isString(f)) {
        var objects = [];
        for (var j = 0; j < arguments.length; j++) {
            objects.push(inspect(arguments[j]));
        }
        return objects.join(' ');
    }

    var i = 1;
    var args = arguments;
    var len = args.length;
    var str = String(f).replace(formatRegExp, function (x) {
        if (x === '%%') return '%';
        if (i >= len) return x;
        switch (x) {
            case '%s':
                return String(args[i++]);
            case '%d':
                return Number(args[i++]);
            case '%j':
                try {
                    return JSON.stringify(args[i++]);
                } catch (_) {
                    return '[Circular]';
                }
            default:
                return x;
        }
    });
    for (var x = args[i]; i < len; x = args[++i]) {
        if (isNull(x) || !isObject(x)) {
            str += ' ' + x;
        } else {
            str += ' ' + inspect(x);
        }
    }
    return str;
};

摘自:https://github.com/joyent/node/blob/master/lib/util.js


为了防止有人需要一个功能来防止污染全球范围,以下是相同的功能:

1
2
3
4
5
  function _format (str, arr) {
    return str.replace(/{(\d+)}/g, function (match, number) {
      return typeof arr[number] != 'undefined' ? arr[number] : match;
    });
  };

我这里有一个稍微长一点的javascript格式化程序…

您可以通过以下几种方式设置格式:

    百万千克1江户十一〔七〕号百万千克1百万千克1埃多克斯1〔8〕百万千克1百万千克1江户十一〔九〕号百万千克1百万千克1埃多克斯1〔10〕百万千克1

另外,如果您说的是objectbase.prototype.format(例如与datejs一起使用),它将使用它。

示例…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var input ="numbered args ({0}-{1}-{2}-{3})";
console.log(String.format(input,"first", 2, new Date()));
//Outputs"numbered args (first-2-Thu May 31 2012...Time)-{3})"

console.log(input.format("first", 2, new Date()));
//Outputs"numbered args(first-2-Thu May 31 2012...Time)-{3})"

console.log(input.format(
   "object properties ({first}-{second}-{third:yyyy-MM-dd}-{fourth})"
    ,{
        'first':'first'
        ,'second':2
        ,'third':new Date() //assumes Date.prototype.format method
    }
));
//Outputs"object properties (first-2-2012-05-31-{3})"

我还使用了.a s format作为别名,并在已经有了string.format的情况下进行了一些检测(例如使用MS Ajax工具包(我讨厌这个库)。


对于基本格式:

1
2
var template = jQuery.validator.format("{0} is not a valid value");
var result = template("abc");

我没有看到String.format变体:

1
2
3
4
5
6
String.format = function (string) {
    var args = Array.prototype.slice.call(arguments, 1, arguments.length);
    return string.replace(/{(\d+)}/g, function (match, number) {
        return typeof args[number] !="undefined" ? args[number] : match;
    });
};


用于jquery.ajax()成功函数。只传递一个参数,字符串替换为该对象的属性propertyname:

1
2
3
4
5
6
7
8
String.prototype.format = function () {
    var formatted = this;
    for (var prop in arguments[0]) {
        var regexp = new RegExp('\\{' + prop + '\\}', 'gi');
        formatted = formatted.replace(regexp, arguments[0][prop]);
    }
    return formatted;
};

例子:

1
var userInfo = ("Email: {Email} - Phone: {Phone}").format({ Email:"[email protected]", Phone:"123-123-1234" });


有了sprintf.js,您可以将一个漂亮的小格式变薄。

1
2
3
4
5
6
7
8
String.prototype.format = function(){
    var _args = arguments
    Array.prototype.unshift.apply(_args,[this])
    return sprintf.apply(undefined,_args)
}  
// this gives you:
"{%1$s}{%2$s}".format("1","0")
// {1}{0}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
 * Format string by replacing placeholders with value from element with
 * corresponsing index in `replacementArray`.
 * Replaces are made simultaneously, so that replacement values like
 * '{1}' will not mess up the function.
 *
 * Example 1:
 * ('{2} {1} {0}', ['three', 'two' ,'one']) -> 'one two three'
 *
 * Example 2:
 * ('{0}{1}', ['{1}', '{0}']) -> '{1}{0}'
 */

function stringFormat(formatString, replacementArray) {
    return formatString.replace(
        /\{(\d+)\}/g, // Matches placeholders, e.g. '{1}'
        function formatStringReplacer(match, placeholderIndex) {
            // Convert String to Number
            placeholderIndex = Number(placeholderIndex);

            // Make sure that index is within replacement array bounds
            if (placeholderIndex < 0 ||
                placeholderIndex > replacementArray.length - 1
            ) {
                return placeholderIndex;
            }

            // Replace placeholder with value from replacement array
            return replacementArray[placeholderIndex];
        }
    );
}

我在列表中没有看到pyformat,所以我想我会把它扔进去:

1
2
3
4
5
6
7
8
9
console.log(pyformat( 'The {} {} jumped over the {}'
                , ['brown' ,'fox' ,'foobar']
                ))
console.log(pyformat('The {0} {1} jumped over the {1}'
                , ['brown' ,'fox' ,'foobar']
                ))
console.log(pyformat('The {color} {animal} jumped over the {thing}'
                , [] ,{color: 'brown' ,animal: 'fox' ,thing: 'foobaz'}
                ))

jquery globalize项目中还有Globalize.format,jquery ui的官方全球化服务。当您需要具有文化意识的格式时,这很好。


arg功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
 * Qt stil arg()
 * var scr ="".arg("mydiv").arg("mydivClass");
 */

String.prototype.arg = function() {
    var signIndex = this.indexOf("%");
    var result = this;
    if (signIndex > -1 && arguments.length > 0) {
        var argNumber = this.charAt(signIndex + 1);
        var _arg ="%"+argNumber;
        var argCount = this.split(_arg);
        for (var itemIndex = 0; itemIndex < argCount.length; itemIndex++) {
            result = result.replace(_arg, arguments[0]);
        }
    }
    return result;
}


使用lodash,您可以获得模板功能:

使用ES模板文本分隔符作为"插入"分隔符。通过替换"interpolate"分隔符禁用支持。

1
2
3
var compiled = _.template('hello ${ user }!');
compiled({ 'user': 'pebbles' });
// => 'hello pebbles!

我需要一个函数,它可以按照用户喜欢的方式格式化价格(以美分表示),而复杂的部分是格式由用户指定——我不希望我的用户理解printf之类的语法或regexps等。我的解决方案与BASIC中使用的有点类似,因此用户只需用位来标记数字,例如:

1
2
3
4
simple_format(1234567,"$ ###,###,###.##")
"$ 12,345.67"
simple_format(1234567,"### ### ###,## pln")
"12 345,67 pln"

我相信这很容易被用户理解,也很容易实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function simple_format(integer,format){
  var text ="";
  for(var i=format.length;i--;){
    if(format[i]=='#'){
      text = (integer%10) + text;
      integer=Math.floor(integer/10);
      if(integer==0){
        return format.substr(0,i).replace(/#(.*#)?/,"")+text;
      }
    }else{
      text = format[i] + text;
    }
  }
  return text;
}


Bobjs可以做到:

1
2
3
4
5
6
var sFormat ="My name is {0} and I am {1} years old.";
var result = bob.string.formatString(sFormat,"Bob", 29);
console.log(result);
//output:
//==========
// My name is Bob and I am 29 years old.


这并不是sprintf的完全复制品;但是,它与之相似,而且功能更强大:https://github.com/anywhichway/stringformatter

使用此库的格式表达式采用嵌入的javascript对象的形式,例如

1
format("I have {number: {currency:"$", precision:2}}.",50.2);

将返回"I have $50.20."


我开始将JavaString.Format(实际上是新的格式化程序().format())移植到javascript。初始版本可从以下网址获得:

https://github.com/robau/javascript.string.format.

您可以简单地添加javscript并调用StringFormat.format("%.2f", [2.4]);等。

请注意,尚未完成,但欢迎反馈:)


你可以使用这个功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
            String.prototype.format = function (args) {
            var str = this;
            return str.replace(String.prototype.format.regex, function(item) {
                var intVal = parseInt(item.substring(1, item.length - 1));
                var replace;
                if (intVal >= 0) {
                    replace = args[intVal];
                } else if (intVal === -1) {
                    replace ="{";
                } else if (intVal === -2) {
                    replace ="}";
                } else {
                    replace ="";
                }
                return replace;
            });
        };
        String.prototype.format.regex = new RegExp("{-?[0-9]+}","g");

        // Sample usage.
        var str ="She {1} {0}{2} by the {0}{3}. {-1}^_^{-2}";
        str = str.format(["sea","sells","shells","shore"]);
        alert(str);


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
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];
    });
};

这一个与0、1和一起工作。

1
2
3
4
5
6
7
String.prototype.format = function format()
{                                                                                                              
  var msg = this;
  for(var i in arguments)
    msg = msg.replace(/\{\}/,arguments[i]).replace(new RegExp('\\{'+i+'\\}','g'),arguments[i]);
  return msg;
}


这是coffeescript的https://stackoverflow.com/a/4673436/1258486的实现。

https://gist.github.com/eces/5669361

1
2
3
4
5
if String.prototype.format is undefined
  String.prototype.format = () ->
    _arguments = arguments
    this.replace /{(\d+)}/g, (match, number) ->
      if typeof _arguments[number] isnt 'undefined' then _arguments[number] else match