相当于 printf / string 的 JavaScript。 格式

I'm looking for a good JavaScript equivalent of the C/PHP printf() or for C#/Java programmers, String.Format() (IFormatProvider for .NET).

My basic requirement is a thousand separator format for numbers for now, but something that handles lots of combinations (including dates) would be good.

I realize Microsoft's Ajax library provides a version of String.Format(), but we don't want the entire overhead of that framework.

转载于:https://stackoverflow.com/questions/610406/javascript-equivalent-to-printf-string-format

csdnceshi71
Memor.の var search = [$scope.dog, "1"]; var url = vsprintf("earth/Services/dogSearch.svc/FindMe/%s/%s", search); ***For node, you can get your module by "npm install sprintf-js"
4 年多之前 回复
csdnceshi63
elliott.david I wrote a cheap one that uses C-like printf syntax.
4 年多之前 回复
csdnceshi60
℡Wang Yan Aside all the great answers below, you may want to take a look at this one: stackoverflow.com/a/2648463/1712065 which IMO, is the most efficient solution to this problem.
5 年多之前 回复

30个回答

Adding to zippoxer's answer, I use this function:

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.

I also have a non-prototype version which I use more often for its Java-like syntax:

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 update

All the cool new stuff in ES 2015 makes this a lot easier:

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

I figured that since this, like the older ones, doesn't actually parse the letters, it might as well just use a single token %%. This has the benefit of being obvious and not making it difficult to use a single %. However, if you need %% for some reason, you would need to replace it with itself:

format("I love percentage signs! %%", "%%");
// "I love percentage signs! %%"
csdnceshi77
狐狸.fox yep, that's the idea :)
3 年多之前 回复
csdnceshi72
谁还没个明天 this answer was great for a quick copy paste into an existing function. No require no downloads etc.
3 年多之前 回复

I'll add my own discoveries which I've found since I asked:

Sadly it seems sprintf doesn't handle thousand separator formatting like .NET's string format.

I want to share my solution for the 'problem'. I haven't re-invented the wheel but tries to find a solution based on what JavaScript already does. The advantage is, that you get all implicit conversions for free. Setting the prototype property $ of String gives a very nice and compact syntax (see examples below). It is maybe not the most efficient way, but in most cases dealing with output it does not have to be super optimized.

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

Here are a few examples:

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'
csdnceshi67
bug^君 unfortunately at least # and + are not implemented for floats. here is a reference for the function in c: tutorialspoint.com/c_standard_library/c_function_sprintf.htm
2 年多之前 回复

If you are looking to handle the thousands separator, you should really use toLocaleString() from the JavaScript Number class since it will format the string for the user's region.

The JavaScript Date class can format localized dates and times.

csdnceshi76
斗士狗 add some examples so that everyone can understands it quickly.
6 年多之前 回复
csdnceshi62
csdnceshi62 It's actually a set by the user as a setting in the application (not the machine their on) but I'll take a look, thanks
11 年多之前 回复

One very slightly different version, the one I prefer (this one uses {xxx} tokens rather than {0} numbered arguments, this is much more self-documenting and suits localization much better):

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

A variation would be:

  var formatted = l(this);

that calls an l() localization function first.

I did not see the String.format variant:

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;
    });
};
/**
 * 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];
        }
    );
}

Here's a minimal implementation of sprintf in JavaScript: it only does "%s" and "%d", but I have left space for it to be extended. It is useless to the OP, but other people who stumble across this thread coming from Google might benefit from it.

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

Example:

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

In contrast with similar solutions in previous replies, this one does all substitutions in one go, so it will not replace parts of previously replaced values.

I have a solution very close to Peter's, but it deals with number and object case.

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

Maybe it could be even better to deal with the all deeps cases, but for my needs this is just fine.

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

PS: This function is very cool if you are using translations in templates frameworks like AngularJS:

<h1> {{('hello-message'|translate).format(user)}} <h1>
<h1> {{('hello-by-name'|translate).format( user ? user.name : 'You' )}} <h1>

Where the en.json is something like

{
    "hello-message": "Hello {name}, welcome.",
    "hello-by-name": "Hello {0}, welcome."
}
weixin_41568208
北城已荒凉 the [^}] part in the regexp is unnecesary.. use {(.*?)} instead, or better {([\s\S]*?)} to match newline too.
大约 6 年之前 回复

I have a slightly longer formatter for JavaScript here...

You can do formatting several ways:

  • String.format(input, args0, arg1, ...)
  • String.format(input, obj)
  • "literal".format(arg0, arg1, ...)
  • "literal".format(obj)

Also, if you have say a ObjectBase.prototype.format (such as with DateJS) it will use that.

Examples...

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

I've also aliased with .asFormat and have some detection in place in case there's already a string.format (such as with MS Ajax Toolkit (I hate that library).

共30条数据 首页 1 3
Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!
立即提问
相关内容推荐