JavaScript entspricht printf / String.Format


1970

Ich suche ein gutes JavaScript-Äquivalent zu C / PHP printf()oder für C # / Java-Programmierer String.Format()( IFormatProviderfür .NET).

Meine Grundvoraussetzung ist vorerst ein Tausendertrennzeichenformat für Zahlen, aber etwas, das viele Kombinationen (einschließlich Datumsangaben) verarbeitet, wäre gut.

Ich weiß, dass die Ajax- Bibliothek von Microsoft eine Version von bietet String.Format(), aber wir wollen nicht den gesamten Overhead dieses Frameworks.


2
Abgesehen von all den großartigen Antworten unten möchten Sie vielleicht einen Blick auf diese werfen : stackoverflow.com/a/2648463/1712065 welche IMO die effizienteste Lösung für dieses Problem ist.
Annie

1
Ich habe eine billige geschrieben , die C-ähnliche printf-Syntax verwendet.
Braden Best

var search = [$ scope.dog, "1"]; var url = vsprintf (" earth / Services / dogSearch.svc / FindMe /% s /% s ", Suche); *** Für Knoten können Sie Ihr Modul von "npm install sprintf-js"
Jenna Leaf

Ich habe auch eine einfache Funktion geschrieben, um dies zu erreichen; stackoverflow.com/a/54345052/5927126
AnandShanbhag

Antworten:


1109

Ab ES6 können Sie Vorlagenzeichenfolgen verwenden:

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

Siehe Kims Antwort unten für Details.


Andernfalls:

Versuchen Sie sprintf () für JavaScript .


Wenn Sie wirklich eine einfache Formatierungsmethode selbst ausführen möchten, führen Sie die Ersetzungen nicht nacheinander durch, sondern gleichzeitig.

Da die meisten anderen genannten Vorschläge fehlschlagen, wenn eine Ersetzungszeichenfolge der vorherigen Ersetzung ebenfalls eine Formatsequenz wie die folgende enthält:

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

Normalerweise würden Sie erwarten, dass die Ausgabe ist, {1}{0}aber die tatsächliche Ausgabe ist {1}{1}. Führen Sie stattdessen einen gleichzeitigen Ersatz durch, wie im Vorschlag von Fearphage .


16
Wenn nur eine einfache Konvertierung von Zahlen in Zeichenfolgen gewünscht wird, ist die num.toFixed()Methode möglicherweise ausreichend!
Heltonbiker

@MaksymilianMajer das scheint etwas ganz anderes zu sein.
Evan Carroll

@EvanCarroll du hast recht. Zu dem Zeitpunkt, als ich den Kommentar schrieb, war das Repository von sprintf() for JavaScriptnicht verfügbar. underscore.stringhat neben sprintf, das auf der sprintf() for JavaScriptImplementierung basiert, weitere Funktionen . Davon abgesehen ist die Bibliothek ein ganz anderes Projekt.
Maksymilian Majer

@MaksymilianMajer richtig, nur zu sagen, dass diese Antwort tot ist und der Link verfallen ist. Es muss vollständig gereinigt werden.
Evan Carroll

2
Diese Antwort sollte nicht mehr akzeptiert werden. Ab ES6 ist dies in die Javascript-Sprache integriert (sowohl in Browsern als auch in NodeJS). Siehe die Antwort von @Kim unten.
Ryan Shillington

1390

Aufbauend auf den zuvor vorgeschlagenen Lösungen:

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

Ausgänge

ASP ist tot, aber ASP.NET lebt! ASP {2}


Wenn Sie den StringPrototyp nicht ändern möchten :

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

Gibt Ihnen das viel vertrautere:

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

mit dem gleichen Ergebnis:

ASP ist tot, aber ASP.NET lebt! ASP {2}


12
die || Trick funktioniert nicht, wenn args [number] 0 ist. Sollte explizit if () ausführen, um zu sehen, ob (args [number] === undefined).
fserb

4
in der else-Anweisung der Kurzschrift if, warum nicht einfach "match" anstelle von "'{' + number + '}'"? Übereinstimmung sollte gleich dieser Zeichenfolge sein.
Mikeycgto

4
Wenn Sie mehrere Zeichenfolgen aneinander angehängt haben (mit dem +Operator), stellen Sie sicher, dass die vollständige ("asd {0}"+"fas {1}").format("first", "second");Zeichenfolge in Klammern steht: Andernfalls wird die Funktion nur auf die zuletzt angehängte Zeichenfolge angewendet.
Lukas Knuth

3
Das verändert das Ergebnis leicht und subtil. Stellen Sie sich vor 'foo {0}'.format(fnWithNoReturnValue()). Es würde derzeit zurückkehren foo {0}. Mit Ihren Änderungen würde es zurückkehren foo undefined.
Furchtphage

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

491

Es ist lustig, weil Stack Overflow tatsächlich eine eigene Formatierungsfunktion für den Stringgenannten Prototyp hat formatUnicorn. Versuch es! Gehen Sie in die Konsole und geben Sie Folgendes ein:

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

Firebug

Sie erhalten diese Ausgabe:

Hello, Gabriel, are you feeling OK?

Sie können Objekte, Arrays und Strings als Argumente verwenden! Ich habe den Code erhalten und ihn überarbeitet, um eine neue Version von zu erstellen 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;
};

Beachten Sie den cleveren Array.prototype.slice.call(arguments)Aufruf - das heißt, wenn Sie Argumente einfügen, die Zeichenfolgen oder Zahlen sind, nicht ein einzelnes Objekt im JSON-Stil, erhalten Sie das String.FormatVerhalten von C # fast genau.

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

Das liegt daran, dass Array's slicealles argumentsin ein erzwingt Array, ob es ursprünglich war oder nicht, und das keyist der Index (0, 1, 2 ...) jedes Array-Elements, das zu einer Zeichenfolge gezwungen wird (z. B. "0", also) "\\{0\\}"für Ihr erstes reguläres Ausdrucksmuster).

Ordentlich.


402
Es ist ziemlich cool, eine Frage zu Stackoverflow mit Code von Stackoverflow zu beantworten, +1
Sneakyness

5
@JamesManning Der reguläre Ausdruck erlaubt das globale Flag ( g), das denselben Schlüssel mehrmals ersetzen kann. Im obigen Beispiel können Sie {name}mehrere Sätze im selben Satz verwenden und alle ersetzen lassen.
KrekkieD

3
Das scheint schrecklich zerbrechlich zu sein, um ehrlich zu sein. Was passiert zum Beispiel , wenn nameist "blah {adjective} blah"?
Sam Hocevar

5
@ Ruffin "ein wenig hyperbolisch"? Code, der dazu verleitet wird, Benutzerdaten als Formatzeichenfolgen zu interpretieren, ist eine ganze Kategorie von Schwachstellen . 98,44% sind mehr als mittelmäßig .
Sam Hocevar

3
@samhocevar Ich kann dir nicht glauben Little Bobby Tabled mich. ;) Wenn Sie Text, der von clientseitigem JavaScript verarbeitet wird, auf Ihrem Datenbankserver ohne Sicherheitsüberprüfungen ausführen, hilft der Himmel uns allen. ; ^) Schauen Sie, es sollte nichts geben, was ein Benutzer von einem Client (z. B. Postman) senden kann, das die Sicherheit Ihres Servers überschreitet. Und Sie sollten davon ausgehen, dass alles Gefährliche, das von einem Kunden gesendet werden könnte, sein wird . Das heißt, wenn Sie 100% ige Sicherheit für clientseitigen JavaScript-Code benötigen, der immer vom Benutzer bearbeitet werden kann, und Sie glauben, dass diese Funktion ein Sicherheitsrisiko darstellen könnte, spielen Sie im falschen Spiel.
Ruffin

325

Zahlenformatierung in JavaScript

Ich bin auf diese Fragenseite gekommen, um herauszufinden, wie man Zahlen in JavaScript formatiert , ohne eine weitere Bibliothek einzuführen. Folgendes habe ich gefunden:

Rundung von Gleitkommazahlen

Das Äquivalent von sprintf("%.2f", num)in JavaScript scheint zu sein num.toFixed(2), das nummit Rundung auf 2 Dezimalstellen formatiert wird (siehe jedoch den Kommentar von @ ars265 weiter Math.roundunten).

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

Exponentialform

Das Äquivalent von sprintf("%.2e", num)ist num.toExponential(2).

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

Hexadezimal und andere Basen

Versuchen Sie, Zahlen in Basis B zu drucken num.toString(B). JavaScript unterstützt die automatische Konvertierung von und zu den Basen 2 bis 36 (außerdem unterstützen einige Browser die Base64-Codierung nur eingeschränkt ).

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

Referenzseiten

Kurzanleitung zur Formatierung von JS-Nummern

Mozilla-Referenzseite für toFixed () (mit Links zu toPrecision (), toExponential (), toLocaleString (), ...)


23
Wäre es nicht besser, das Zahlenliteral in Klammern zu setzen, als dort einen seltsamen Leerraum zu lassen?
Rmobis

7
Das würde wahrscheinlich besser aussehen, stimmt. Aber mein Ziel dort ist nur, auf die Syntaxfehlerfalle hinzuweisen.
Rescdsk

4
Nur eine Randnotiz: Wenn Sie einen älteren Browser verwenden oder ältere Browser unterstützen, sind einige Browser, die toFixed falsch implementiert haben, die bessere Lösung, wenn Sie Math.round anstelle von toFixed verwenden.
Ars265

7
@ Raphael_ und @ Rescdsk: ..funktioniert auch:33333..toExponential(2);
Peter Jaric

Oder (33333) .toExponential (2)
Jonathan

245

Ab ES6 können Sie Vorlagenzeichenfolgen verwenden :

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

Beachten Sie, dass Vorlagenzeichenfolgen von Backticks anstelle von (einfachen) Anführungszeichen umgeben sind.

Für weitere Informationen:

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

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

Hinweis: Überprüfen Sie die Mozilla-Site, um eine Liste der unterstützten Browser zu finden.


61
Das Problem mit Vorlagenzeichenfolgen besteht darin, dass sie scheinbar sofort ausgeführt werden, sodass sie beispielsweise als i18n-ähnliche Zeichenfolgentabelle völlig wertlos sind. Ich kann die Zeichenfolge nicht frühzeitig definieren und die Parameter angeben, die später und / oder wiederholt verwendet werden sollen.
Tustin2121

4
@ Tustin2121 Sie haben Recht, dass sie nicht für die Zuweisung zu einer Variablen erstellt wurden, was ein wenig verwirrend ist, aber es ist einfach genug, mit den Tendenzen zur sofortigen Ausführung von Zeichenfolgen zu arbeiten, wenn Sie sie in einer Funktion ausblenden. Siehe jsfiddle.net/zvcm70pa
inanutshellus

13
@ Tustin2121 Es gibt keinen Unterschied zwischen der Verwendung einer Vorlagenzeichenfolge oder der Verkettung von Zeichenfolgen im alten Stil, deren Zucker für dasselbe gilt. Sie müssten einen String-Generator im alten Stil in eine einfache Funktion einbinden, und dasselbe funktioniert gut mit String-Vorlagen. const compile = (x, y) => `I can call this template string whenever I want.. x=${x}, y=${y}`...compile(30, 20)
Kammerherr

4
Diese Lösung funktioniert nicht für
Formatzeichenfolgen,

1
@inanutshellus Das funktioniert gut, wenn Ihre Vorlagenfunktion auf demselben Computer definiert ist, auf dem sie ausgeführt wird. Soweit ich weiß, können Sie eine Funktion nicht als JSON übergeben, sodass das Speichern von Vorlagenfunktionen in einer Datenbank nicht gut funktioniert.
Styfle

171

jsxt, Zippo

Diese Option passt besser.

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

Mit dieser Option kann ich Zeichenfolgen wie diese ersetzen:

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

Mit Ihrem Code würde die zweite {0} nicht ersetzt. ;)


3
gist.github.com/1049426 Ich habe Ihr Beispiel mit diesem Ansatz aktualisiert. Zahlreiche Vorteile, einschließlich Speichern der nativen Implementierung, falls vorhanden, Stringifizieren usw. Ich habe versucht, reguläre Ausdrücke zu entfernen, aber es ist durchaus erforderlich, dass sie global ersetzt werden. : - /
tbranyen

6
jsxt ist leider GPL-lizenziert
AndiDog

109

Ich benutze diese einfache Funktion:

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

Das ist string.format sehr ähnlich:

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

1
warum +=? sollte esformatted = this.replace("{" + arg + "}", arguments[arg]);
guilin 30

2
Ich denke, der Code ist immer noch nicht korrekt. Das richtige sollte wie Filipiz gepostet sein.
Wenqiang

3
Als Referenz for...infunktioniert nicht in jedem Browser, wie dieser Code es erwartet. Es werden alle aufzählbaren Eigenschaften durchlaufen, die in einigen Browsern enthalten sind arguments.length, und in anderen werden die Argumente selbst überhaupt nicht enthalten sein. In jedem Fall, wenn Object.prototypehinzugefügt wird, werden wahrscheinlich alle Ergänzungen in der Gruppe enthalten sein. Der Code sollte eine Standardschleife verwenden forund nicht for...in.
CHao

3
Dies schlägt fehl, wenn eine vorherige Ersetzung auch eine Formatzeichenfolge enthält:"{0} is dead, but {1} is alive!".format("{1}", "ASP.NET") === "ASP.NET is dead, but ASP.NET is alive!"
Gumbo

6
Die Variable argist global. Sie müssen dies stattdessen tun:for (var arg in arguments) {
Pauan

68

Für Node.js- Benutzer gibt es util.formateine printf-ähnliche Funktionalität:

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

1
Dies unterstützt% x ab Node v0.10.26
Max Krohn

Unterstützt auch keine Modifikatoren für Breite und Ausrichtung (z. B. %-20s %5.2f)
FGM

Ich musste den ganzen Weg nach unten scrollen, um diese nützliche Antwort zu sehen.
Donato

53

Ich bin überrascht, dass niemand etwas verwendet reducehat. Dies ist eine native, präzise und leistungsstarke JavaScript-Funktion.

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"

Wie es funktioniert:

Reduzieren wendet eine Funktion auf einen Akkumulator und jedes Element im Array (von links nach rechts) an, um es auf einen einzelnen Wert zu reduzieren.

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
Hier ist eine Version, die diesen Ansatz verwendet, um eine vereinfachte printfFunktion zu erstellen : jsfiddle.net/11szrbx9
Dem Pilafian

1
Und hier ist eine andere mit ES6, in einer Zeile:(...a) => {return a.reduce((p: string, c: any) => p.replace(/%s/, c));
dtasev

Keine Notwendigkeit für String.prototype.formatin ES6: ((a,b,c)=>`${a}, ${b} and ${c}`)(...['me', 'myself', 'I'])(Beachten Sie, dass dies etwas redundant ist, um besser in Ihr Beispiel zu passen)
Tino

Sie müssten Ersatzfunktionen für jeden printfTypspezifizierer implementieren und Logik zum Auffüllen von Präfixen einschließen. Das vernünftige Durchlaufen des Formatstrings scheint hier die kleine Herausforderung zu sein, imho. Eine gute Lösung, wenn Sie nur Zeichenfolgen ersetzen müssen.
Kollapsar

51

Hier ist eine minimale Implementierung von sprintf in JavaScript: Es werden nur "% s" und "% d" ausgeführt, aber ich habe Platz für die Erweiterung gelassen. Es ist für das OP nutzlos, aber andere Leute, die über diesen Thread von Google stolpern, könnten davon profitieren.

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

Beispiel:

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

Im Gegensatz zu ähnlichen Lösungen in früheren Antworten werden bei dieser Lösung alle Ersetzungen auf einmal ausgeführt , sodass Teile zuvor ersetzter Werte nicht ersetzt werden.



24

Neben der zippoxerAntwort verwende ich diese Funktion:

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.

Ich habe auch eine Nicht-Prototyp-Version, die ich häufiger für ihre Java-ähnliche Syntax verwende:

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 Update

All die coolen neuen Sachen in ES 2015 machen das viel einfacher:

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

Ich dachte mir, da dies, wie die älteren, die Buchstaben nicht wirklich analysiert, könnte es genauso gut nur ein einziges Token verwenden %%. Dies hat den Vorteil, dass es offensichtlich ist und es nicht schwierig macht, eine einzige zu verwenden %. Wenn Sie jedoch %%aus irgendeinem Grund benötigen , müssen Sie es durch sich selbst ersetzen:

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

3
Diese Antwort war großartig für ein schnelles Kopieren und Einfügen in eine vorhandene Funktion. Nein, keine Downloads erforderlich usw.
Nick

@ Nick yep, das ist die Idee :)
Braden Best

21

+1 Zippo mit der Ausnahme, dass der Funktionskörper wie folgt sein muss oder auf andere Weise die aktuelle Zeichenfolge bei jeder Iteration anhängt:

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

1
In Firefox hat es nicht funktioniert. Der Debugger zeigt arg als undefiniert an.
Xiao 11

Es ersetzt nicht das zweite Zeichen, zu dem 'The {0} is dead. Don\'t code {0}. Code {1} that is open source!'.format('ASP', 'PHP'); das Ergebnis wird The ASP is dead. Don't code {0}. Code PHP that is open source!. Eine weitere Sache for(arg in arguments)funktioniert im IE nicht. Ich ersetzte durch for (arg = 0; arg <arguments.length; arg++)
Samarjit Samanta

2
Als Referenz for...infunktioniert nicht in jedem Browser, wie dieser Code es erwartet. Es werden alle aufzählbaren Eigenschaften durchlaufen, die in einigen Browsern enthalten sind arguments.length, und in anderen werden die Argumente selbst überhaupt nicht enthalten sein. In jedem Fall, wenn Object.prototypehinzugefügt wird, werden wahrscheinlich alle Ergänzungen in der Gruppe enthalten sein. Der Code sollte eine Standardschleife verwenden forund nicht for...in.
CHao

Sie sollten eine Antwortbearbeitung anstelle einer doppelten Antwort vorschlagen. Dieses Duplikat dieser Antwort
RousseauAlexandre

19

Ich möchte meine Lösung für das "Problem" teilen. Ich habe das Rad nicht neu erfunden, versuche aber, eine Lösung zu finden, die auf dem basiert, was JavaScript bereits tut. Der Vorteil ist, dass Sie alle impliziten Conversions kostenlos erhalten. Das Festlegen der Prototyp-Eigenschaft $ von String ergibt eine sehr schöne und kompakte Syntax (siehe Beispiele unten). Es ist vielleicht nicht der effizienteste Weg, aber in den meisten Fällen muss der Umgang mit der Ausgabe nicht besonders optimiert werden.

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

Hier einige Beispiele:

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'

Leider sind mindestens # und + für Floats nicht implementiert. Hier ist eine Referenz für die Funktion in c: tutorialspoint.com/c_standard_library/c_function_sprintf.htm
Daniel


14

Ich verwende eine kleine Bibliothek namens String.format für JavaScript, die die meisten Formatzeichenfolgenfunktionen (einschließlich des Formats von Zahlen und Datumsangaben) unterstützt und die .NET-Syntax verwendet. Das Skript selbst ist kleiner als 4 kB, sodass nicht viel Overhead entsteht.


Ich habe mir diese Bibliothek angesehen und sie sieht wirklich toll aus. Ich war sauer, als ich sah, dass der Download eine EXE war. Worum geht es zum Teufel? Nicht heruntergeladen.
Jessegavin

Oft ist ein herunterladbares Archiv, das eine EXE-Datei ist, nichts anderes als eine "selbstextrahierende ZIP". Führen Sie es aus, und es wird sich selbst entpacken. Dies ist sehr praktisch, ABER weil es so sehr nach Malware aussieht, dass das Format im Web nicht mehr so ​​oft verwendet wird.
Chuck Kollars

Während dieser Link die Frage beantworten kann, ist es besser, die wesentlichen Teile der Antwort hier aufzunehmen und den Link als Referenz bereitzustellen. Nur-Link-Antworten können ungültig werden, wenn sich die verknüpfte Seite ändert.
Starmole

@starmole Der Link führt zu einer (minimierten) 4-kB-Javascript- Bibliothek . Ich glaube nicht, dass es eine gute Idee ist, es in die Antwort einzufügen.
ivarni

Sie haben Recht, es wäre nicht besser. Ich habe diesen Kommentar gerade zur zufälligen Überprüfung erhalten - und ihn kommentiert, bevor ich ihn ablehne. Für mich ist Stackoverflow besser, wenn ich Erklärungen gebe, als fertige Lösungen (die der Link ist). Ich möchte die Leute auch nicht dazu ermutigen, Black-Box-Code zu posten oder herunterzuladen.
Starmole

14

Sehr elegant:

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 geht an (defekter Link) https://gist.github.com/0i0/1519811


Dies ist die einzige, die Escape-Klammern {{0}}und ähnliches behandelt {0}{1}.format("{1}", "{0}"). Sollte ganz oben sein!
Juan

11

Wenn Sie mit dem Tausendertrennzeichen umgehen möchten, sollten Sie toLocaleString () aus der JavaScript Number- Klasse verwenden, da dadurch die Zeichenfolge für die Region des Benutzers formatiert wird.

Die JavaScript- Datumsklasse kann lokalisierte Datums- und Uhrzeitangaben formatieren.


1
Es ist eigentlich eine Einstellung des Benutzers als Einstellung in der Anwendung (nicht die Maschine, auf der sie sich befindet), aber ich werde einen Blick darauf werfen, danke
Chris S

Fügen Sie einige Beispiele hinzu, damit jeder es schnell verstehen kann.
Bhushan Kawadkar


9

Ich benutze dieses:

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

    return newStr;
}

Dann nenne ich es:

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

9

Ich habe eine Lösung, die der von Peter sehr nahe kommt, aber sie befasst sich mit Zahlen- und Objektfällen.

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

Vielleicht könnte es sogar noch besser sein, sich mit allen tiefen Fällen zu befassen, aber für meine Bedürfnisse ist das in Ordnung.

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

PS: Diese Funktion ist sehr cool, wenn Sie Übersetzungen in Vorlagen-Frameworks wie AngularJS verwenden :

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

Wo das en.json so etwas ist

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

Der [^}] Teil im regulären Ausdruck ist nicht erforderlich. Verwenden Sie stattdessen {(. *?)} oder besser {([\ s \ S] *?)}, um auch die Zeilenumbrüche abzugleichen.
Rawiro

7

Eine etwas andere Version, die ich bevorzuge (diese verwendet {xxx} Token anstelle von {0} nummerierten Argumenten, dies ist viel selbstdokumentierender und passt viel besser zur Lokalisierung):

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

Eine Variation wäre:

  var formatted = l(this);

das ruft zuerst eine l () -Lokalisierungsfunktion auf.



6

Für diejenigen, die Node.JS und seine util.formatFunktion mögen , habe ich es gerade in seine Vanille-JavaScript-Form extrahiert (mit nur Funktionen, die util.format verwendet):

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

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


6

Für die grundlegende Formatierung:

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

5

Ich habe einen etwas längeren Formatierer für JavaScript hier ...

Sie können auf verschiedene Arten formatieren:

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

Wenn Sie ein ObjectBase.prototype.format (z. B. mit DateJS ) angegeben haben, wird dieses verwendet.

Beispiele ...

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

Ich habe auch einen Alias ​​mit .asFormat erstellt und eine Erkennung eingerichtet, falls bereits ein string.format vorhanden ist (z. B. mit MS Ajax Toolkit (ich hasse diese Bibliothek).


5

Nur für den Fall, dass jemand eine Funktion benötigt, um die Verschmutzung des globalen Bereichs zu verhindern, ist hier die Funktion, die dasselbe tut:

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

3

Wir können eine einfache, leichte String.Format- String-Operationsbibliothek für Typescript verwenden.

String.Format ():

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

Zeichenfolgenformat für Bezeichner:

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"

Zeichenfolgenformat für Objekte einschließlich Bezeichner:

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

Ich habe die String.formatVariante nicht gesehen :

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

Zur Verwendung mit jQuery.ajax () Erfolgsfunktionen. Übergeben Sie nur ein einziges Argument und ersetzen Sie die Zeichenfolge durch die Eigenschaften dieses Objekts als {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;
};

Beispiel:

var userInfo = ("Email: {Email} - Phone: {Phone}").format({ Email: "someone@somewhere.com", Phone: "123-123-1234" });
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.