JavaScript эквивалентен printf / String.Format - PullRequest
1785 голосов
/ 04 марта 2009

Я ищу хороший JavaScript-эквивалент C / PHP printf() или для программистов на C # / Java, String.Format() (IFormatProvider для .NET).

Мое основное требование на данный момент - это формат разделителя тысяч для чисел, но то, что обрабатывает множество комбинаций (включая даты), будет хорошо.

Я понимаю, что Microsoft Ajax предоставляет версию String.Format(), но нам не нужны все издержки этой инфраструктуры.

Ответы [ 45 ]

30 голосов
/ 14 октября 2010

Программисты JavaScript могут использовать String.prototype.sprintf в https://github.com/ildar-shaimordanov/jsxt/blob/master/js/String.js. Ниже приведен пример:

var d = new Date();
var dateStr = '%02d:%02d:%02d'.sprintf(
    d.getHours(), 
    d.getMinutes(), 
    d.getSeconds());
21 голосов
/ 01 сентября 2010

+ 1 Zippo за исключением того, что тело функции должно быть таким, как показано ниже, или иначе оно добавляет текущую строку на каждой итерации:

String.prototype.format = function() {
    var formatted = this;
    for (var arg in arguments) {
        formatted = formatted.replace("{" + arg + "}", arguments[arg]);
    }
    return formatted;
};
19 голосов
/ 11 марта 2013

Добавляя к ответу 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! %%"
15 голосов
/ 04 марта 2009

Я добавлю свои открытия, которые я обнаружил с тех пор, как спросил:

К сожалению, кажется, что sprintf не обрабатывает форматирование тысяч разделителей, как строковый формат .NET.

14 голосов
/ 11 декабря 2011

Очень элегантно:

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

14 голосов
/ 18 ноября 2012

Я хочу поделиться своим решением для «проблемы». Я не изобрел колесо заново, но пытаюсь найти решение, основанное на том, что JavaScript уже делает. Преимущество в том, что вы получаете все неявные преобразования бесплатно. Установка свойства prototype $ для 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'
14 голосов
/ 27 сентября 2009

Я использую небольшую библиотеку под названием String.format для JavaScript , которая поддерживает большинство возможностей форматирования строк (включая формат чисел и дат) и использует синтаксис .NET. Сам скрипт меньше 4 кБ, поэтому он не создает больших накладных расходов.

11 голосов
/ 04 марта 2009

Если вы хотите обработать разделитель тысяч, вам действительно следует использовать toLocaleString () из класса JavaScript Number , так как он отформатирует строку для региона пользователя.

Класс JavaScript Дата может форматировать локализованные даты и время.

9 голосов
/ 07 мая 2014

У меня есть решение, очень похожее на решение Питера, но оно касается числа и случая объекта.

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

Может быть, было бы еще лучше разобраться со всеми случаями глубины, но для моих нужд это просто прекрасно.

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

PS: эта функция очень полезна, если вы используете переводы в шаблонах, таких как AngularJS :

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

Где en.json что-то вроде

{
    "hello-message": "Hello {name}, welcome.",
    "hello-by-name": "Hello {0}, welcome."
}
8 голосов
/ 15 апреля 2011

В проекте PHPJS написаны реализации JavaScript для многих функций PHP. Поскольку функция PHP sprintf() в основном совпадает с функцией C printf(), ее реализация JavaScript должна удовлетворить ваши потребности.

...