JavaScript equivalente a printf / String.Format


1970

Sto cercando un buon equivalente JavaScript del C / PHP printf()o dei programmatori C # / Java, String.Format()( IFormatProviderper .NET).

Il mio requisito di base è un formato di mille separatori per i numeri per ora, ma qualcosa che gestisce molte combinazioni (comprese le date) sarebbe buono.

Mi rendo conto che la libreria Ajax di Microsoft fornisce una versione di String.Format(), ma non vogliamo l'intero sovraccarico di quel framework.


2
A parte tutte le ottime risposte di seguito, potresti dare un'occhiata a questo: stackoverflow.com/a/2648463/1712065 quale IMO, è la soluzione più efficiente a questo problema.
Annie,

1
Ne ho scritto uno economico che usa la sintassi di printf simile a C.
Braden Best,

var search = [$ scope.dog, "1"]; var url = vsprintf (" terra / Servizi / dogSearch.svc / FindMe /% s /% s ", ricerca); *** Per il nodo, è possibile ottenere il modulo tramite "npm install sprintf-js"
Jenna Leaf,

Ho anche scritto una semplice funzione per raggiungere questo obiettivo; stackoverflow.com/a/54345052/5927126
AnandShanbhag

Risposte:


1110

Da ES6 in poi è possibile utilizzare stringhe di modelli:

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

Vedi Kim risposta seguito per i dettagli.


Altrimenti:

Provare sprintf () per JavaScript .


Se vuoi davvero fare un semplice metodo di formattazione da solo, non eseguire le sostituzioni in successione, ma contemporaneamente.

Perché la maggior parte delle altre proposte menzionate fallisce quando una stringa di sostituzione della sostituzione precedente contiene anche una sequenza di formati come questa:

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

Normalmente ci si aspetta che l'output sia, {1}{0}ma l'output reale è {1}{1}. Quindi esegui una sostituzione simultanea invece come nel suggerimento di fearphage .


16
Se si desidera solo una semplice conversione da numero a stringa, il num.toFixed()metodo potrebbe essere sufficiente!
Heltonbiker,

@MaksymilianMajer che sembra essere qualcosa di enormemente diverso.
Evan Carroll,

@EvanCarroll hai ragione. Al momento in cui ho scritto il commento, il repository di sprintf() for JavaScriptnon era disponibile. underscore.stringha più funzioni oltre a sprintf che si basa sprintf() for JavaScriptsull'implementazione A parte questo, la biblioteca è un progetto completamente diverso.
Maksymilian Majer,

@MaksymilianMajer giusto, solo dicendo che questa risposta è morta e il collegamento è decaduto. Deve essere completamente eliminato.
Evan Carroll,

2
Questa non dovrebbe essere più la risposta accettata. A partire da ES6 questo è integrato nel linguaggio javascript (sia nei browser che in NodeJS). Vedi la risposta di @Kim di seguito.
Ryan Shillington,

1391

Sulla base delle soluzioni precedentemente suggerite:

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

uscite

ASP è morto, ma ASP.NET è vivo! ASP {2}


Se preferisci non modificare Stringil prototipo:

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

Ti dà molto più familiare:

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

con lo stesso risultato:

ASP è morto, ma ASP.NET è vivo! ASP {2}


12
il || il trucco non funziona se args [numero] è 0. Dovrebbe fare un esplicito if () per vedere se (args [numero] === non definito).
fserb

4
nell'istruzione else della scorciatoia se, perché non semplicemente "match" anziché "'{' + numero + '}'". match dovrebbe essere uguale a quella stringa.
mikeycgto,

4
Se hai più stringhe aggiunte l'una all'altra (con l' +operatore), assicurati di mettere la stringa completa tra parentesi: in ("asd {0}"+"fas {1}").format("first", "second");caso contrario, la funzione verrà applicata solo all'ultima stringa aggiunta.
Lukas Knuth,

3
Ciò modifica leggermente e delicatamente il risultato. Immagina 'foo {0}'.format(fnWithNoReturnValue()). Al momento sarebbe tornato foo {0}. Con le tue modifiche, tornerebbe foo undefined.
fearphage,

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

491

È divertente perché Stack Overflow ha in realtà una propria funzione di formattazione per il Stringprototipo chiamato formatUnicorn. Provalo! Vai nella console e digita qualcosa del tipo:

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

Firebug

Ottieni questo risultato:

Hello, Gabriel, are you feeling OK?

È possibile utilizzare oggetti, matrici e stringhe come argomenti! Ho ottenuto il suo codice e lo ho rielaborato per produrre una nuova versione di 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;
};

Nota la Array.prototype.slice.call(arguments)chiamata intelligente : ciò significa che se lanci argomenti che sono stringhe o numeri, non un singolo oggetto in stile JSON, ottieni il String.Formatcomportamento di C # quasi esattamente.

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

Questo perché Array's sliceforzerà tutto quello che è in argumentsin una Array, se era originariamente o meno, e il keysarà l'indice (0, 1, 2 ...) di ciascun elemento dell'array costretto in una stringa (ad esempio, '0', così "\\{0\\}"per il tuo primo modello regexp).

Neat.


402
È abbastanza interessante rispondere a una domanda su StackOverflow con il codice da StackOverflow, +1
Sneakyness

5
@JamesManning Il regex consente il flag globale ( g), che può sostituire la stessa chiave più di una volta. Nell'esempio sopra, è possibile utilizzare {name}più volte nella stessa frase e sostituirle tutte.
KrekkieD

3
Sembra terribilmente fragile, a dire il vero. Cosa succede per esempio se lo nameè "blah {adjective} blah"?
Sam Hocevar,

5
@ruffin “un po 'iperbolico”? Il codice ingannato nell'interpretazione dei dati dell'utente come stringhe di formato è un'intera categoria di vulnerabilità . Il 98,44% è oltre il mediocre .
Sam Hocevar,

3
@samhocevar Non posso crederti, Little Bobby. ;) Se stai eseguendo testo elaborato da JavaScript lato client sul tuo server di database senza alcun controllo di sicurezza, Heaven ci aiuta tutti. ; ^) Guarda, non dovrebbe esserci nulla che un utente possa inviare da un client (es. Postman) che superi la sicurezza del tuo server. E si dovrebbe supporre qualcosa di pericoloso che potrebbe essere inviato da un client sarà essere. Cioè, se richiedi la sicurezza al 100% dal codice JavaScript sul lato client che è sempre modificabile dall'utente e pensi che questa funzione possa aprire un rischio per la sicurezza, stai giocando nel gioco sbagliato.
ruffin,

325

Formattazione numerica in JavaScript

Sono arrivato a questa pagina di domande sperando di trovare come formattare i numeri in JavaScript, senza introdurre ancora un'altra libreria. Ecco cosa ho trovato:

Arrotondamento di numeri in virgola mobile

sprintf("%.2f", num)Sembra essere l'equivalente di in JavaScript num.toFixed(2), che formatta numcon 2 cifre decimali, con arrotondamento (ma vedi il commento di @ ars265 di Math.roundseguito).

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

Forma esponenziale

L'equivalente di sprintf("%.2e", num)è num.toExponential(2).

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

Esadecimali e altre basi

Per stampare i numeri nella base B, provare num.toString(B). JavaScript supporta la conversione automatica da e verso le basi da 2 a 36 (inoltre, alcuni browser hanno un supporto limitato per la codifica base64 ).

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

Pagine di riferimento

Esercitazione rapida sulla formattazione dei numeri JS

Pagina di riferimento di Mozilla per toFixed () (con collegamenti a toPrecision (), toExponential (), toLocaleString (), ...)


23
Non sarebbe solo meglio racchiudere il numero letterale tra parentesi, invece di lasciare lì uno strano spazio bianco?
rmobis,

7
Probabilmente sarebbe meglio, vero. Ma il mio obiettivo è solo quello di sottolineare la trappola dell'errore di sintassi.
rescdsk,

4
Solo una nota a margine se si utilizza un browser più vecchio o si supportano browser più vecchi, alcuni browser implementati su Fixato in modo errato, usando Math.round al posto di Fix risolto è una soluzione migliore.
ars265,

7
@Raphael_ e @rescdsk: ..funziona anche:33333..toExponential(2);
Peter Jaric,

O (33333) .toExponential (2)
Jonathan

245

Da ES6 in poi è possibile utilizzare stringhe di modelli :

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

Tenere presente che le stringhe del modello sono circondate da backtick `anziché da virgolette singole.

Per maggiori informazioni:

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

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

Nota: controllare il sito mozilla per trovare un elenco di browser supportati.


61
Il problema con le stringhe di template è che sembrano essere eseguite immediatamente, rendendo il loro uso come, diciamo, una tabella di stringhe simile a i18n completamente senza valore. Non riesco a definire la stringa all'inizio e fornire i parametri da utilizzare in seguito e / o ripetutamente.
Tustin2121,

4
@ Tustin2121 Hai ragione sul fatto che non sono costruiti per essere assegnati a una variabile, il che è un po 'deformante, ma è abbastanza facile lavorare con le tendenze di esecuzione istantanea delle stringhe se le nascondi in una funzione. Vedi jsfiddle.net/zvcm70pa
inanutshellus

13
@ Tustin2121 non c'è alcuna differenza tra l'utilizzo di una stringa modello o una concatenazione di stringhe vecchio stile, il suo zucchero per la stessa cosa. Dovresti avvolgere un generatore di stringhe vecchio stile in una semplice funzione e la stessa cosa funziona bene con i modelli di stringa. const compile = (x, y) => `I can call this template string whenever I want.. x=${x}, y=${y}`...compile(30, 20)
Cchamberlain,

4
questa soluzione non funzionerà per la stringa di formato passata in variabile (dal server ad esempio)
user993954

1
@inanutshellus Funziona bene se la tua funzione template è definita sulla stessa macchina su cui viene eseguita. Per quanto ne so, non è possibile passare una funzione come JSON, quindi la memorizzazione delle funzioni del modello in un database non funziona bene.
styfle

171

jsxt, Zippo

Questa opzione si adatta meglio.

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

Con questa opzione posso sostituire stringhe come queste:

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

Con il tuo codice il secondo {0} non verrebbe sostituito. ;)


3
gist.github.com/1049426 Ho aggiornato il tuo esempio con questo approccio. Numerosi vantaggi, tra cui il salvataggio dell'implementazione nativa, se esiste, il restringimento, ecc. Ho provato a rimuovere le espressioni regolari, ma ho bisogno di aiuto per la sostituzione globale. : - /
tbranyen,

6
purtroppo
jsxt ha

109

Uso questa semplice funzione:

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

È molto simile a string.format:

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

1
perché +=?, dovrebbeformatted = this.replace("{" + arg + "}", arguments[arg]);
guilin 桂林

2
Penso che il codice non sia ancora corretto. Quello corretto dovrebbe essere come pubblicato da Filipiz .
wenqiang,

3
Per riferimento, for...innon funzionerà in tutti i browser come previsto da questo codice. Passerà in rassegna tutte le proprietà enumerabili, che in alcuni browser includeranno arguments.lengthe in altri non includeranno nemmeno gli argomenti stessi. In ogni caso, se Object.prototypeaggiunto a, eventuali aggiunte saranno probabilmente incluse nel mazzo. Il codice dovrebbe utilizzare un forciclo standard , anziché for...in.
cHao,

3
Ciò non riesce se una sostituzione precedente contiene anche una stringa di formato:"{0} is dead, but {1} is alive!".format("{1}", "ASP.NET") === "ASP.NET is dead, but ASP.NET is alive!"
Gumbo

6
La variabile argè globale. Devi invece fare questo:for (var arg in arguments) {
Pauan il

68

Per gli utenti di Node.js esiste una util.formatfunzionalità simile a printf:

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

1
Questo non supporta% x dal Nodo v0.10.26
Max Krohn

Non supporta neanche modificatori di larghezza e allineamento (ad es. %-20s %5.2f)
MGF

Ho dovuto scorrere fino in fondo alla pagina per vedere questa risposta utile.
Donato,

53

Sono sorpreso che nessuno l'abbia usato reduce , questa è una funzione JavaScript concisa e potente nativa.

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"

Come funziona:

ridurre applica una funzione a fronte di un accumulatore e ogni elemento dell'array (da sinistra a destra) per ridurla a un singolo valore.

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
Ecco una versione che utilizza questo approccio per creare una printffunzione semplificata : jsfiddle.net/11szrbx9
Dem Pilafian

1
Ed eccone un altro che utilizza ES6, in una riga:(...a) => {return a.reduce((p: string, c: any) => p.replace(/%s/, c));
dtasev il

Non è necessario String.prototype.formatin ES6: ((a,b,c)=>`${a}, ${b} and ${c}`)(...['me', 'myself', 'I'])(nota che questo è un po 'ridondante per adattarsi meglio al tuo esempio)
Tino

Dovresti implementare funzioni di sostituzione per ciascuno degli identificatori di printftipo e includere la logica per i prefissi di riempimento. Iterare la stringa di formato in modo ragionevole sembra essere la sfida minore qui, imho. Soluzione accurata se hai solo bisogno di sostituzioni di stringhe, però.
collapsar

51

Ecco un'implementazione minima di sprintf in JavaScript: fa solo "% s" e "% d", ma ho lasciato spazio per estenderlo. È inutile per il PO, ma altre persone che si imbattono in questa discussione proveniente da Google potrebbero trarne beneficio.

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

Esempio:

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

Contrariamente a soluzioni simili nelle risposte precedenti, questa esegue tutte le sostituzioni in una volta sola , quindi non sostituirà parti di valori sostituiti in precedenza.



24

Aggiungendo alla zippoxerrisposta, utilizzo questa funzione:

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.

Ho anche una versione non prototipo che uso più spesso per la sua sintassi simile a 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

Aggiornamento ES 2015

Tutte le fantastiche novità di ES 2015 rendono tutto molto più semplice:

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

Ho pensato che dato che questo, come i più vecchi, in realtà non analizza le lettere, potrebbe anche usare un solo token %%. Questo ha il vantaggio di essere ovvio e di non rendere difficile l'uso di un singolo %. Tuttavia, se è necessario %%per qualche motivo, è necessario sostituirlo con se stesso:

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

3
questa risposta è stata ottima per un rapido copia incolla in una funzione esistente. Non è necessario alcun download, ecc.
Nick,

@Nick sì, questa è l'idea :)
Braden Best il

21

+1 Zippo con l'eccezione che il corpo della funzione deve essere come di seguito o altrimenti aggiunge la stringa corrente ad ogni iterazione:

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

1
Non ha funzionato su Firefox. Il debugger mostra arg come non definito.
xiao 啸

Non sostituisce il secondo carattere 'The {0} is dead. Don\'t code {0}. Code {1} that is open source!'.format('ASP', 'PHP'); il risultato diventa The ASP is dead. Don't code {0}. Code PHP that is open source!. Un'altra cosa for(arg in arguments)non funziona in IE. ho sostituito con for (arg = 0; arg <arguments.length; arg++)
samarjit samanta il

2
Per riferimento, for...innon funzionerà in tutti i browser come previsto da questo codice. Passerà in rassegna tutte le proprietà enumerabili, che in alcuni browser includeranno arguments.lengthe in altri non includeranno nemmeno gli argomenti stessi. In ogni caso, se Object.prototypeaggiunto a, eventuali aggiunte saranno probabilmente incluse nel mazzo. Il codice dovrebbe utilizzare un forciclo standard , anziché for...in.
cHao,

Dovresti proporre una modifica della risposta anziché una risposta duplicata. Questo duplica questa risposta
RousseauAlexandre

19

Voglio condividere la mia soluzione per il "problema". Non ho reinventato la ruota ma cerco di trovare una soluzione basata su ciò che già JavaScript fa. Il vantaggio è che puoi ottenere gratuitamente tutte le conversioni implicite. L'impostazione della proprietà prototipo $ di String fornisce una sintassi molto piacevole e compatta (vedere gli esempi di seguito). Forse non è il modo più efficiente, ma nella maggior parte dei casi gestire l'output non deve essere super ottimizzato.

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

Ecco alcuni esempi:

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'

purtroppo almeno # e + non sono implementati per i float. ecco un riferimento per la funzione in c: tutorialspoint.com/c_standard_library/c_function_sprintf.htm
Daniel,


14

Uso una piccola libreria chiamata String.format per JavaScript che supporta la maggior parte delle funzionalità di stringa di formato (incluso il formato di numeri e date) e utilizza la sintassi .NET. Lo script stesso è inferiore a 4 kB, quindi non crea sovraccarico.


Ho dato un'occhiata a quella biblioteca e sembra davvero eccezionale. Ero incazzato quando ho visto che il download era un EXE. Di che diamine si tratta? Non scaricato.
jessegavin,

Spesso un archivio scaricabile che è un EXE non è altro che un "ZIP autoestraente". Eseguilo e si decomprimerà da solo. Questo è abbastanza conveniente MA perché assomiglia molto al malware, il formato non è più usato sul web così spesso.
Chuck Kollars,

Sebbene questo collegamento possa rispondere alla domanda, è meglio includere qui le parti essenziali della risposta e fornire il collegamento come riferimento. Le risposte di solo collegamento possono diventare non valide se la pagina collegata cambia.
starmole,

@starmole il link è verso una libreria javascript (minimizzata) da 4 kB . Non credo che incollarlo nella risposta sia una buona idea.
Ivarni,

Hai ragione, non sarebbe meglio. Ho appena ricevuto questo commento per la revisione casuale - e commentato prima di non gradirlo. Per me stackoverflow è meglio quando si forniscono spiegazioni piuttosto che soluzioni già pronte (che è il collegamento). Inoltre, non voglio incoraggiare le persone a pubblicare o scaricare codice black-box.
starmole,

14

Molto elegante:

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

Il merito va a (collegamento non funzionante) https://gist.github.com/0i0/1519811


Questo è l'unico che gestisce le parentesi di escape {{0}}e cose del genere {0}{1}.format("{1}", "{0}"). Dovrebbe essere al top!
Juan,

11

Se stai cercando di gestire il separatore delle migliaia, dovresti davvero usare toLocaleString () dalla classe Numero JavaScript poiché formatterà la stringa per la regione dell'utente.

La classe Date JavaScript può formattare date e orari localizzati.


1
In realtà è un set da parte dell'utente come impostazione nell'applicazione (non la macchina è accesa) ma darò un'occhiata, grazie
Chris S

aggiungi alcuni esempi in modo che tutti possano capirlo rapidamente.
Bhushan Kawadkar,


9

Io uso questo:

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

    return newStr;
}

Quindi lo chiamo:

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

9

Ho una soluzione molto vicina a quella di Peter, ma si occupa di numeri e case object.

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

Forse potrebbe essere ancora meglio affrontare tutti i casi profondi, ma per le mie esigenze va bene.

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

PS: questa funzione è molto interessante se stai usando traduzioni in framework di template come AngularJS :

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

Dove en.json è qualcosa di simile

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

la parte [^}] in regexp è inutile .. usa invece {(. *?)}, o meglio {([\ s \ S] *?)} per abbinare anche newline.
Rawiro,

7

Una versione leggermente diversa, quella che preferisco (questa usa token {xxx} anziché {0} argomenti numerati, è molto più autocompattante e si adatta molto meglio alla localizzazione):

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

Una variazione sarebbe:

  var formatted = l(this);

che chiama prima una funzione di localizzazione l ().



6

Per coloro a cui piace Node.JS e le sue util.formatfunzionalità, l'ho appena estratto nel suo modulo JavaScript vanilla (con solo le funzioni utilizzate da 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;
};

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


6

Per la formattazione di base:

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

5

Ho un formattatore leggermente più lungo per JavaScript qui ...

Puoi eseguire la formattazione in diversi modi:

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

Inoltre, se hai detto un ObjectBase.prototype.format (come con DateJS ) lo userà.

Esempi ...

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

Ho anche creato un'alleanza con .asFormat e ho un po 'di rilevamento nel caso in cui ci sia già un string.format (come con MS Ajax Toolkit (odio quella libreria).


5

Nel caso in cui qualcuno abbia bisogno di una funzione per prevenire l'inquinamento dell'ambito globale, ecco la funzione che fa lo stesso:

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

3

Possiamo usare una semplice libreria di operazioni stringa String.Format per Typescript.

String.Format ():

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

Formato stringa per specificatori:

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"

Formato stringa per oggetti inclusi identificatori:

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

Non ho visto la String.formatvariante:

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

Da utilizzare con le funzioni di successo jQuery.ajax (). Passa solo un singolo argomento e la stringa sostituisce con le proprietà di quell'oggetto come {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;
};

Esempio:

var userInfo = ("Email: {Email} - Phone: {Phone}").format({ Email: "someone@somewhere.com", Phone: "123-123-1234" });
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.