JavaScript équivalent à printf / String.Format

1972

Je recherche un bon équivalent JavaScript du C / PHP printf()ou des programmeurs C # / Java String.Format()( IFormatProviderpour .NET).

Ma condition de base est un format de mille séparateurs pour les nombres pour l'instant, mais quelque chose qui gère beaucoup de combinaisons (y compris les dates) serait bien.

Je me rends compte que la bibliothèque Ajax de Microsoft fournit une version de String.Format(), mais nous ne voulons pas l'intégralité des frais généraux de ce cadre.

Chris S
la source
2
En plus de toutes les bonnes réponses ci-dessous, vous voudrez peut-être jeter un œil à celle-ci: stackoverflow.com/a/2648463/1712065 qui IMO, est la solution la plus efficace à ce problème.
Annie
1
J'en ai écrit un bon marché qui utilise la syntaxe printf de type C.
Braden Best
var search = [$ scope.dog, "1"]; var url = vsprintf (" earth / Services / dogSearch.svc / FindMe /% s /% s ", recherche); *** Pour le nœud, vous pouvez obtenir votre module par "npm install sprintf-js"
Jenna Leaf
J'ai également écrit une fonction simple pour y parvenir; stackoverflow.com/a/54345052/5927126
AnandShanbhag

Réponses:

1111

À partir d'ES6, vous pouvez utiliser des chaînes de modèle:

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

Voir la réponse de Kim ci-dessous pour plus de détails.


Autrement:

Essayez sprintf () pour JavaScript .


Si vous voulez vraiment faire vous-même une méthode de formatage simple, ne faites pas les remplacements successivement mais faites-les simultanément.

Parce que la plupart des autres propositions mentionnées échouent lorsqu'une chaîne de remplacement du remplacement précédent contient également une séquence de format comme celle-ci:

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

Normalement, vous vous attendez à ce que la sortie soit, {1}{0}mais la sortie réelle l'est {1}{1}. Faites donc un remplacement simultanément à la place comme dans la suggestion de fearphage .

Gumbo
la source
16
Si seulement une simple conversion de nombre en chaîne est souhaitée, la num.toFixed()méthode pourrait être suffisante!
heltonbiker
@MaksymilianMajer qui semble être quelque chose de très différent.
Evan Carroll le
@EvanCarroll vous avez raison. Au moment où j'ai écrit le commentaire, le référentiel sprintf() for JavaScriptn'était pas disponible. underscore.stringa plus de fonctionnalités en dehors de sprintf qui est basé sur la sprintf() for JavaScriptmise en œuvre. A part cela, la bibliothèque est un projet entièrement différent.
Maksymilian Majer
@MaksymilianMajer à droite, juste en disant que cette réponse est morte et que le lien s'est détérioré. Il doit être totalement purgé.
Evan Carroll
2
Cette réponse ne devrait plus être acceptée. Depuis ES6, cela est intégré dans le langage javascript (à la fois dans les navigateurs et dans NodeJS). Voir la réponse de @Kim ci-dessous.
Ryan Shillington
1391

S'appuyant sur les solutions précédemment suggérées:

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

les sorties

ASP est mort, mais ASP.NET est vivant! ASP {2}


Si vous préférez ne pas modifier Stringle prototype de:

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

Vous donne le plus familier:

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

avec le même résultat:

ASP est mort, mais ASP.NET est vivant! ASP {2}

peur
la source
12
le || astuce ne fonctionne pas si args [nombre] est 0. Doit faire un if explicite () pour voir si (args [nombre] === non défini).
fserb
4
dans l'instruction else de la sténographie if, pourquoi ne pas simplement faire "match" au lieu de "'{' + nombre + '}'". doit correspondre à cette chaîne.
mikeycgto
4
Si vous avez plusieurs chaînes ajoutées les unes aux autres (avec l' +opérateur-), assurez-vous de mettre la chaîne complète entre parenthèses: ("asd {0}"+"fas {1}").format("first", "second");sinon, la fonction ne sera appliquée qu'à la dernière chaîne ajoutée.
Lukas Knuth du
3
Cela change légèrement et subtilement le résultat. Imaginez 'foo {0}'.format(fnWithNoReturnValue()). Il reviendrait actuellement foo {0}. Avec vos changements, cela reviendrait foo undefined.
fearphage
2
@avenmore: / \ {(\ d +) \} / g
Hozuki
491

C'est drôle parce que Stack Overflow a en fait sa propre fonction de formatage pour le Stringprototype appelé formatUnicorn. Essayez! Allez dans la console et tapez quelque chose comme:

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

Pyromane

Vous obtenez cette sortie:

Hello, Gabriel, are you feeling OK?

Vous pouvez utiliser des objets, des tableaux et des chaînes comme arguments! J'ai obtenu son code et l'ai retravaillé pour produire une nouvelle version de 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;
};

Notez l' Array.prototype.slice.call(arguments)appel intelligent - cela signifie que si vous jetez des arguments qui sont des chaînes ou des nombres, pas un seul objet de style JSON, vous obtenez le String.Formatcomportement de C # presque exactement.

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

En effet Array, les caractères sliceforceront tout ce qui se trouve argumentsdans un Array, que ce soit à l'origine ou non, et le keysera l'index (0, 1, 2 ...) de chaque élément du tableau contraint dans une chaîne (par exemple, "0", donc "\\{0\\}"pour votre premier modèle d'expression rationnelle).

Soigné.

Gabriel Nahmias
la source
402
C'est assez cool de répondre à une question sur stackoverflow avec le code de stackoverflow, +1
Sneakyness
5
@JamesManning La regex autorise l'indicateur global ( g), qui peut remplacer la même clé plus d'une fois. Dans l'exemple ci-dessus, vous pouvez utiliser {name}plusieurs fois dans la même phrase et les faire remplacer tous.
KrekkieD du
3
Pour être honnête, cela semble terriblement fragile. Que se passe-t-il par exemple si nameest "blah {adjective} blah"?
sam hocevar
5
@ruffin «un peu hyperbolique»? Le code trompé dans l'interprétation des données utilisateur sous forme de chaînes de format est une catégorie entière de vulnérabilités . 98,44% est au - delà de médiocre .
sam hocevar
3
@samhocevar Je n'arrive pas à vous croire Little Bobby m'a déposé. ;) Si vous exécutez du texte traité par JavaScript côté client sur votre serveur de base de données sans aucun contrôle de sécurité, le ciel nous aide tous. ; ^) Regardez, il ne devrait pas être quelque chose à tout utilisateur peut envoyer d'un client (par exemple, Postman) qui obtient passé la sécurité de votre serveur. Et vous devriez prendre quelque chose de dangereux qui pourrait être envoyé d'un client sera être. Autrement dit, si vous avez besoin d'une sécurité à 100% du code JavaScript côté client qui est toujours modifiable par l'utilisateur, et que vous pensez que cette fonction pourrait ouvrir un risque pour la sécurité, vous jouez dans le mauvais jeu.
ruffin
325

Formatage des nombres en JavaScript

Je suis arrivé à cette page de questions en espérant trouver comment formater les nombres en JavaScript, sans introduire encore une autre bibliothèque. Voici ce que j'ai trouvé:

Arrondir les nombres à virgule flottante

L'équivalent de sprintf("%.2f", num)JavaScript semble être num.toFixed(2), qui formate numà 2 décimales, avec arrondi (mais voir le commentaire de @ ars265 à propos Math.roundci-dessous).

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

Forme exponentielle

L'équivalent de sprintf("%.2e", num)is num.toExponential(2).

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

Bases hexadécimales et autres

Pour imprimer des nombres en base B, essayez num.toString(B). JavaScript prend en charge la conversion automatique vers et depuis les bases 2 à 36 (en outre, certains navigateurs ont une prise en charge limitée du codage base64 ).

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

Pages de référence

Tutoriel rapide sur le formatage des numéros JS

Page de référence Mozilla pour toFixed () (avec des liens vers toPrecision (), toExponential (), toLocaleString (), ...)

rescdsk
la source
23
Ne serait-il pas préférable de mettre le nombre littéral entre parenthèses, au lieu de laisser là un espace blanc étrange?
rmobis
7
Cela aurait probablement l'air mieux, c'est vrai. Mais mon objectif est juste de souligner le piège des erreurs de syntaxe.
rescdsk
4
Juste une note latérale si vous utilisez un navigateur plus ancien ou si vous prenez en charge des navigateurs plus anciens, certains navigateurs implémentés de manière incorrecte dans ToFixed, en utilisant Math.round à la place de toFixed est une meilleure solution.
ars265
7
@Raphael_ et @rescdsk: ..fonctionne également:33333..toExponential(2);
Peter Jaric
Ou (33333) .toExponential (2)
Jonathan
245

À partir d'ES6, vous pouvez utiliser des chaînes de modèle :

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

Sachez que les chaînes de modèle sont entourées de guillemets `au lieu de (simples) guillemets.

Pour plus d'informations:

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

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

Remarque: consultez le site mozilla pour trouver une liste des navigateurs pris en charge.

Kim
la source
61
Le problème avec les chaînes de modèle est qu'elles semblent être exécutées immédiatement, ce qui rend leur utilisation comme, par exemple, une table de chaînes de type i18n sans aucune valeur. Je ne peux pas définir la chaîne dès le début et fournir les paramètres à utiliser ultérieurement et / ou à plusieurs reprises.
Tustin2121
4
@ Tustin2121 Vous avez raison, ils ne sont pas conçus pour être affectés à une variable, ce qui est un peu déformant, mais il est assez facile de travailler avec les tendances d'exécution instantanée des chaînes de modèles si vous les cachez dans une fonction. Voir jsfiddle.net/zvcm70pa
inanutshellus
13
@ Tustin2121 il n'y a pas de différence entre utiliser une chaîne de modèle ou une concaténation de chaîne à l'ancienne, son sucre pour la même chose. Vous devriez encapsuler un générateur de chaînes de style ancien dans une fonction simple et la même chose fonctionne bien avec les modèles de chaînes. const compile = (x, y) => `I can call this template string whenever I want.. x=${x}, y=${y}`...compile(30, 20)
cchamberlain
4
cette solution ne fonctionnera pas pour la chaîne de format passée en variable (du serveur par exemple)
user993954
1
@inanutshellus Cela fonctionne bien si votre fonction de modèle est définie sur la même machine où elle est exécutée. Pour autant que je sache, vous ne pouvez pas passer une fonction en tant que JSON, donc le stockage des fonctions de modèle dans une base de données ne fonctionne pas bien.
styfle
171

jsxt, Zippo

Cette option convient mieux.

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

Avec cette option, je peux remplacer des chaînes comme celles-ci:

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

Avec votre code, le deuxième {0} ne serait pas remplacé. ;)

Filipiz
la source
3
gist.github.com/1049426 J'ai mis à jour votre exemple avec cette approche. De nombreux avantages, y compris l'enregistrement de l'implémentation native si elle existe, la stringence, etc. J'ai essayé de supprimer les expressions régulières, mais en quelque sorte nécessaires pour le remplacement global. : - /
tbranyen
6
jsxt est sous licence GPL malheureusement
AndiDog
109

J'utilise cette fonction simple:

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

C'est très similaire à string.format:

"{0} is dead, but {1} is alive!".format("ASP", "ASP.NET")
Zippoxer
la source
1
pourquoi +=?, devrait-ilformatted = this.replace("{" + arg + "}", arguments[arg]);
guilin
2
Je pense que le code n'est toujours pas correct. Le bon devrait être comme Filipiz publié.
wenqiang
3
Pour référence, for...inne fonctionnera pas dans tous les navigateurs comme ce code s'y attend. Il bouclera sur toutes les propriétés énumérables, qui dans certains navigateurs incluront arguments.length, et dans d'autres n'incluront même pas les arguments eux-mêmes. Dans tous les cas, si Object.prototypeest ajouté à, tous les ajouts seront probablement inclus dans le groupe. Le code devrait utiliser une forboucle standard plutôt que for...in.
cHao
3
Cela échoue si un remplacement précédent contient également une chaîne de format:"{0} is dead, but {1} is alive!".format("{1}", "ASP.NET") === "ASP.NET is dead, but ASP.NET is alive!"
Gumbo
6
La variable argest globale. Vous devez le faire à la place:for (var arg in arguments) {
Pauan
68

Pour les utilisateurs de Node.js , il existe une util.formatfonctionnalité semblable à printf:

util.format("%s world", "Hello")
George Eracleous
la source
1
Cela ne prend pas en charge% x à partir du nœud v0.10.26
Max Krohn
Ne prend pas non plus en charge les modificateurs de largeur et d'alignement (par exemple %-20s %5.2f)
FGM
J'ai dû faire défiler tout le long de la page pour voir cette réponse utile.
Donato
53

Je suis surpris que personne ne l'ait utilisé reduce, il s'agit d'une fonction JavaScript concise et puissante native.

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"

Comment ça fonctionne:

réduire applique une fonction à un accumulateur et à chaque élément du tableau (de gauche à droite) pour le réduire à une seule valeur.

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

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

CPHPython
la source
4
Voici une version qui utilise cette approche pour créer une printffonction simplifiée : jsfiddle.net/11szrbx9
Dem Pilafian
1
Et voici un autre utilisant ES6, sur une seule ligne:(...a) => {return a.reduce((p: string, c: any) => p.replace(/%s/, c));
dtasev
Pas besoin d' String.prototype.formatES6: ((a,b,c)=>`${a}, ${b} and ${c}`)(...['me', 'myself', 'I'])(notez que c'est un peu redondant pour mieux s'adapter à votre exemple)
Tino
Vous devez implémenter des fonctions de remplacement pour chacun des printfspécificateurs de type et inclure une logique pour les préfixes de remplissage. Itérer sur la chaîne de format d'une manière sensée semble être le défi mineur ici, à mon humble avis. Une solution intéressante si vous n'avez besoin que de remplacements de chaînes.
collapsar
51

Voici une implémentation minimale de sprintf en JavaScript: il ne fait que "% s" et "% d", mais j'ai laissé de l'espace pour qu'il soit étendu. Il est inutile pour l'OP, mais d'autres personnes qui tombent sur ce fil venant de Google pourraient en bénéficier.

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

Exemple:

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

Contrairement à des solutions similaires dans les réponses précédentes, celle-ci effectue toutes les substitutions en une seule fois , elle ne remplacera donc pas les parties des valeurs précédemment remplacées.

Luke Madhanga
la source
24

Pour ajouter à zippoxerla réponse, j'utilise cette fonction:

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.

J'ai également une version non prototype que j'utilise plus souvent pour sa syntaxe de type 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

Mise à jour ES 2015

Tous les nouveaux trucs sympas d'ES 2015 rendent cela beaucoup plus facile:

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

Je me suis dit que comme cela, comme les plus anciens, n'analysait pas réellement les lettres, il pourrait tout aussi bien utiliser un seul jeton %%. Cela a l'avantage d'être évident et de ne pas rendre difficile l'utilisation d'un seul %. Cependant, si vous en avez besoin %%pour une raison quelconque, vous devrez le remplacer par lui-même:

format("I love percentage signs! %%", "%%");
// "I love percentage signs! %%"
Braden Best
la source
3
cette réponse était excellente pour un copier-coller rapide dans une fonction existante. Pas besoin de téléchargements, etc.
Nick
@Nick yep, c'est l'idée :)
Braden Best
21

+1 Zippo à l'exception que le corps de la fonction doit être comme ci-dessous ou sinon il ajoute la chaîne actuelle à chaque itération:

String.prototype.format = function() {
    var formatted = this;
    for (var arg in arguments) {
        formatted = formatted.replace("{" + arg + "}", arguments[arg]);
    }
    return formatted;
};
utilisateur437231
la source
1
Cela n'a pas fonctionné sur Firefox. Le débogueur affiche arg comme non défini.
xiao
Il ne remplace pas le deuxième caractère 'The {0} is dead. Don\'t code {0}. Code {1} that is open source!'.format('ASP', 'PHP'); du résultat The ASP is dead. Don't code {0}. Code PHP that is open source!. Une dernière chose for(arg in arguments)ne fonctionne pas dans IE. j'ai remplacé par for (arg = 0; arg <arguments.length; arg++)
samarjit samanta
2
Pour référence, for...inne fonctionnera pas dans tous les navigateurs comme ce code s'y attend. Il bouclera sur toutes les propriétés énumérables, qui dans certains navigateurs incluront arguments.length, et dans d'autres n'incluront même pas les arguments eux-mêmes. Dans tous les cas, si Object.prototypeest ajouté à, tous les ajouts seront probablement inclus dans le groupe. Le code devrait utiliser une forboucle standard plutôt que for...in.
cHao
Vous devez proposer une modification de la réponse au lieu d'une réponse en double. Cette duplication de cette réponse
RousseauAlexandre
19

Je veux partager ma solution pour le «problème». Je n'ai pas réinventé la roue mais essaie de trouver une solution basée sur ce que JavaScript fait déjà. L'avantage est que vous obtenez gratuitement toutes les conversions implicites. La définition de la propriété prototype $ of String donne une syntaxe très agréable et compacte (voir les exemples ci-dessous). Ce n'est peut-être pas le moyen le plus efficace, mais dans la plupart des cas, la sortie n'a pas besoin d'être super optimisée.

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

Voici quelques exemples:

String.format("%s %s", [ "This is a string", 11 ])
console.log("%s %s".$("This is a string", 11))
var arr = [ "12.3", 13.6 ]; console.log("Array: %s".$(arr));
var obj = { test:"test", id:12 }; console.log("Object: %s".$(obj));
console.log("%c", "Test");
console.log("%5d".$(12)); // '   12'
console.log("%05d".$(12)); // '00012'
console.log("%-5d".$(12)); // '12   '
console.log("%5.2d".$(123)); // '  120'
console.log("%5.2f".$(1.1)); // ' 1.10'
console.log("%10.2e".$(1.1)); // '   1.10e+0'
console.log("%5.3p".$(1.12345)); // ' 1.12'
console.log("%5x".$(45054)); // ' affe'
console.log("%20#2x".$("45054")); // '    1010111111111110'
console.log("%6#2d".$("111")); // '     7'
console.log("%6#16d".$("affe")); // ' 45054'
Rtlprmft
la source
malheureusement, au moins # et + ne sont pas implémentés pour les flottants. voici une référence pour la fonction en c: tutorialspoint.com/c_standard_library/c_function_sprintf.htm
Daniel
14

J'utilise une petite bibliothèque appelée String.format pour JavaScript qui prend en charge la plupart des capacités de chaîne de format (y compris le format des nombres et des dates) et utilise la syntaxe .NET. Le script lui-même est inférieur à 4 Ko, il ne crée donc pas beaucoup de surcharge.

Sven N
la source
J'ai jeté un œil à cette bibliothèque et elle a l'air vraiment super. J'ai été énervé quand j'ai vu que le téléchargement était un EXE. Qu'est-ce que c'est que ça? N'a pas téléchargé.
jessegavin
Souvent, une archive téléchargeable qui est un EXE n'est rien de plus qu'un "ZIP auto-extractible". Exécutez-le et il se déballera. C'est assez pratique MAIS car il ressemble tellement à un malware, le format n'est plus utilisé sur le web plus souvent.
Chuck Kollars
Bien que ce lien puisse répondre à la question, il est préférable d'inclure les parties essentielles de la réponse ici et de fournir le lien de référence. Les réponses de lien uniquement peuvent devenir invalides si la page liée change.
starmole
@starmole le lien est vers une bibliothèque javascript (minifiée) de 4 ko . Je ne pense pas que le coller dans la réponse soit une bonne idée.
ivarni
Vous avez raison de coller ce ne serait pas mieux. Je viens de recevoir ce commentaire pour un examen aléatoire - et commenté avant de ne pas l'aimer. Pour moi, le stackoverflow est préférable pour fournir des explications plutôt que des solutions toutes faites (dont le lien est). Je ne veux pas non plus encourager les gens à publier ou à télécharger du code de boîte noire.
starmole
14

Très élégant:

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

Le crédit va à (lien brisé) https://gist.github.com/0i0/1519811

lior hakim
la source
C'est le seul qui gère les parenthèses d'échappement {{0}}ainsi que des choses comme {0}{1}.format("{1}", "{0}"). Devrait être au sommet!
Juan
11

Si vous cherchez à gérer le séparateur des milliers, vous devez vraiment utiliser toLocaleString () de la classe JavaScript Number car il formatera la chaîne pour la région de l'utilisateur.

La classe JavaScript Date peut formater des dates et heures localisées.

17 sur 26
la source
1
Il s'agit en fait d'un ensemble défini par l'utilisateur comme paramètre dans l'application (et non sur la machine allumée), mais je vais y jeter un coup d'œil, merci
Chris S
ajoutez quelques exemples pour que tout le monde puisse le comprendre rapidement.
Bhushan Kawadkar
9

Le projet PHPJS a écrit des implémentations JavaScript pour de nombreuses fonctions de PHP. Étant donné que la sprintf()fonction de PHP est fondamentalement la même que celle de C printf(), leur implémentation JavaScript devrait satisfaire vos besoins.

Spudley
la source
9

J'utilise celui-ci:

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

    return newStr;
}

Alors je l'appelle:

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

J'ai une solution très proche de celle de Peter, mais elle concerne le nombre et le cas d'objet.

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

Il serait peut-être encore mieux de traiter les cas de tous les fonds, mais pour mes besoins, c'est très bien.

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

PS: Cette fonction est très cool si vous utilisez des traductions dans des frameworks de modèles comme AngularJS :

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

Où le en.json est quelque chose comme

{
    "hello-message": "Hello {name}, welcome.",
    "hello-by-name": "Hello {0}, welcome."
}
Thiago Mata
la source
la partie [^}] dans l'expression rationnelle n'est pas nécessaire .. utilisez {(. *?)} à la place, ou mieux {([\ s \ S] *?)} pour faire correspondre la nouvelle ligne aussi.
rawiro
7

Une version très légèrement différente, celle que je préfère (celle-ci utilise des jetons {xxx} plutôt que des arguments numérotés {0}, c'est beaucoup plus auto-documenté et convient beaucoup mieux à la localisation):

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

Une variation serait:

  var formatted = l(this);

qui appelle d'abord une fonction de localisation l ().

Peter
la source
6

Pour ceux qui aiment Node.JS et sa util.formatfonctionnalité, je viens de l'extraire dans sa forme JavaScript vanille (avec uniquement des fonctions utilisées par 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;
};

Récolté sur: https://github.com/joyent/node/blob/master/lib/util.js

À
la source
6

Pour le formatage de base:

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

J'ai un formateur légèrement plus long pour JavaScript ici ...

Vous pouvez effectuer le formatage de plusieurs manières:

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

De plus, si vous avez dit un ObjectBase.prototype.format (comme avec DateJS ), il l'utilisera.

Exemples...

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

J'ai également un alias avec .asFormat et j'ai une détection en place au cas où il y aurait déjà un string.format (comme avec MS Ajax Toolkit (je déteste cette bibliothèque).

Tracker1
la source
5

Juste au cas où quelqu'un aurait besoin d'une fonction pour empêcher une portée mondiale polluante, voici la fonction qui fait de même:

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

Nous pouvons utiliser une bibliothèque d'opérations de chaîne String.Format simple et légère pour Typescript .

String.Format ():

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

Format de chaîne pour les spécificateurs:

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

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

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


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

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

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

Format de chaîne pour les objets, y compris les spécificateurs:

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

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

Je n'ai pas vu 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;
    });
};
jerone
la source
2

À utiliser avec les fonctions de réussite jQuery.ajax (). Passez un seul argument et remplacez la chaîne par les propriétés de cet objet en tant que {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;
};

Exemple:

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