JavaScript对应于printf/String.Format的函数

2411

我正在寻找一个良好的JavaScript等价物来替代C/PHP中的printf()或对于C#/Java程序员,String.Format()(.NET使用 IFormatProvider)。

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

我意识到微软的Ajax库提供了一个版本的String.Format(),但我们不想要整个框架的开销。


4
除了下面众多优秀的回答,您或许也想看看这个链接:https://dev59.com/pXNA5IYBdhLWcg3wPbSe#2648463,在我看来,这是解决此问题最高效的方法。 - Annie
3
我写了一个简单的程序,使用类C语言的printf语法。 - Braden Best
var search = [$scope.dog, "1"]; var url = vsprintf("http://earth/Services/dogSearch.svc/FindMe/%s/%s", search); ***对于Node,您可以通过“npm install sprintf-js”获取您的模块。 - Jenna Leaf
5
这里的大多数答案都令人失望。printf和String.Format不仅仅是简单的模板,而且问题特别提到了千位分隔符,这是简单模板解决方案都无法处理的。 - blm
除了“模板字符串”之外,人们可能还在寻找String.padStart。 (请参见https://dev59.com/jXE85IYBdhLWcg3wnU-p) - Nor.Z
显示剩余3条评论
61个回答

34

三种不同的方式来格式化JavaScript字符串

有三种不同的方式来通过替换占位符为变量值来格式化一个字符串。

  1. Using template literal (backticks ``)

    let name = 'John';
    let age = 30;
    // using backticks
    console.log(`${name} is ${age} years old.`);
    // John is 30 years old.

  2. Using concatenation

let name = 'John';
let age = 30;
// using concatenation
console.log(name + ' is ' + age + ' years old.');
// John is 30 years old.

  1. 创建自定义格式化函数

String.prototype.format = function () {
  var args = arguments;
  return this.replace(/{([0-9]+)}/g, function (match, index) {
    // check if the argument is there
    return typeof args[index] == 'undefined' ? match : args[index];
  });
};


console.log('{0} is {1} years old.'.format('John', 30));


2
谢谢!第三个选项适合我的情况。 - Amila Senadheera
1
第三个选项是唯一一个允许在声明字符串之后“注入”变量的选项。 - Prid
第三个选项称为“猴子补丁”,通常被认为是一种反模式,一般仅在将功能(如Array.prototype.forEach)向旧引擎进行后移时才使用。请参见[MDN上的警告](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain#implicit_constructors_of_literals)。虽然与`printf`不完全对称,但`util.format`可能是op正在寻找的最佳选择。 - Subfuzion

32

28

我想分享一下我的“问题”解决方案。我没有重新发明轮子,而是尝试找到基于JavaScript已有的东西的解决方案。优点是,你可以免费获得所有隐式转换。设置String的原型属性$可以提供非常好的和紧凑的语法(请参见下面的示例)。这也许不是最有效的方式,但在大多数情况下,处理输出时不必进行超级优化。

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'

不幸的是,浮点数并未实现至少 # 和 +。这里提供了 C 语言函数的参考链接:https://www.tutorialspoint.com/c_standard_library/c_function_sprintf.htm - Daniel

28

zippoxer 的回答之上,我使用了这个函数:

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的语法,所以我更经常使用它:

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中所有酷炫的新功能使这变得更加容易:

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."

我想既然这个版本和之前的版本一样并没有真正解析字母,那么就可以使用一个单独的令牌%%。这样做的好处是明显的,而且不会让单个%难以使用。但是,如果你因某种原因需要%%,那么就需要用它自身替换它:

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

3
这个答案非常适合快速复制粘贴到现有的函数中。不需要下载或任何要求。 - Nick
唯一能令人满意的答案。不知道为什么有人建议使用模板字面量。printf/sprintf并不限制参数,但是当使用模板字面量时,我必须知道变量,并且这些变量是不固定的。常识很少是普遍的!对不起,我说话粗鲁了! - ssi-anik

20

+1 Zippo的唯一区别在于,函数主体需要如下所示,否则它会在每次迭代时附加当前字符串:

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

1
它在Firefox上不起作用。调试器显示arg未定义。 - xiao 啸
它不会替换第二个字符'The {0} is dead. Don\'t code {0}. Code {1} that is open source!'.format('ASP', 'PHP'); 结果变成了 The ASP is dead. Don't code {0}. Code PHP that is open source!. 还有一件事,for(arg in arguments)在IE中不起作用。我用for (arg = 0; arg <arguments.length; arg++)代替了它。 - samarjit samanta
2
供参考,for...in 在某些浏览器中不能按照此代码的期望工作。它会循环遍历所有可枚举属性,其中在某些浏览器中将包括 arguments.length,而在其他浏览器中甚至不包括参数本身。无论如何,如果添加了Object.prototype,任何添加的内容都可能被包含在其中。代码应该使用标准的 for 循环,而不是 for...in - cHao
你应该提出答案编辑而不是重复回答。这个回答已经存在:https://dev59.com/YXRB5IYBdhLWcg3weXOX#3492815 - alexandre-rousseau

15

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


我仔细研究了那个库,它看起来真的很棒。当我看到下载文件是一个EXE文件时,我感到非常恼火。这是怎么回事?我没有下载。 - jessegavin
通常,可下载的EXE档案只是一个“自解压缩ZIP”文件。执行它,它会自行解压缩。这非常方便,但由于它看起来很像恶意软件,因此这种格式在网络上并不经常使用。 - Chuck Kollars
虽然这个链接或许可以解答问题,但最好还是在这里包含答案的重点,并提供该链接以便参考。仅提供链接的答案可能会因为链接页面的变化而失效。 - starmole
@starmole 这个链接是一个(压缩后的)4 kB JavaScript 。我认为把它粘贴到答案中不是一个好主意。 - ivarni
你说得对,复制粘贴并不是更好的选择。我刚刚收到了这个随机评论,并在不喜欢它之前发表了评论。对我来说,StackOverflow 在提供解释方面要比提供现成解决方案(链接中的内容)更好。我也不想鼓励人们发布或下载黑盒代码。 - starmole

14

13

非常优雅:

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

感谢 (链接失效) https://gist.github.com/0i0/1519811


这是唯一一个处理转义括号 {{0}} 以及像 {0}{1}.format("{1}", "{0}") 这样的东西的函数。应该放在最顶部! - Juan

13
如果您想处理千位分隔符,应该使用 JavaScript Number 类中的 toLocaleString(),因为它会根据用户的地区格式化字符串。
JavaScript Date 类可以格式化本地化日期和时间。

1
实际上,这是由用户在应用程序中设置的一组(而不是他们所在的机器),但我会查看一下,谢谢。 - Chris S
添加一些示例,以便每个人可以快速理解。在代码中使用注释可以帮助其他程序员理解您的代码。例如://这是一个注释您还可以使用注释来临时禁用代码:/* 这段代码现在不会运行: console.log('这行代码不会被执行!'); */在JavaScript中,您可以通过创建对象来存储和访问数据。例如:var person = { name: '张三', age: 30, city: '北京' };您可以使用点表示法或方括号表示法来访问对象的属性:console.log(person.name); //输出:张三 console.log(person['age']); //输出:30 - Bhushan Kawadkar

9
我使用这个:
String.prototype.format = function() {
    var newStr = this, i = 0;
    while (/%s/.test(newStr))
        newStr = newStr.replace("%s", arguments[i++])

    return newStr;
}

然后我调用它:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接