JavaScript setara dengan printf / String.Format

1970

Saya sedang mencari JavaScript yang setara dengan printf()programmer C / PHP atau C # / Java, String.Format()(IFormatProvider untuk .NET).

Persyaratan dasar saya adalah seribu format pemisah untuk angka untuk saat ini, tetapi sesuatu yang menangani banyak kombinasi (termasuk tanggal) akan bagus.

Saya menyadari perpustakaan Ajax Microsoft menyediakan versi String.Format(), tapi kami tidak ingin seluruh overhead kerangka itu.

Chris S
sumber
2
Selain semua jawaban hebat di bawah ini, Anda mungkin ingin melihat yang ini: stackoverflow.com/a/2648463/1712065 yang IMO, adalah solusi paling efisien untuk masalah ini.
Annie
1
Saya menulis yang murah yang menggunakan sintaks printf seperti C.
Braden Best
var search = [$ scope.dog, "1"]; var url = vsprintf (" earth / Services / dogSearch.svc / FindMe /% s /% s ", cari); *** Untuk simpul, Anda bisa mendapatkan modul Anda dengan "npm install sprintf-js"
Jenna Leaf
Saya juga telah menulis fungsi sederhana untuk mencapai ini; stackoverflow.com/a/54345052/5927126
AnandShanbhag

Jawaban:

1110

Dari ES6 Anda dapat menggunakan string template:

let soMany = 10;
console.log(`This is ${soMany} times easier!`);
// "This is 10 times easier!

Lihat jawaban Kim di bawah untuk detailnya.


Jika tidak:

Coba sprintf () untuk JavaScript .


Jika Anda benar-benar ingin melakukan metode format sederhana sendiri, jangan lakukan penggantian secara berturut-turut tetapi lakukan secara bersamaan.

Karena sebagian besar proposal lain yang disebutkan gagal ketika string pengganti pengganti sebelumnya juga berisi urutan format seperti ini:

"{0}{1}".format("{1}", "{0}")

Biasanya Anda akan mengharapkan output {1}{0}tetapi output aktual {1}{1}. Jadi lakukan penggantian secara bersamaan sebagai gantinya dalam saran fearphage .

Gumbo
sumber
16
Jika hanya beberapa konversi nomor-ke-string sederhana yang diinginkan, num.toFixed()metode mungkin cukup!
heltonbiker
@MaksymilianMajer yang tampaknya menjadi sesuatu yang sangat berbeda.
Evan Carroll
@ EvanCarroll Anda benar. Pada saat saya menulis komentar, repositori sprintf() for JavaScripttidak tersedia. underscore.stringmemiliki lebih banyak fitur selain sprintf yang didasarkan pada sprintf() for JavaScriptimplementasi. Selain itu perpustakaan adalah proyek yang sama sekali berbeda.
Maksymilian Majer
@MaksymilianMajer benar, hanya mengatakan jawaban ini sudah mati, dan tautannya sudah rusak. Itu harus benar-benar dibersihkan.
Evan Carroll
2
Ini seharusnya tidak diterima lagi. Pada ES6 ini dibangun ke dalam bahasa javascript (baik di browser dan NodeJS). Lihat jawaban @ Kim di bawah ini.
Ryan Shillington
1391

Membangun solusi yang disarankan sebelumnya:

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

output

ASP sudah mati, tetapi ASP.NET masih hidup! ASP {2}


Jika Anda memilih untuk tidak memodifikasi Stringprototipe:

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

Memberi Anda lebih akrab:

String.format('{0} is dead, but {1} is alive! {0} {2}', 'ASP', 'ASP.NET');

dengan hasil yang sama:

ASP sudah mati, tetapi ASP.NET masih hidup! ASP {2}

rasa takut
sumber
12
yang || trik tidak berfungsi jika args [angka] adalah 0. Harus melakukan eksplisit if () untuk melihat apakah (args [angka] === tidak terdefinisi).
fserb
4
dalam pernyataan steno lain jika, mengapa tidak melakukan "cocokkan" alih-alih "'{' + number + '}'". kecocokan harus sama dengan string itu.
mikeycgto
4
Jika Anda memiliki beberapa string yang ditambahkan satu sama lain (dengan +-operator), pastikan untuk memasukkan String lengkap dalam tanda kurung: ("asd {0}"+"fas {1}").format("first", "second");Jika tidak, fungsi hanya akan diterapkan ke string terakhir yang ditambahkan.
Lukas Knuth
3
Itu sedikit dan secara halus mengubah hasilnya. Bayangkan 'foo {0}'.format(fnWithNoReturnValue()). Saat ini akan kembali foo {0}. Dengan perubahan Anda, itu akan kembali foo undefined.
fearphage
2
@avenmore: / \ {(\ d +) \} / g
Hozuki
491

Ini lucu karena Stack Overflow sebenarnya memiliki fungsi format sendiri untuk Stringprototipe yang disebut formatUnicorn. Cobalah! Pergilah ke konsol dan ketik sesuatu seperti:

"Hello, {name}, are you feeling {adjective}?".formatUnicorn({name:"Gabriel", adjective: "OK"});

Pembakar

Anda mendapatkan hasil ini:

Hello, Gabriel, are you feeling OK?

Anda bisa menggunakan objek, array, dan string sebagai argumen! Saya mendapatkan kode dan mengolahnya untuk menghasilkan versi baru 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;
};

Perhatikan Array.prototype.slice.call(arguments)panggilan pintar - itu artinya jika Anda melempar argumen yang berupa string atau angka, bukan objek gaya JSON tunggal, Anda mendapatkan String.Formatperilaku C # yang hampir persis.

"a{0}bcd{1}ef".formatUnicorn("foo", "bar"); // yields "aFOObcdBARef"

Itu karena Array's sliceakan memaksa apa pun yang ada di argumentsdalam sebuah Array, apakah itu awalnya atau tidak, dan keyakan menjadi indeks (0, 1, 2 ...) dari masing-masing elemen array dipaksa string (misalnya, '0', sehingga "\\{0\\}"untuk pola regexp pertama Anda).

Rapi.

Gabriel Nahmias
sumber
402
Cukup keren untuk menjawab pertanyaan tentang stackoverflow dengan kode dari stackoverflow, +1
Sneakyness
5
@JamesManning Regex memungkinkan flag global ( g), yang dapat mengganti kunci yang sama lebih dari sekali. Dalam contoh di atas, Anda bisa menggunakan {name}beberapa kali dalam kalimat yang sama dan semuanya diganti.
KrekkieD
3
Jujur saja, ini sangat rapuh. Apa yang terjadi misalnya jika nameitu "blah {adjective} blah"?
sam hocevar
5
@ruffin "sedikit hiperbolik"? Kode yang tertipu dalam menafsirkan data pengguna sebagai string format adalah seluruh kategori kerentanan . 98,44% di luar biasa-biasa saja .
sam hocevar
3
@samhocevar Aku tidak percaya kamu, Bobby Kecil. ;) Jika Anda menjalankan teks yang diproses oleh JavaScript sisi klien di server database Anda tanpa pemeriksaan keamanan apa pun, surga bantu kami semua. ; ^) Dengar, seharusnya tidak ada apa pun yang dapat dikirim pengguna dari klien (misalnya, tukang pos) yang melewati keamanan server Anda. Dan Anda harus mengasumsikan sesuatu yang berbahaya yang dapat dikirim dari klien akan menjadi. Artinya, jika Anda memerlukan keamanan 100% dari kode JavaScript sisi-klien yang selalu dapat diedit pengguna, dan Anda pikir fungsi ini bisa membuka risiko keamanan, Anda bermain di permainan yang salah.
ruffin
325

Memformat Angka dalam JavaScript

Saya membuka halaman pertanyaan ini dengan harapan menemukan cara memformat angka dalam JavaScript, tanpa memperkenalkan perpustakaan lain. Inilah yang saya temukan:

Membulatkan angka floating-point

Setara dengan sprintf("%.2f", num)di JavaScript tampaknya num.toFixed(2), yang memformat numke 2 tempat desimal, dengan pembulatan (tetapi lihat komentar @ ars265 tentang di Math.roundbawah).

(12.345).toFixed(2); // returns "12.35" (rounding!)
(12.3).toFixed(2); // returns "12.30" (zero padding)

Bentuk eksponensial

Setara dengan sprintf("%.2e", num)is num.toExponential(2).

(33333).toExponential(2); // "3.33e+4"

Basis heksadesimal dan lainnya

Untuk mencetak angka di basis B, coba num.toString(B). JavaScript mendukung konversi otomatis ke dan dari basis 2 hingga 36 (selain itu, beberapa browser memiliki dukungan terbatas untuk pengkodean base64 ).

(3735928559).toString(16); // to base 16: "deadbeef"
parseInt("deadbeef", 16); // from base 16: 3735928559

Halaman Referensi

Tutorial cepat tentang pemformatan nomor JS

Halaman referensi Mozilla untuk toFixed () (dengan tautan ke toPrecision (), toExponential (), toLocaleString (), ...)

rescdsk
sumber
23
Bukankah lebih baik untuk menyertakan angka literal dalam tanda kurung, daripada meninggalkan ruang putih yang aneh di sana?
rmobis
7
Itu mungkin akan terlihat lebih baik, benar. Tapi tujuan saya di sana hanya untuk menunjukkan perangkap kesalahan sintaksis.
rescdsk
4
Hanya catatan tambahan jika Anda menggunakan peramban yang lebih lama, atau mendukung peramban yang lebih lama, beberapa peramban yang menerapkan toFixed secara tidak benar, menggunakan Math.round sebagai ganti toFixed adalah solusi yang lebih baik.
ars265
7
@Raphael_ dan @rescdsk: ..juga berfungsi:33333..toExponential(2);
Peter Jaric
Atau (33333) .toExponential (2)
Jonathan
245

Dari ES6 Anda dapat menggunakan string template :

let soMany = 10;
console.log(`This is ${soMany} times easier!`);
// "This is 10 times easier!

Ketahuilah bahwa string templat dikelilingi oleh tanda kutip `bukan tanda kutip (tunggal).

Untuk informasi lebih lanjut:

https://developers.google.com/web/updates/2015/01/ES6-Template-Strings

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/template_strings

Catatan: Periksa situs mozilla untuk menemukan daftar browser yang didukung.

Kim
sumber
61
Masalah dengan string template adalah bahwa mereka tampaknya dieksekusi segera, menjadikan penggunaannya sebagai, katakanlah, tabel string seperti i18n benar-benar tidak berharga. Saya tidak bisa mendefinisikan string sejak awal, dan menyediakan parameter untuk digunakan nanti dan / atau berulang kali.
Tustin2121
4
@ Tustin2121 Anda benar bahwa mereka tidak dibangun untuk ditugaskan ke variabel, yang sedikit membingungkan, tetapi cukup mudah untuk bekerja dengan kecenderungan eksekusi instan string templated 'jika Anda menyembunyikannya dalam suatu fungsi. Lihat jsfiddle.net/zvcm70pa
inanutshellus
13
@ Tustin2121 tidak ada perbedaan antara menggunakan string template atau penggabungan string gaya lama, gula untuk hal yang sama. Anda harus membungkus generator string gaya lama dalam fungsi sederhana dan hal yang sama berfungsi baik dengan template string. const compile = (x, y) => `I can call this template string whenever I want.. x=${x}, y=${y}`...compile(30, 20)
cchamberlain
4
solusi ini tidak akan berfungsi untuk format string yang dikirimkan dalam variabel (dari server misalnya)
user993954
1
@inanutshellus Itu berfungsi dengan baik jika fungsi template Anda didefinisikan pada mesin yang sama di mana ia dijalankan. Sejauh yang saya tahu, Anda tidak bisa melewatkan fungsi sebagai JSON, jadi menyimpan fungsi template dalam database tidak berfungsi dengan baik.
styfle
171

jsxt, Zippo

Opsi ini lebih cocok.

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

Dengan opsi ini saya dapat mengganti string seperti ini:

'The {0} is dead. Don\'t code {0}. Code {1} that is open source!'.format('ASP', 'PHP');

Dengan kode Anda {0} kedua tidak akan diganti. ;)

Bahasa Indonesia
sumber
3
gist.github.com/1049426 Saya memperbarui contoh Anda dengan pendekatan ini. Banyak manfaat termasuk menyimpan implementasi asli jika ada, merangkai, dll. Saya mencoba menghapus ekspresi reguler, tetapi semacam welp yang diperlukan untuk penggantian global. : - /
tbranyen
6
jsxt sayangnya berlisensi GPL
AndiDog
109

Saya menggunakan fungsi sederhana ini:

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

Itu sangat mirip dengan string.format:

"{0} is dead, but {1} is alive!".format("ASP", "ASP.NET")
Zippoxer
sumber
1
mengapa +=?, haruskah ituformatted = this.replace("{" + arg + "}", arguments[arg]);
guilin 桂林
2
Saya pikir kodenya masih salah. Yang benar harus seperti yang diposting Filipina .
wenqiang
3
Sebagai referensi, for...intidak akan berfungsi di setiap browser seperti yang diharapkan kode ini. Ini akan mengulang semua properti enumerable, yang di beberapa browser akan sertakan arguments.length, dan yang lain bahkan tidak akan menyertakan argumen sendiri. Dalam hal apa pun, jika Object.prototypeditambahkan ke, penambahan apa pun mungkin akan dimasukkan dalam kelompok. Kode harus menggunakan forloop standar , bukan for...in.
cHao
3
Ini gagal jika pengganti sebelumnya berisi format string juga:"{0} is dead, but {1} is alive!".format("{1}", "ASP.NET") === "ASP.NET is dead, but ASP.NET is alive!"
Gumbo
6
Variabelnya argglobal. Anda perlu melakukan ini sebagai gantinya:for (var arg in arguments) {
Pauan
68

Untuk pengguna Node.js ada util.formatyang memiliki fungsionalitas seperti printf:

util.format("%s world", "Hello")
George Eracleous
sumber
1
Ini tidak mendukung% x pada Node v0.10.26
Max Krohn
Tidak mendukung pengubah lebar dan penyelarasan (mis. %-20s %5.2f)
FGM
Saya harus menggulir ke bawah halaman untuk melihat jawaban yang bermanfaat ini.
Donato
53

Saya terkejut tidak ada yang digunakan reduce , ini adalah fungsi JavaScript ringkas dan kuat asli.

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"

Bagaimana itu bekerja:

mengurangi menerapkan fungsi terhadap akumulator dan setiap elemen dalam array (dari kiri ke kanan) untuk menguranginya menjadi nilai tunggal.

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

CPHPython
sumber
4
Berikut adalah versi yang menggunakan pendekatan ini untuk membuat printffungsi yang disederhanakan : jsfiddle.net/11szrbx9
Dem Pilafian
1
Dan ini satu lagi yang menggunakan ES6, dalam satu baris:(...a) => {return a.reduce((p: string, c: any) => p.replace(/%s/, c));
dtasev
Tidak perlu String.prototype.formatdi ES6: ((a,b,c)=>`${a}, ${b} and ${c}`)(...['me', 'myself', 'I'])(perhatikan bahwa ini agak berlebihan agar lebih sesuai dengan contoh Anda)
Tino
Anda harus mengimplementasikan fungsi pengganti untuk masing-masing printfpenentu tipe dan memasukkan logika untuk awalan padding. Iterasi atas format string dengan cara yang masuk akal tampaknya menjadi tantangan kecil di sini, imho. Solusi yang rapi jika Anda hanya membutuhkan penggantian string.
collapsar
51

Berikut ini adalah implementasi minimal dari sprintf dalam JavaScript: ia hanya melakukan "% s" dan "% d", tetapi saya telah meninggalkan ruang untuk diperpanjang. Tidak ada gunanya bagi OP, tetapi orang lain yang menemukan thread ini berasal dari Google mungkin mendapat manfaat darinya.

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

Contoh:

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

Berbeda dengan solusi serupa di balasan sebelumnya, yang ini melakukan semua penggantian dalam sekali jalan , sehingga tidak akan mengganti bagian dari nilai yang sebelumnya diganti.

Luke Madhanga
sumber
31

Programmer JavaScript dapat menggunakan String.prototype.sprintf di https://github.com/ildar-shaimordanov/jsxt/blob/master/js/String.js . Di bawah ini adalah contohnya:

var d = new Date();
var dateStr = '%02d:%02d:%02d'.sprintf(
    d.getHours(), 
    d.getMinutes(), 
    d.getSeconds());
jsxt
sumber
@JasonMorgan, saya telah membagikan tautan kerja di GitHub.Lihat jawaban yang sudah diperbaiki.
jsxt
24

Menambah zippoxerjawaban, saya menggunakan fungsi ini:

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.

Saya juga memiliki versi non-prototipe yang lebih sering saya gunakan untuk sintaks seperti 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

Pembaruan ES 2015

Semua hal baru yang keren di ES 2015 membuat ini jauh lebih mudah:

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

Saya pikir karena ini, seperti yang lebih lama, tidak benar-benar menguraikan huruf, mungkin juga hanya menggunakan token tunggal %%. Ini memiliki keuntungan karena jelas dan tidak membuatnya sulit untuk menggunakan tunggal %. Namun, jika Anda perlu %%karena suatu alasan, Anda harus menggantinya dengan itu sendiri:

format("I love percentage signs! %%", "%%");
// "I love percentage signs! %%"
Braden Best
sumber
3
jawaban ini sangat bagus untuk tempel salinan cepat ke fungsi yang ada. Tidak memerlukan tidak ada unduhan dll.
Nick
@Nggak, itu idenya :)
Braden Best
21

+1 Zippo dengan pengecualian bahwa fungsi tubuh harus seperti di bawah ini atau jika tidak menambahkan string saat ini pada setiap iterasi:

String.prototype.format = function() {
    var formatted = this;
    for (var arg in arguments) {
        formatted = formatted.replace("{" + arg + "}", arguments[arg]);
    }
    return formatted;
};
user437231
sumber
1
Itu tidak berfungsi di Firefox. Debugger menunjukkan arg sebagai tidak terdefinisi.
xiao 啸
Itu tidak menggantikan karakter kedua 'The {0} is dead. Don\'t code {0}. Code {1} that is open source!'.format('ASP', 'PHP'); hasilnya menjadi The ASP is dead. Don't code {0}. Code PHP that is open source!. Satu hal lagi for(arg in arguments)tidak berfungsi di IE. saya diganti dengan for (arg = 0; arg <arguments.length; arg++)
samarjit samanta
2
Sebagai referensi, for...intidak akan berfungsi di setiap browser seperti yang diharapkan kode ini. Ini akan mengulang semua properti enumerable, yang di beberapa browser akan sertakan arguments.length, dan yang lain bahkan tidak akan menyertakan argumen sendiri. Dalam kasus apa pun, jika Object.prototypeditambahkan ke, penambahan apa pun mungkin akan dimasukkan dalam kelompok. Kode harus menggunakan forloop standar , bukan for...in.
cHao
Anda harus mengusulkan edit jawaban alih-alih jawaban ganda. Gandakan jawaban ini
RousseauAlexandre
19

Saya ingin berbagi solusi untuk 'masalah'. Saya belum menemukan kembali roda tetapi mencoba mencari solusi berdasarkan apa yang sudah dilakukan JavaScript. Keuntungannya adalah, Anda mendapatkan semua konversi implisit secara gratis. Mengatur properti prototipe $ of String memberikan sintaks yang sangat bagus dan ringkas (lihat contoh di bawah). Ini mungkin bukan cara yang paling efisien, tetapi dalam banyak kasus berurusan dengan output tidak harus super optimal.

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

Berikut ini beberapa contoh:

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'
Rtlprmft
sumber
sayangnya setidaknya # dan + tidak diterapkan untuk pelampung. di sini adalah referensi untuk fungsi di c: tutorialspoint.com/c_standard_library/c_function_sprintf.htm
Daniel
14

Saya menggunakan pustaka kecil bernama String.format untuk JavaScript yang mendukung sebagian besar kemampuan format string (termasuk format angka dan tanggal), dan menggunakan sintaks .NET. Script itu sendiri lebih kecil dari 4 kB, sehingga tidak membuat banyak overhead.

Sven N
sumber
Saya melihat perpustakaan itu dan terlihat sangat bagus. Saya kesal ketika melihat bahwa unduhan itu EXE. Tentang apa itu? Tidak mengunduh.
jessegavin
Seringkali arsip yang dapat diunduh yang merupakan EXE tidak lebih dari "ZIP yang mengekstraksi sendiri". Jalankan, dan itu akan membongkar dirinya sendiri. Ini cukup nyaman TETAPI karena terlihat sangat mirip malware, formatnya tidak digunakan di web lebih sering.
Chuck Kollars
Meskipun tautan ini dapat menjawab pertanyaan, lebih baik untuk memasukkan bagian-bagian penting dari jawaban di sini dan memberikan tautan untuk referensi. Jawaban hanya tautan dapat menjadi tidak valid jika halaman tertaut berubah.
starmole
@starmole tautannya adalah ke (javascript) perpustakaan 4 kB . Saya tidak percaya menempelkannya ke jawaban adalah ide yang bagus.
ivarni
Anda benar menempel itu tidak akan lebih baik. Saya baru saja mendapat komentar ini untuk ulasan acak - dan berkomentar sebelum tidak menyukainya. Bagi saya stackoverflow lebih baik ketika memberikan penjelasan daripada solusi siap pakai (yang mana tautannya). Saya juga tidak ingin mendorong orang untuk mengirim atau mengunduh kode kotak hitam.
starmole
14

Sangat elegan:

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

Kredit menjadi (tautan rusak) https://gist.github.com/0i0/1519811

lior hakim
sumber
Ini adalah satu-satunya yang menangani kurung {{0}}serta hal-hal seperti {0}{1}.format("{1}", "{0}"). Harus di bagian paling atas!
Juan
11

Jika Anda ingin menangani pemisah ribuan, Anda harus benar-benar menggunakan toLocaleString () dari kelas JavaScript Number karena akan memformat string untuk wilayah pengguna.

Kelas Tanggal JavaScript dapat memformat tanggal dan waktu yang dilokalkan.

17 dari 26
sumber
1
Ini sebenarnya satu set oleh pengguna sebagai pengaturan dalam aplikasi (bukan mesin mereka pada) tetapi saya akan melihatnya, terima kasih
Chris S
tambahkan beberapa contoh sehingga semua orang dapat memahaminya dengan cepat.
Bhushan Kawadkar
9

Proyek PHPJS telah menulis implementasi JavaScript untuk banyak fungsi PHP. Karena sprintf()fungsi PHP pada dasarnya sama dengan C printf(), implementasi JavaScript-nya harus memenuhi kebutuhan Anda.

Spudley
sumber
9

Saya menggunakan ini:

String.prototype.format = function() {
    var newStr = this, i = 0;
    while (/%s/.test(newStr))
        newStr = newStr.replace("%s", arguments[i++])

    return newStr;
}

Lalu saya menyebutnya:

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

Saya punya solusi yang sangat dekat dengan Peter, tetapi berkaitan dengan jumlah dan kasus objek.

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

Mungkin bisa lebih baik untuk berurusan dengan semua kasus mendalam, tetapi untuk kebutuhan saya ini baik-baik saja.

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

PS: Fungsi ini sangat keren jika Anda menggunakan terjemahan dalam kerangka template seperti AngularJS :

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

Di mana en.json adalah sesuatu seperti

{
    "hello-message": "Hello {name}, welcome.",
    "hello-by-name": "Hello {0}, welcome."
}
Thiago Mata
sumber
bagian [^}] di regexp tidak perlu .. gunakan {(. *?)} sebagai gantinya, atau lebih baik {([\ s \ S] *?)} untuk mencocokkan baris baru juga.
rawiro
7

Satu versi yang sedikit berbeda, yang saya sukai (yang ini menggunakan token {xxx} daripada {0} argumen bernomor, ini jauh lebih mendokumentasikan diri sendiri dan lebih cocok untuk pelokalan):

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

Variasi adalah:

  var formatted = l(this);

yang memanggil fungsi lokalisasi l () terlebih dahulu.

Peter
sumber
6

Bagi mereka yang menyukai Node.JS dan util.formatfitur - fiturnya, saya baru saja mengekstraknya ke dalam bentuk JavaScript vanilla-nya (dengan hanya fungsi yang menggunakan util.format):

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

Dipanen dari: https://github.com/joyent/node/blob/master/lib/util.js

DI
sumber
6

Untuk pemformatan dasar:

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

Saya memiliki pemformat yang sedikit lebih panjang untuk JavaScript di sini ...

Anda dapat melakukan pemformatan beberapa cara:

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

Juga, jika Anda telah mengatakan ObjectBase.prototype.format (seperti dengan DateJS ) itu akan menggunakannya.

Contoh ...

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

Saya juga alias dengan .asFormat dan memiliki beberapa deteksi jika seandainya sudah ada string.format (seperti dengan MS Ajax Toolkit (Saya benci perpustakaan itu).

Tracker1
sumber
5

Untuk berjaga-jaga jika seseorang membutuhkan fungsi untuk mencegah pencemaran lingkup global, berikut adalah fungsi yang melakukan hal yang sama:

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

Kita dapat menggunakan ringan sederhana String.Format tali perpustakaan operasi untuk Naskah yang diketik.

String.Format ():

var id = image.GetId()
String.Format("image_{0}.jpg", id)
output: "image_2db5da20-1c5d-4f1a-8fd4-b41e34c8c5b5.jpg";

Format String untuk penentu:

var value = String.Format("{0:L}", "APPLE"); //output "apple"

value = String.Format("{0:U}", "apple"); // output "APPLE"

value = String.Format("{0:d}", "2017-01-23 00:00"); //output "23.01.2017"


value = String.Format("{0:s}", "21.03.2017 22:15:01") //output "2017-03-21T22:15:01"

value = String.Format("{0:n}", 1000000);
//output "1.000.000"

value = String.Format("{0:00}", 1);
//output "01"

Format String untuk Objek termasuk specifier:

var fruit = new Fruit();
fruit.type = "apple";
fruit.color = "RED";
fruit.shippingDate = new Date(2018, 1, 1);
fruit.amount = 10000;

String.Format("the {type:U} is {color:L} shipped on {shippingDate:s} with an amount of {amount:n}", fruit);
// output: the APPLE is red shipped on 2018-01-01 with an amount of 10.000
Murtaza Hussain
sumber
2

Saya tidak melihat String.formatvarian:

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;
    });
};
Jerone
sumber
2

Untuk digunakan dengan fungsi sukses jQuery.ajax (). Berikan hanya satu argumen dan ganti string dengan properti objek itu sebagai {propertyName}:

String.prototype.format = function () {
    var formatted = this;
    for (var prop in arguments[0]) {
        var regexp = new RegExp('\\{' + prop + '\\}', 'gi');
        formatted = formatted.replace(regexp, arguments[0][prop]);
    }
    return formatted;
};

Contoh:

var userInfo = ("Email: {Email} - Phone: {Phone}").format({ Email: "[email protected]", Phone: "123-123-1234" });
Raymond Powell
sumber