相当于 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个回答

There is "sprintf" for JavaScript which you can find at http://www.webtoolkit.info/javascript-sprintf.html.

I use this simple function:

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

That's very similar to string.format:

"{0} is dead, but {1} is alive!".format("ASP", "ASP.NET")
weixin_41568183
零零乙 The function is unsafe as it sometimes interprets {n}s within the strings passed to it: '{0}'.format('you should only see this {1}', 'you should not see this')
大约 2 年之前 回复
csdnceshi69
YaoRaoLov formatted.replace(new RegExp("\\{" + arg + "\\}", "g"), arguments[arg]) allows you to repeat indexes in case you need to duplicate values in the string.
6 年多之前 回复
csdnceshi57
perhaps? The variable arg is global. You need to do this instead: for (var arg in arguments) {
接近 8 年之前 回复
weixin_41568134
MAO-EYE This fails if a previous replacement contains a format string as well: "{0} is dead, but {1} is alive!".format("{1}", "ASP.NET") === "ASP.NET is dead, but ASP.NET is alive!"
9 年多之前 回复
csdnceshi58
Didn"t forge For reference, for...in won't work in every browser as this code expects it to. It'll loop over all enumerable properties, which in some browsers will include arguments.length, and in others won't even include the arguments themselves at all. In any case, if Object.prototype is added to, any additions will probably be included in the bunch. The code should be using a standard for loop, rather than for...in.
9 年多之前 回复
csdnceshi54
hurriedly% I think the code is still not correct. The correct one one should be like Filipiz posted.
接近 10 年之前 回复
csdnceshi60
℡Wang Yan Ahh, but now I see he's adding the whole replacement to the string every time, you're right. user437231's answer is correct. For some reason I was thinking this.replace was only returning the replaced part...
接近 10 年之前 回复
csdnceshi60
℡Wang Yan It shouldn't be =, it should be +=, because arguments[arg] may not be a string. Because format is already a string outside the loop, + will concatenate the string, and arguments[arg] will automatically have .toString() called on it. If arguments[0] was 5 and arguments[1] was 7, then instead of "{1}{2}".format(5,7) returning 57, the first argument will yield 12 and the second one will throw an out of bounds exception.
接近 10 年之前 回复
csdnceshi67
bug^君 why +=?, should it formatted = this.replace("{" + arg + "}", arguments[arg]);
接近 10 年之前 回复
csdnceshi66
必承其重 | 欲带皇冠 What about "{{0}} use this next instead {0}"? Not really a String.Format, but it is small and nice function to use.
接近 10 年之前 回复

Very elegant:

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

Credit goes to (broken link) https://gist.github.com/0i0/1519811

weixin_41568110
七度&光 This is the only one that handles escape brackets {{0}} as well as things like {0}{1}.format("{1}", "{0}"). Should be at the very top!
6 年多之前 回复

+1 Zippo with the exception that the function body needs to be as below or otherwise it appends the current string on every iteration:

String.prototype.format = function() {
    var formatted = this;
    for (var arg in arguments) {
        formatted = formatted.replace("{" + arg + "}", arguments[arg]);
    }
    return formatted;
};
csdnceshi61
derek5. For reference, for...in won't work in every browser as this code expects it to. It'll loop over all enumerable properties, which in some browsers will include arguments.length, and in others won't even include the arguments themselves at all. In any case, if Object.prototype is added to, any additions will probably be included in the bunch. The code should be using a standard for loop, rather than for...in.
9 年多之前 回复
csdnceshi51
旧行李 It does not replace the second character 'The {0} is dead. Don\'t code {0}. Code {1} that is open source!'.format('ASP', 'PHP'); the result becomes The ASP is dead. Don't code {0}. Code PHP that is open source!. One more thing for(arg in arguments) does not work in IE. i replaced with for (arg = 0; arg <arguments.length; arg++)
9 年多之前 回复
csdnceshi76
斗士狗 Yeah I didn't post the code correctly, thanks for fixing :)
接近 10 年之前 回复
csdnceshi62
csdnceshi62 It didn't work on Firefox. The debugger show arg as undefined.
接近 10 年之前 回复

I didn't see pyformat in the list so I thought I'd throw it in:

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

I use a small library called String.format for JavaScript which supports most of the format string capabilities (including format of numbers and dates), and uses the .NET syntax. The script itself is smaller than 4 kB, so it doesn't create much of overhead.

csdnceshi51
旧行李 You are right pasting it would be no better. I just got this comment for random review - and commented before disliking it. To me stackoverflow is better when providing explanations rather than ready made solutions (which the link is). I also do not want to encourage people to post or download black-box code.
6 年多之前 回复
csdnceshi72
谁还没个明天 the link is to a (minified) 4 kB javascript library. I don't believe pasting it into the answer is a good idea.
6 年多之前 回复
csdnceshi51
旧行李 While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes.
6 年多之前 回复
csdnceshi74
7*4 Often a downloadable archive that's an EXE is nothing more than a "self-extracting ZIP". Execute it, and it will unpack itself. This is quite convenient BUT because it looks so much like malware, the format is not used on the web all that often any more.
7 年多之前 回复
csdnceshi76
斗士狗 Not an exe any more, looks pretty excellent.
10 年多之前 回复
csdnceshi53
Lotus@ I took a look at that library and it looks really great. I was pissed off when I saw that the download was an EXE. What the heck is that about? Didn't download.
10 年多之前 回复

For those who like Node.JS and its util.format feature, I've just extracted it out into its vanilla JavaScript form (with only functions that util.format uses):

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('\n') > -1) {
                if (array) {
                    str = str.split('\n').map(function (line) {
                        return '  ' + line;
                    }).join('\n').substr(2);
                } else {
                    str = '\n' + str.split('\n').map(function (line) {
                        return '   ' + line;
                    }).join('\n');
                }
            }
        } 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 + '\n ') +
            ' ' +
            output.join(',\n  ') +
            ' ' +
            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;
};

Harvested from: https://github.com/joyent/node/blob/master/lib/util.js

Just in case someone needs a function to prevent polluting global scope, here is the function that does the same:

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

For basic formatting:

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

The PHPJS project has written JavaScript implementations for many of PHP's functions. Since PHP's sprintf() function is basically the same as C's printf(), their JavaScript implementation of it should satisfy your needs.

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