Питання JavaScript еквівалент printf / String.Format


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

Моя основна вимога - це тисячний роздільний формат цифр для зараз, але те, що обробляє багато комбінацій (включаючи дати), буде добре.

Я усвідомлюю Microsoft Ajax Бібліотека надає версію String.Format(), але ми не хочемо, щоб всі накладні витрати на цю систему.


1610
2018-01-12 20:02


походження


Крім всіх чудових відповідей нижче ви можете поглянути на цей: stackoverflow.com/a/2648463/1712065 який ІМО є найбільш ефективним рішенням цієї проблеми. - Annie
Я написав a дешевий один що використовує C-подібний синтаксис printf. - Braden Best
var search = [$ scope.dog, "1"]; var url = vsprintf ("Земля / Послуги / dogSearch.svc / FindMe /% s /% s "; пошук); *** Для вузла ви можете отримати ваш модуль за допомогою "npm install sprintf-js" - Jenna Leaf


Відповіді:


Спробуй sprintf () для JavaScript.


ОновитиГаразд, якщо ви дійсно хочете зробити простий метод форматування самостійно, не замінюйте їх послідовно, але виконуйте їх одночасно.

Оскільки більшість інших згаданих пропозицій не вдається, коли рядок заміни попередньої заміни також містить таку послідовність форматів:

"{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 [число] дорівнює 0. Необхідно зробити явним, якщо (), щоб побачити, чи (args [number] === undefined). - fserb
в іншому викладі стенограми, якщо, чому б не зробити "матч" замість "" ('+ number +') '". матч повинен дорівнювати такому рядку. - mikeycgto
@ckozl Ваш код переформатування недійсний відповідь. Він змінює вихідний код. Будь ласка, перевірте свій код перед застосуванням змін. Дякую. - fearphage
@ckozl - Будь ласка, не повторюйте форматування даної відповіді неодноразово проти волі того, хто його залишив. Я блокую це на наступний день, і якщо ви хочете пояснити, чому ви це робите, будь ласка, дайте це Мета. - Brad Larson♦
The (!String.prototype.format) перевірка є поганою ідеєю, якщо не вдаватися до поліфайлів, оскільки це означає, що нові веб-переглядачі можуть прийти і зламати ваш веб-сайт з несумісним 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"

Це тому, що Arrayс slice змусить все, що входить arguments в ан Array, незалежно від того, спочатку чи ні, а також key буде індексом (0, 1, 2 ...) кожного елемента масиву, примусеним до рядка (наприклад, "0", так що "\\{0\\}" для вашого першого регулярного виразу).

Тихий


366



Це досить круто, щоб відповісти на запитання сток-перехресного потоку з кодом із stackoverflow, +1 - 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-номера

Сторінка довідки Mozilla для toFixed () (з посиланнями на toPrecision (), toExponential (), toLocaleString (), ...)


288



Чи не краще було б вписати літеральний номер у дужку, а не залишати там дивний пробіл? - rmobis
Це, мабуть, виглядатиме краще, вірно. Але моя мета полягає лише в тому, щоб вказати на синтаксичну помилку. - rescdsk
Просто бічну примітку, якщо ви використовуєте старий веб-переглядач або підтримуєте старі веб-переглядачі, деякі веб-переглядачі, виконані насправді помилково, використовуючи Math.round на місці toFixed, є кращим рішенням. - ars265
@ Rafael_ і @rescdsk: .. також працює: 33333..toExponential(2); - Peter Jaric
Or (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
ви не оголошуєте змінну всередині для циклу - 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 桂林
Я думаю, що код ще не правильний. Правильний той повинен бути таким Філіпіти опубліковано - 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


Ось а мінімальний реалізація sprintf в JavaScript: це тільки "% s" і "% d", але я залишив місце для його розширення. Це марно для ОП, але інші люди, які натрапляють на це потік від 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



Це не підтримує% x від вузла v0.10.26 - Max Krohn


Я здивований, ніхто не користувався reduce, це оригінальна лаконічна та потужна функція JavaScript.

ES6 (EcmaScript2015)

String.prototype.format = function() {
  return [...arguments].reduce((p,c) => p.replace(/%s/,c), this);
};

console.log('Is that a %s or a %s?... No, it\'s %s!'.format('plane', 'bird', 'SOman'));

<ES6

function interpolate(theString, argumentArray) {
    var regex = /%s/;
    var _r=function(p,c){return p.replace(regex,c);}
    return argumentArray.reduce(_r, theString);
}

interpolate("%s, %s and %s", ["Me", "myself", "I"]); // "Me, myself and I"

Як це працює:

зменшити застосовує функцію проти акумулятора та кожного елемента масиву (зліва направо), щоб зменшити його до одного значення.

var _r= function(p,c){return p.replace(/%s/,c)};

console.log(
  ["a", "b", "c"].reduce(_r, "[%s], [%s] and [%s]") + '\n',
  [1, 2, 3].reduce(_r, "%s+%s=%s") + '\n',
  ["cool", 1337, "stuff"].reduce(_r, "%s %s %s")
);


36



Ось версія, яка використовує цей підхід для створення спрощеного printf функція: jsfiddle.net/11szrbx9 - Dem Pilafian
І ось ще один, який використовує ES6, в одному рядку: (...a) => {return a.reduce((p: string, c: any) => p.replace(/%s/, c)); - Bhyd
Не потрібно String.prototype.format в ES6: ((a,b,c)=>`${a}, ${b} and ${c}`)(...['me', 'myself', 'I']) (зверніть увагу, що це трохи зайвим, щоб краще відповідати вашому прикладу) - Tino
Вам доведеться реалізувати функції заміни для кожного з них printfСпецифікатори типу типу та включають логіку для префіксів заповнення. Ітерація над стрункою формату в розумній манері, схоже, є незначною проблемою тут, imho. Чітке рішення, якщо вам потрібні лише рядові заміни. - collapsar


Програмісти 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());

30



code.google.com є мертвою посиланням - Jason Morgan
@JasonMorgan, я поділився робочою посиланням на GitHub. Див. Виправлену відповідь. - jsxt