题 JavaScript等效于printf / String.Format


我正在寻找一个与C / PHP相当的好的JavaScript printf() 或者对于C#/ Java程序员, String.Format() (IFormatProvider for .NET)。

我的基本要求是现在有数千个数字分隔符格式,但处理许多组合(包括日期)的东西会很好。

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


1610
2018-01-12 20:02


起源


除了下面的所有重要答案,你可能想看看这个: stackoverflow.com/a/2648463/1712065 哪个IMO,是解决这个问题最有效的方法。 - Annie
我写了一篇 便宜的 它使用类似C的printf语法。 - Braden Best
var search = [$ scope.dog,“1”]; var url = vsprintf(“大地/服务/ dogSearch.svc / FindMe /%S /%S”;,搜索); ***对于节点,您可以通过“npm install sprintf-js”获取模块 - Jenna Leaf


答案:


尝试 用于JavaScript的sprintf()


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

因为当前替换的替换字符串也包含如下格式序列时,提及的大多数其他提议都会失败:

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

通常你会期望输出 {1}{0} 但实际输出是 {1}{1}。所以做一个同时替换,而不是像 恐惧症的建议


687



它再次被移动: epeli.github.com/underscore.string - Maksymilian Majer
如果只需要一些简单的数字到字符串转换, num.toFixed() 方法可能就足够了! - heltonbiker
@MaksymilianMajer似乎是一个大不相同的东西。 - Evan Carroll
@EvanCarroll你是对的。当时我写了评论的存储库 sprintf() for JavaScript 没有。 underscore.string 除了基于的sprintf之外还有更多功能 sprintf() for JavaScript 实现。除此之外,图书馆是一个完全不同的项目。 - Maksymilian Majer
@MaksymilianMajer对,只是说这个答案已经死了,链接已经腐烂了。它需要完全清除。 - Evan Carroll


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

// 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已经死了,但ASP.NET还活着! ASP {2}


如果您不想修改 String原型:

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已经死了,但ASP.NET还活着! ASP {2}


1279



||如果args [number]为0,则技巧不起作用。应该使用显式if()来查看是否(args [number] === undefined)。 - fserb
在简写的else语句中,为什么不只是“匹配”而不是“'{'+ number +'}'”。匹配应该等于该字符串。 - mikeycgto
@ckozl您的代码重新格式化使答案无效。它改变了代码的输出。请在应用修改前测试您的代码。谢谢。 - fearphage
@ckozl - 请不要反复覆盖此答案的格式,违背留下该答案的人的意愿。我在第二天锁定了这个,如果你想解释为什么要这样做,请这样做 元。 - Brad Larson♦
该 (!String.prototype.format) 检查是一个坏主意,除非处理polyfill,因为它意味着新的浏览器可以出现并破坏您的网站与不兼容 format 方法。我建议删除它。 - Simon Lindholm


这很有趣,因为Stack Overflow实际上有自己的格式化功能 String 原型叫做 formatUnicorn。尝试一下!进入控制台并键入以下内容:

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

Firebug

你得到这个输出:

Hello, Gabriel, are you feeling 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) call - 这意味着如果你抛出的是字符串或数字的参数,而不是单个JSON样式的对象,你会得到C#的 String.Format 几乎完全是行为。

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

那是因为 Arrayslice 会强迫所有的东西 arguments 变成一个 Array,无论是否原来,和 key 将每个数组元素的索引(0,1,2 ...)强制转换为字符串(例如,“0”,所以 "\\{0\\}" 为你的第一个正则表达式模式)。

整齐。


366



使用stackoverflow,+1代码回答stackoverflow上的问题非常酷 - Sneakyness
@JamesManning正则表达式允许全局标记(g),可以多次替换同一个键。在上面的示例中,您可以使用 {name} 在同一个句子中多次,并将它们全部替换。 - Greggg
@ DineiA.Rockenbach但代码 for (arg in args)  将定义一个名为的全局变量 arg。如果你不使用 var arg 在第一行,那么你应该使用 for(var arg in args) 避免定义全局变量。 - RainChen
说实话,这似乎非常脆弱。例如,如果发生了什么 name 是 "blah {adjective} blah"? - sam hocevar
@ruffin“有点夸张”?被愚弄为将用户数据解释为格式字符串的代码是一个整体 漏洞类别。 98.44%是 超越平庸。 - sam hocevar


JavaScript中的数字格式

我到这个问题页面希望找到如何 格式编号 在JavaScript中,没有引入另一个库。这是我发现的:

舍入浮点数

相当于 sprintf("%.2f", num) 在JavaScript中似乎是 num.toFixed(2),格式化 num 到2位小数,有四舍五入(但请参阅@ ars265的评论 Math.round 下面)。

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

指数形式

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

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

十六进制和其他基础

要在基数B中打印数字,请尝试 num.toString(B)。 JavaScript支持自动转换到基数2到36(此外,一些浏览器具有 对base64编码的有限支持)。

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

参考页面

关于JS数字格式的快速教程

toFixed()的Mozilla参考页面 (链接到toPrecision(),toExponential(),toLocaleString(),...)


288



将数字文字括在括号中,而不是在那里留下一个奇怪的白色空间,这不是更好吗? - rmobis
那可能看起来更好,真实。但我的目标只是指出语法错误陷阱。 - rescdsk
如果您使用的是较旧的浏览器,或支持较旧的浏览器,某些浏览器实现了错误的修复,使用Math.round代替toFixed,这只是一个侧面说明是一个更好的解决方案。 - ars265
@Raphael_和@rescdsk: .. 也有效: 33333..toExponential(2); - Peter Jaric
或(33333).toExponential(2) - Jonathan


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

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_strings

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


170



模板字符串的问题在于它们似乎是立即执行的,因此它们的使用就像一个类似i18n的字符串表完全没用。我无法在早期定义字符串,并提供稍后和/或重复使用的参数。 - Tustin2121
@ Tustin2121你是对的,它们并不是为了分配给一个变量而构建的,这有点令人头疼,但如果你把它们隐藏在一个函数中,它很容易使用模板化字符串的即时执行趋势。看到 jsfiddle.net/zvcm70pa - inanutshellus
@Tustin2121使用模板字符串或旧样式字符串连接没有区别,它的糖为同一件事。你必须在一个简单的函数中包装一个旧样式字符串生成器,同样的东西适用于字符串模板。 const compile = (x, y) => `I can call this template string whenever I want.. x=${x}, y=${y}` ... compile(30, 20) - cchamberlain
此解决方案不适用于传入变量的格式字符串(例如来自服务器) - user993954
@inanutshellus如果您的模板函数是在执行它的同一台机器上定义的,那么它可以正常工作。据我所知,您不能将函数作为JSON传递,因此在数据库中存储模板函数不能很好地工作。 - styfle


jsxt,Zippo

此选项更适合。

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;
};

使用此选项,我可以替换这些字符串:

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

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


168



我喜欢这个,因为它全局取代了格式标记。 - Jarrod Dixon♦
gist.github.com/1049426 我用这种方法更新了你的例子。许多好处,包括保存本机实现(如果存在),字符串化等。我尝试删除正则表达式,但是需要全局替换。 : - / - tbranyen
不幸的是,jsxt是GPL许可的 - AndiDog
你没有在for循环中声明一个变量 - brielov


我用这个简单的功能:

String.prototype.format = function() {
    var formatted = this;
    for( var arg in arguments ) {
        formatted = formatted.replace("{" + arg + "}", arguments[arg]);
    }
    return formatted;
};

这与string.format非常相似:

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

91



为什么 +=?, 应该是 formatted = this.replace("{" + arg + "}", arguments[arg]); - guilin 桂林
我认为代码仍然不正确。正确的一个应该是这样的 Filipiz 发布。 - wenqiang
以供参考, for...in 将无法在每个浏览器中运行,因为此代码需要它。它将遍历所有可枚举属性,在某些浏览器中将包含这些属性 arguments.length,在其他人甚至根本不包括论据本身。无论如何,如果 Object.prototype 添加到,任何添加可能会包括在一堆。代码应该使用标准 for 循环,而不是 for...in。 - cHao
如果先前的替换包含格式字符串,则会失败: "{0} is dead, but {1} is alive!".format("{1}", "ASP.NET") === "ASP.NET is dead, but ASP.NET is alive!" - Gumbo
变量 arg 是全球性的。你需要这样做: for (var arg in arguments) { - Pauan


这是一个 最小 在JavaScript中实现sprintf:它只执行“%s”和“%d”,但是我留下了空间来扩展它。它对OP来说毫无用处,但是偶然发现来自Google的这个帖子的其他人可能会从中受益。

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;
    });
}

例:

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

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


48





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

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

47



从node v0.10.26开始,这不支持%x - Max Krohn