Printf / String.Format ile aynı JavaScript


1970

C / PHP printf()veya C # / Java programcıları String.Format()( IFormatProvider.NET için) iyi bir JavaScript eşdeğer arıyorum .

Benim temel gereksinim şimdilik sayılar için bin ayırıcı biçimidir, ancak çok sayıda kombinasyon (tarihler dahil) işleyen bir şey iyi olurdu.

Microsoft'un Ajax kütüphanesinin bir sürümünü String.Format()sunduğunu anlıyorum, ancak bu çerçevenin tüm yükünü istemiyoruz.


2
Aşağıdaki tüm harika cevapların yanı sıra, buna bir göz atmak isteyebilirsiniz: hangi IMO'nun bu soruna en verimli çözüm olduğunu stackoverflow.com/a/2648463/1712065 .
Annie

1
C benzeri printf sözdizimini kullanan ucuz bir tane yazdım .
Braden Best

var search = [$ scope.dog, "1"]; var url = vsprintf (" earth / Services / dogSearch.svc / FindMe /% s /% s ", arama); *** Düğüm için, modülünüzü "npm install sprintf-js" ile alabilirsiniz
Jenna Leaf

Bunu başarmak için basit bir işlev de yazdım; stackoverflow.com/a/54345052/5927126
AnandShanbhag

Yanıtlar:


1109

ES6'dan itibaren şablon dizelerini kullanabilirsiniz:

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

Ayrıntılar için Kim'in cevabına bakınız.


Aksi takdirde:

JavaScript için sprintf () yöntemini deneyin .


Gerçekten kendi başınıza basit bir format yöntemi yapmak istiyorsanız, değiştirmeleri art arda yapmayın, aynı anda yapın.

Belirtilen diğer tekliflerin çoğu, önceki değiştirme yerine dizesi de böyle bir biçim sırası içerdiğinde başarısız olduğundan:

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

Normalde çıktının olmasını beklersiniz {1}{0}ancak gerçek çıktıdır {1}{1}. Öyleyse korkunun önerisinde olduğu gibi eşzamanlı olarak yerine koyun .


16
Yalnızca basit bir sayı-dizge dönüşümü isteniyorsa num.toFixed()yöntem yeterli olabilir!
heltonbiker

@MaksymilianMajer çok farklı bir şey gibi görünüyor.
Evan Carroll

@EvanCarroll haklısın. Yorum yazdığım sırada depo sprintf() for JavaScriptmevcut değildi. uygulamaya underscore.stringdayalı sprintf dışında daha fazla özelliğe sahiptir sprintf() for JavaScript. Bunun dışında kütüphane tamamen farklı bir projedir.
Maksymilian Majer

@MaksymilianMajer haklı, sadece bu cevabın öldüğünü ve bağlantının çürüdüğünü söyledi. Tamamen tasfiye edilmesi gerekiyor.
Evan Carroll

2
Bu artık cevap olarak kabul edilmemelidir. ES6'dan itibaren bu javascript dilinde yerleşiktir (hem tarayıcılarda hem de NodeJS'de). Aşağıdaki @Kim'in cevabına bakınız.
Ryan Shillington

1390

Daha önce önerilen çözümlere dayanarak:

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

çıktılar

ASP öldü, ama ASP.NET yaşıyor! ASP {2}


String'In prototipini değiştirmemeyi tercih ediyorsanız :

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

Size çok daha tanıdık geliyor:

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

aynı sonuçla:

ASP öldü, ama ASP.NET yaşıyor! ASP {2}


12
|| args [number] 0 ise hile çalışmaz.
fserb

4
"" {'+ number +'} "" yerine "eşleme" yapmıyorsa, stenografinin else ifadesinde. eşleme bu dizeye eşit olmalıdır.
mikeycgto

4
Birbirine birden fazla dize +ekliyorsanız ( -operator ile), tam Dize'i parantez içine aldığınızdan emin olun: ("asd {0}"+"fas {1}").format("first", "second");Aksi takdirde, işlev yalnızca son eklenen dize uygulanır.
Lukas Knuth

3
Bu, sonucu hafifçe ve ustaca değiştirir. Hayal edin 'foo {0}'.format(fnWithNoReturnValue()). Şu anda geri dönecekti foo {0}. Yaptığınız değişiklikler ile geri dönecekti foo undefined.
fearphage

2
@avenmore: / \ {(\ d +) \} / g
Hozuki

491

Komik çünkü Stack Overflow adlı Stringprototip için kendi biçimlendirme fonksiyonuna sahip formatUnicorn. Dene! Konsola gidin ve şöyle bir şey yazın:

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

kundakçı

Bu çıktıyı alırsınız:

Hello, Gabriel, are you feeling OK?

Nesneleri, dizileri ve karakter dizilerini argüman olarak kullanabilirsiniz! Kodunu aldım ve yeni bir sürümünü üretmek için yeniden çalıştım 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;
};

Akıllı Array.prototype.slice.call(arguments)çağrıyı not edin - bu, tek bir JSON stili nesne değil, dizeler veya sayılar olan bağımsız değişkenler atarsanız, C # 'ın String.Formatdavranışını neredeyse tam olarak alırsınız demektir .

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

Çünkü yıllardan bu Arrays' slicene varsa zorlayacaktır argumentsbir içine Arrayorijinal olup olmadığı, ve keybu yüzden, örneğin '0'(bir dize zorlandığını her dizi elemanının indeksi (0, 1, 2 ...) olacaktır "\\{0\\}"ilk normal ifadeniz için).

Temiz.


402
Stackoverflow, +1 kodu ile stackoverflow ile ilgili bir soruya cevap vermek çok güzel
Sneakyness

5
@JamesManning Normal ifade g, aynı anahtarı bir kereden fazla değiştirebilen genel bayrağa ( ) izin verir . Yukarıdaki örnekte {name}, aynı cümlede birden çok kez kullanabilir ve hepsinin değiştirilmesini sağlayabilirsiniz.
KrekkieD

3
Dürüst olmak gerekirse, bu çok kırılgan görünüyor. Ne mesela olur nameise "blah {adjective} blah"?
sam hocevar

5
@ruffin “biraz hiperbolik” mi? Kullanıcı verilerini biçim dizeleri olarak yorumlamaya çalışan kod, tüm güvenlik açıkları kategorisidir . % 98,44'ü vasatın ötesindedir .
sam hocevar

3
@samhocevar Sana inanamıyorum Küçük Bobby beni Masaya yatırdı. ;) Herhangi bir güvenlik denetimi yapmadan veritabanı sunucunuzda istemci tarafı JavaScript tarafından işlenen metin çalıştırıyorsanız, cennet hepimize yardımcı olur. ; ^) Bakın, herhangi bir kullanıcının bir istemciden (örneğin Postacı) gönderebileceği ve sunucunuzun güvenliğini aşan hiçbir şey olmamalıdır . Ve gereken bir istemciden gönderilebilir tehlikeli bir şey varsayalım edecek olması. Yani, her zaman kullanıcı tarafından düzenlenebilir olan istemci tarafı JavaScript kodundan % 100 güvenliğe ihtiyacınız varsa ve bu işlevin bir güvenlik riski açabileceğini düşünüyorsanız, yanlış oyunda oynuyorsunuz.
ruffin

325

JavaScript'te Sayı Biçimlendirme

Bu soru sayfasına , başka bir kütüphane tanıtmadan JavaScript'te sayıları nasıl biçimlendireceğinizi bulmayı umuyorum . İşte bulduğum:

Kayan noktalı sayıların yuvarlanması

sprintf("%.2f", num)JavaScript'teki eşdeğeri, yuvarlama ile 2 ondalık basamağa num.toFixed(2)formatlanan gibi görünüyor num(ancak aşağıdaki @ ars265'in yorumuna Math.roundbakın).

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

Üstel form

Eşdeğer sprintf("%.2e", num)IS num.toExponential(2).

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

Onaltılık ve diğer bazlar

B tabanındaki sayıları yazdırmak için deneyin num.toString(B). JavaScript 2'den 36'ya kadar olan tabanlara ve 36'dan otomatik dönüşümü destekler (ayrıca bazı tarayıcılarda base64 kodlaması için sınırlı destek vardır ).

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

Referans Sayfaları

JS numarası biçimlendirmesi hakkında hızlı eğitim

ToFixed () için Mozilla referans sayfası ( toPrecision (), toExponential (), toLocaleString (), ... bağlantılarına sahip)


23
Orada tuhaf bir boşluk bırakmak yerine, sayı dizgesini parantez içine almak daha iyi olmaz mıydı?
rmobis

7
Muhtemelen daha iyi görünecekti, doğru. Ama benim amacım sadece sözdizimi hatası tuzağına işaret etmek.
rescdsk

4
Daha eski bir tarayıcı kullanıyorsanız veya daha eski tarayıcıları destekliyorsanız, bir yan not, toFixed yanlış uygulanmış, toFixed yerine Math.round'u kullanan bazı tarayıcılar daha iyi bir çözümdür.
ars265

7
@Raphael_ ve @rescdsk: ..ayrıca çalışıyor:33333..toExponential(2);
Peter Jaric

Veya Üstel (2)
Jonathan

245

ES6'dan itibaren şablon dizelerini kullanabilirsiniz :

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

Şablon dizelerinin tırnak işaretleri yerine tek tırnaklarla çevrili olduğunu unutmayın .

Daha fazla bilgi için:

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

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

Not: Desteklenen tarayıcıların bir listesini bulmak için mozilla sitesini kontrol edin.


61
Şablon dizeleriyle ilgili sorun, derhal yürütüldükleri ve i18n benzeri bir dize tablosu olarak tamamen değersiz olduklarını görüyorlar. Dizeyi erken tanımlayamıyorum ve daha sonra ve / veya tekrar tekrar kullanmak için parametreleri sağlayamıyorum.
Tustin2121

4
@ Tustin2121 Bir değişkene atanacak şekilde inşa edilmedikleri konusunda haklısınız, bu biraz zihin çözgüdür, ancak bir işlevde gizlerseniz şablonlanmış dizelerin anlık yürütme tendancileri ile çalışmak yeterince kolaydır. Bkz. Jsfiddle.net/zvcm70pa
inanutshellus

13
@ Tustin2121, bir şablon dizesi veya eski stil dizesi birleştirme, aynı şey için şekeri kullanma arasında bir fark yoktur. Eski bir stil dizgi jeneratörünü basit bir fonksiyona sarmanız gerekir ve aynı şey dize şablonları ile iyi çalışır. const compile = (x, y) => `I can call this template string whenever I want.. x=${x}, y=${y}`...compile(30, 20)
cchamberlain

4
bu çözüm, değişkene geçirilen biçim dizesi için çalışmaz (örneğin sunucudan)
user993954

1
@ inanutshellus Şablon işleviniz yürütüldüğü makinede tanımlanmışsa bu iyi çalışır. Bildiğim kadarıyla JSON olarak bir işlevi geçiremezsiniz, bu nedenle şablon işlevlerini bir veritabanında depolamak işe yaramaz.
styfle

171

jsxt, Zippo

Bu seçenek daha iyi uyuyor.

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

Bu seçenekle aşağıdaki gibi dizeleri değiştirebilirsiniz:

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

Kodunuzla ikinci {0} değiştirilmez. ;)


3
gist.github.com/1049426 Örneğinizi bu yaklaşımla güncelledim. Varsa yerel uygulamayı kaydetme, dizgi oluşturma, vb dahil olmak üzere çok sayıda fayda. Düzenli ifadeleri kaldırmayı denedim, ancak küresel değiştirme için gerekli welp tür. : - /
tbranyen

6
jsxt ne yazık ki GPL lisanslı
AndiDog

109

Bu basit işlevi kullanıyorum:

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

Bu string.format'a çok benzer:

"{0} is dead, but {1} is alive!".format("ASP", "ASP.NET")

1
neden +=formatted = this.replace("{" + arg + "}", arguments[arg]);
?,

2
Kodun hala doğru olmadığını düşünüyorum. Doğru olanı, Filipiz'in yayınladığı gibi olmalıdır .
wenqiang

3
Referans olarak, for...inbu kod beklediği için her tarayıcıda çalışmaz. Bazı tarayıcılarda bulunacak arguments.lengthve diğerlerinde argümanları bile içermeyecek tüm numaralandırılabilir özellikler üzerinde döngü yapar . Her durumda, eğer Object.prototypeeklenirse, herhangi bir ekleme muhtemelen gruba dahil edilecektir. Kod foryerine standart bir döngü kullanıyor olmalıdır for...in.
cHao

3
Önceki bir değiştirme de bir biçim dizesi içeriyorsa bu başarısız olur:"{0} is dead, but {1} is alive!".format("{1}", "ASP.NET") === "ASP.NET is dead, but ASP.NET is alive!"
Gumbo

6
Değişken arggeneldir. Bunun yerine bunu yapmalısınız:for (var arg in arguments) {
Pauan

68

Node.js kullanıcıları için util.formatprintf benzeri işlevler vardır:

util.format("%s world", "Hello")

1
Bu, v0.10.26 düğümünden% x değerini desteklemiyor
Max Krohn

Genişlik ve hizalama değiştiricileri de desteklemez (örn. %-20s %5.2f)
FGM

Bu yararlı yanıtı görmek için sayfayı aşağı kaydırmam gerekiyordu.
Donato

53

Kimsenin kullanılmadığına şaşırdım reduce, bu yerel bir özlü ve güçlü bir JavaScript işlevidir.

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"

Nasıl çalışır:

reduce işlevi, bir akümülatöre ve dizideki her öğeye (soldan sağa) tek bir değere azaltmak için bir işlev uygular.

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


4
Basitleştirilmiş bir printfişlev oluşturmak için bu yaklaşımı kullanan bir sürüm : jsfiddle.net/11szrbx9
Dem Pilafian

1
Ve burada bir satırda ES6 kullanan bir tane daha:(...a) => {return a.reduce((p: string, c: any) => p.replace(/%s/, c));
dtasev

String.prototype.formatES6'da gerek yok : ((a,b,c)=>`${a}, ${b} and ${c}`)(...['me', 'myself', 'I'])(bunun örneğinize daha iyi uyması için biraz gereksiz olduğunu unutmayın)
Tino

Her bir printftür belirteci için değiştirme işlevleri uygulamanız ve dolgu önekleri için mantık eklemeniz gerekir. Biçim dizgisini mantıklı bir şekilde yinelemek, burada küçük bir zorluk gibi görünüyor, imho. Gerçi sadece dize değiştirmeye ihtiyacınız varsa temiz bir çözüm.
colpsar

51

İşte sprintf JavaScript'te minimal bir uygulama: sadece "% s" ve "% d" yapar, ancak uzatılması için yer bıraktım. OP için işe yaramaz, ancak Google'dan gelen bu konuya rastlayan diğer kişiler bundan faydalanabilir.

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

Misal:

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

Önceki yanıtlarda benzer çözümler aksine, bu bir bütün değiştirmelerin yapar tek seferde daha önce değiştirilen değerleri parçalarını yerini almayacak, böylece.



24

Cevabına ek olarak zippoxer, bu işlevi kullanıyorum:

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.

Ayrıca Java benzeri sözdizimi için daha sık kullandığım prototip olmayan bir sürümü var:

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 güncellemesi

ES 2015'teki tüm harika yeni şeyler bunu çok daha kolay hale getiriyor:

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

Bunun, eskileri gibi, harfleri gerçekten ayrıştırmadığından, tek bir jeton da kullanabileceğini düşündüm %%. Bu, açık olma ve tek bir kullanımı zorlaştırmama avantajına sahiptir %. Bununla birlikte, herhangi bir %%nedenden dolayı ihtiyacınız varsa , onu kendinizle değiştirmeniz gerekir:

format("I love percentage signs! %%", "%%");
// "I love percentage signs! %%"

3
bu cevap, mevcut bir işleve hızlı bir kopya yapıştırmak için harikaydı. İndirme gerektirmez vb.
Nick

@Nick yep, fikir bu :)
Braden Best

21

+1 Zippo, işlev gövdesinin aşağıdaki gibi olması dışında veya başka bir şekilde geçerli dizeyi her yinelemeye ekler:

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

1
Firefox'ta çalışmadı. Hata ayıklayıcı arg öğesini tanımsız olarak gösterir.
xiao 啸

'The {0} is dead. Don\'t code {0}. Code {1} that is open source!'.format('ASP', 'PHP'); Sonucun elde ettiği ikinci karakterin yerini almaz The ASP is dead. Don't code {0}. Code PHP that is open source!. for(arg in arguments)IE'de bir şey daha çalışmıyor. yerine for (arg = 0; arg <arguments.length; arg++)
samarjit samanta

2
Referans olarak, for...inbu kod beklediği için her tarayıcıda çalışmaz. Bazı tarayıcılarda bulunacak arguments.lengthve diğerlerinde argümanları bile içermeyecek tüm numaralandırılabilir özellikler üzerinde döngü yapar . Her durumda, eğer Object.prototypeeklenirse, herhangi bir ekleme muhtemelen gruba dahil edilecektir. Kod foryerine standart bir döngü kullanıyor olmalıdır for...in.
cHao

Yinelenen cevap yerine bir cevap düzenlemesi önermelisiniz. Bu cevap bu cevabı
RousseauAlexandre

19

'Sorun' için çözümümü paylaşmak istiyorum. Tekerleği yeniden icat etmedim, ancak JavaScript'in zaten ne yaptığına dayalı bir çözüm bulmaya çalışıyor. Avantajı, tüm örtük dönüşümleri ücretsiz almanızdır. $ Dizesinin prototip özelliğini ayarlamak çok hoş ve kompakt bir sözdizimi sağlar (aşağıdaki örneklere bakın). Belki de en etkili yol değildir, ancak çoğu durumda çıktıyla uğraşmak için süper optimize edilmesi gerekmez.

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

İşte birkaç örnek:

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'

ne yazık ki şamandıralar için en az # ve + uygulanmamıştır. İşte c işlevinde bir başvuru: tutorialspoint.com/c_standard_library/c_function_sprintf.htm
Daniel


14

Ben biçim dizesi yeteneklerinin (sayı ve tarih biçimi dahil) çoğunu destekleyen ve .NET sözdizimini kullanan JavaScript için String.format adlı küçük bir kütüphane kullanın. Betiğin kendisi 4 kB'den küçüktür, bu yüzden fazla yük oluşturmaz.


Bu kütüphaneye bir göz attım ve gerçekten harika görünüyor. Ben indirme bir EXE olduğunu görünce ben sinirli oldu. Bu ne halt? İndirmediniz.
jessegavin

Genellikle bir EXE olan indirilebilir bir arşiv "kendi kendine açılan ZIP" den başka bir şey değildir. Yürütün ve kendini açacaktır. Bu oldukça kullanışlıdır, ancak kötü amaçlı yazılımlara çok benziyor, biçim artık web'de artık kullanılmıyor.
Chuck Kollars

Bu bağlantı soruyu cevaplayabilse de, cevabın temel kısımlarını buraya eklemek ve bağlantıyı referans olarak sağlamak daha iyidir. Bağlantı verilen sayfa değişirse, yalnızca bağlantı yanıtları geçersiz olabilir.
starmole

@starmole bağlantı (küçültülmüş) 4 kB'lik bir javascript kütüphanesine bağlanır . Cevaba yapıştırmanın iyi bir fikir olduğuna inanmıyorum.
ivarni

Daha iyi olmayacağını söyleyerek haklısın. Ben sadece rastgele inceleme için bu yorumu aldım - ve beğenmeden önce yorumladı. Bana göre stackoverflow hazır çözümler (açıklama olan) yerine açıklamalar sağlarken daha iyidir. Ayrıca insanları kara kutu kodu göndermeye veya indirmeye teşvik etmek istemiyorum.
starmole

14

Çok zarif:

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

Kredi gidiyor (bozuk bağlantı) https://gist.github.com/0i0/1519811


Kaçış köşeli parantezleri ve {{0}}bunun gibi şeyleri işleyen tek kişi budur {0}{1}.format("{1}", "{0}"). En tepede olmalı!
Juan

11

Binlik ayırıcıyı işlemek istiyorsanız, JavaScript Number sınıfından toLocaleString () yöntemini kullanmanız gerekir; çünkü bu, kullanıcının bölgesi için dizeyi biçimlendirir.

JavaScript Date sınıfı yerelleştirilmiş tarihleri ​​ve saatleri biçimlendirebilir.


1
Aslında uygulamada bir ayar olarak kullanıcı tarafından bir set (makine onların değil) ama ben bir göz atacağım, teşekkürler
Chris S

herkesin çabucak anlayabilmesi için bazı örnekler ekleyin.
Bhushan Kawadkar


9

Bunu kullanıyorum:

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

    return newStr;
}

Sonra ben diyorum:

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

9

Peter'e çok yakın bir çözümüm var, ama sayı ve nesne davasıyla ilgileniyor.

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

Belki de tüm derin vakalarla başa çıkmak daha iyi olabilir, ama ihtiyaçlarım için bu iyi.

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

Not: AngularJS gibi şablon çerçevelerinde çeviriler kullanıyorsanız bu işlev çok güzeldir :

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

En.json gibi bir şey olduğunda

{
    "hello-message": "Hello {name}, welcome.",
    "hello-by-name": "Hello {0}, welcome."
}

normal ifadedeki [^}] bölüm gereksizdir .. bunun yerine {(. *?)} kullanın veya satırsonu ile eşleştirmek için daha iyi {([\ s \ S] *?)} kullanın.
rawiro

7

Çok biraz farklı bir sürüm, tercih ettiğim (bu, {0} numaralı bağımsız değişkenler yerine {xxx} belirteçlerini kullanıyor, bu çok daha fazla kendi kendini belgeliyor ve yerelleştirmeye çok daha uygun):

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

Bir varyasyon şöyle olacaktır:

  var formatted = l(this);

önce l () yerelleştirme işlevini çağıran.



6

Gibi olanlar için node.js ve util.formatözelliği, sadece (util.format kullanımları sadece işlevleriyle) onun vanilya JavaScript forma dışarı çıkarılan ettik:

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

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


6

Temel biçimlendirme için:

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

5

Burada JavaScript için biraz daha uzun bir biçimlendiricim var ...

Biçimlendirmeyi birkaç yolla yapabilirsiniz:

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

Ayrıca, bir ObjectBase.prototype.format ( DateJS ile olduğu gibi ) söylediyseniz , bunu kullanır.

Örnekler ...

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

Ayrıca .asFormat ile aliased ve zaten bir string.format (MS Ajax Toolkit gibi (ben bu kütüphaneden nefret ediyorum) durumunda bazı algılama var.


5

Birisinin küresel kapsamı kirletmeyi önlemek için bir işleve ihtiyacı olması durumunda, işte aynı şeyi yapan işlev:

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

3

Typescript için basit bir String.Format string operasyon kütüphanesi kullanabiliriz .

String.Format ():

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

Belirticiler için Dize Biçimi:

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"

Belirticiler dahil Nesneler için Dize Biçimi:

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

2

String.formatVaryantı görmedim :

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

2

JQuery.ajax () başarı işlevleriyle kullanım için. Yalnızca tek bir bağımsız değişken iletin ve dizenin yerine {propertyName} olarak bu nesnenin özelliklerini ekleyin:

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

Misal:

var userInfo = ("Email: {Email} - Phone: {Phone}").format({ Email: "someone@somewhere.com", Phone: "123-123-1234" });
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.