Équivalent de String.format dans jQuery


Réponses:


193

Le code source d'ASP.NET AJAX est disponible pour votre référence, vous pouvez donc le parcourir et inclure les pièces que vous souhaitez continuer à utiliser dans un fichier JS distinct. Ou, vous pouvez les porter sur jQuery.

Voici la fonction de formatage ...

String.format = function() {
  var s = arguments[0];
  for (var i = 0; i < arguments.length - 1; i++) {       
    var reg = new RegExp("\\{" + i + "\\}", "gm");             
    s = s.replace(reg, arguments[i + 1]);
  }

  return s;
}

Et voici les finsAvec et commenceAvec les fonctions prototypes ...

String.prototype.endsWith = function (suffix) {
  return (this.substr(this.length - suffix.length) === suffix);
}

String.prototype.startsWith = function(prefix) {
  return (this.substr(0, prefix.length) === prefix);
}

2
Il ne semble pas y avoir grand-chose. La version JavaScript n'a pas tous les trucs de mise en forme de nombres fantaisie, évidemment. blog.stevex.net/index.php/string-formatting-in-csharp
Nosredna

Wow, j'ai déjà pensé à cela, mais je pensais aussi que ce n'était pas possible à cause de la licence, je ne savais pas qu'ils l'avaient publiée sous licence permissive Microsoft, merci beaucoup pour cela
Waleed Eissa

23
Licence ou pas de licence .. il n'y a qu'une seule bonne façon d'écrire quelque chose d'aussi simple
adamJLev

1
La construction (puis la suppression) d'un objet RegEx pour chaque argument à chaque appel de format peut surcharger le garbage collector.
mckoss

14
Avertissement: Cela formera récursivement: donc si vous en avez {0}{1}, {0}sera remplacé en premier, puis toutes les occurrences de {1}dans le texte déjà substitué et le format d'origine seront remplacées.
Zenexer

147

Il s'agit d'une variante plus rapide / plus simple (et prototypique) de la fonction que Josh a publiée:

String.prototype.format = String.prototype.f = function() {
    var s = this,
        i = arguments.length;

    while (i--) {
        s = s.replace(new RegExp('\\{' + i + '\\}', 'gm'), arguments[i]);
    }
    return s;
};

Usage:

'Added {0} by {1} to your collection'.f(title, artist)
'Your balance is {0} USD'.f(77.7) 

Je l'utilise tellement que je l'ai aliasé juste f, mais vous pouvez également utiliser le plus verbeux format. par exemple'Hello {0}!'.format(name)


1
Avec les navigateurs modernes, il existe des approches encore plus simples: stackoverflow.com/a/41052964/120296
david

3
@david Template string n'est pas du tout la même chose. Ils sont du sucre syntaxique pour la concaténation de chaînes, ce qui est bien, mais pas comme le formatage. Ils s'exécutent instantanément, par conséquent, les remplacements doivent être dans la portée au point de définition. Vous ne pouvez pas les stocker dans un fichier texte ou une base de données pour cette raison. En fait, la seule façon de les stocker n'importe où est de les mettre dans une fonction. Une fonction de chaîne formatée accepte les remplacements de position qui ne se soucient pas du nom de la variable.
krowe2

131

La plupart des fonctions ci-dessus (à l'exception de Julian Jelfs) contiennent l'erreur suivante:

js> '{0} {0} {1} {2}'.format(3.14, 'a{2}bc', 'foo');
3.14 3.14 afoobc foo

Ou, pour les variantes qui comptent à rebours à partir de la fin de la liste d'arguments:

js> '{0} {0} {1} {2}'.format(3.14, 'a{0}bc', 'foo');
3.14 3.14 a3.14bc foo

Voici une fonction correcte. C'est une variante prototypique du code de Julian Jelfs, que j'ai rendu un peu plus serré:

String.prototype.format = function () {
  var args = arguments;
  return this.replace(/\{(\d+)\}/g, function (m, n) { return args[n]; });
};

Et voici une version légèrement plus avancée de la même chose, qui vous permet d'échapper aux accolades en les doublant:

String.prototype.format = function () {
  var args = arguments;
  return this.replace(/\{\{|\}\}|\{(\d+)\}/g, function (m, n) {
    if (m == "{{") { return "{"; }
    if (m == "}}") { return "}"; }
    return args[n];
  });
};

Cela fonctionne correctement:

js> '{0} {{0}} {{{0}}} {1} {2}'.format(3.14, 'a{2}bc', 'foo');
3.14 {0} {3.14} a{2}bc foo

Voici une autre bonne mise en œuvre par Blair Mitchelmore, avec un tas de fonctionnalités supplémentaires intéressantes: https://web.archive.org/web/20120315214858/http://blairmitchelmore.com/javascript/string.format


Et un autre, que je n'ai pas examiné de trop près, mais qui semble implémenter des formats tels que {0: + $ #, 0.00; - $ #, 0.00; 0}: masterdata.dyndns.org/r/string_format_for_javascript
gpvos

Ooh, et celui qui utilise le format d'interpolation Python: code.google.com/p/jquery-utils/wiki/…
gpvos

L'implémentation du format dont j'ai mentionné deux commentaires a été déplacée vers masterdata.se/r/string_format_for_javascript
gpvos

Remarque: la réponse d'ianj ailleurs sur cette page vous permet d'utiliser des paramètres nommés au lieu de paramètres numériques. Si vous changez sa méthode en une méthode qui utilise des prototypes, vous devrez changer le deuxième paramètre de la fonction slice.call de 1 à 0.
gpvos

48

Création d'une fonction de format qui prend une collection ou un tableau comme arguments

Usage:

format("i can speak {language} since i was {age}",{language:'javascript',age:10});

format("i can speak {0} since i was {1}",'javascript',10});

Code:

var format = function (str, col) {
    col = typeof col === 'object' ? col : Array.prototype.slice.call(arguments, 1);

    return str.replace(/\{\{|\}\}|\{(\w+)\}/g, function (m, n) {
        if (m == "{{") { return "{"; }
        if (m == "}}") { return "}"; }
        return col[n];
    });
};

5
Sympa, tout ce qui manque est: String.prototype.format = function (col) {return format (this, col);}
Erik

10
je préfère ne pas étendre la chaîne
ianj

3
il y a une petite faute de frappe dans l'utilisation: la 2ème ligne devrait être: format ("je peux parler {0} depuis que j'étais {1}", 'javascript', 10);
Guillaume Gendre

Pourquoi ne pas préférer étendre la chaîne en utilisant String.prototype?
Kiquenet

36

Il existe une option (quelque peu) officielle: jQuery.validator.format .

Livré avec jQuery Validation Plugin 1.6 (au moins).
Assez semblable auString.Format trouvé dans .NET.

Modifier le lien cassé fixe.



13

Bien que ce ne soit pas exactement ce que le Q demandait, j'en ai construit un qui est similaire mais utilise des espaces réservés nommés au lieu de numérotés. Personnellement, je préfère avoir des arguments nommés et simplement envoyer un objet comme argument (plus verbeux, mais plus facile à maintenir).

String.prototype.format = function (args) {
    var newStr = this;
    for (var key in args) {
        newStr = newStr.replace('{' + key + '}', args[key]);
    }
    return newStr;
}

Voici un exemple d'utilisation ...

alert("Hello {name}".format({ name: 'World' }));

8

À l'aide d'un navigateur moderne, qui prend en charge EcmaScript 2015 (ES6), vous pouvez profiter des chaînes de modèles . Au lieu de mettre en forme, vous pouvez directement y injecter la valeur de la variable:

var name = "Waleed";
var message = `Hello ${name}!`;

Notez que la chaîne de modèle doit être écrite à l'aide de tiques inversées (`).


6

Aucune des réponses présentées jusqu'à présent n'a d'optimisation évidente de l'utilisation de l'enceinte pour initialiser une fois et stocker les expressions régulières, pour les utilisations suivantes.

// DBJ.ORG string.format function
// usage:   "{0} means 'zero'".format("nula") 
// returns: "nula means 'zero'"
// place holders must be in a range 0-99.
// if no argument given for the placeholder, 
// no replacement will be done, so
// "oops {99}".format("!")
// returns the input
// same placeholders will be all replaced 
// with the same argument :
// "oops {0}{0}".format("!","?")
// returns "oops !!"
//
if ("function" != typeof "".format) 
// add format() if one does not exist already
  String.prototype.format = (function() {
    var rx1 = /\{(\d|\d\d)\}/g, rx2 = /\d+/ ;
    return function() {
        var args = arguments;
        return this.replace(rx1, function($0) {
            var idx = 1 * $0.match(rx2)[0];
            return args[idx] !== undefined ? args[idx] : (args[idx] === "" ? "" : $0);
        });
    }
}());

alert("{0},{0},{{0}}!".format("{X}"));

De plus, aucun des exemples ne respecte l'implémentation de format () s'il en existe déjà un.


2
rx2 est inutile, voir mon implémentation; vous avez (parenthèses) dans rx1, mais n'utilisez pas la valeur qu'ils transmettent à la fonction interne. De plus, je pense que c'est une optimisation évidente à faire dans le moteur Javascript . Êtes-vous sûr que les navigateurs modernes ne font pas déjà cette optimisation en arrière-plan? Perl l'a fait en 1990. Vous avez raison, il devrait y avoir un wrapper autour de la fonction pour vérifier si elle est déjà implémentée.
gpvos

1
De plus, le test sur (args [idx] === "") me semble superflu: il est déjà couvert par le premier test sur cette ligne, car non défini! == "".
gpvos

4

Voici la mienne:

String.format = function(tokenised){
        var args = arguments;
        return tokenised.replace(/{[0-9]}/g, function(matched){
            matched = matched.replace(/[{}]/g, "");
            return args[parseInt(matched)+1];             
        });
    }

Pas à l'épreuve des balles mais fonctionne si vous l'utilisez judicieusement.


4

Bien au-delà de la fin de la saison, mais je viens de regarder les réponses données et j'ai ma valeur de tuppence:

Usage:

var one = strFormat('"{0}" is not {1}', 'aalert', 'defined');
var two = strFormat('{0} {0} {1} {2}', 3.14, 'a{2}bc', 'foo');

Méthode:

function strFormat() {
    var args = Array.prototype.slice.call(arguments, 1);
    return arguments[0].replace(/\{(\d+)\}/g, function (match, index) {
        return args[index];
    });
}

Résultat:

"aalert" is not defined
3.14 3.14 a{2}bc foo

3

Vous pouvez maintenant utiliser des modèles de littéraux :

var w = "the Word";
var num1 = 2;
var num2 = 3;

var long_multiline_string = `This is very long
multiline templete string. Putting somthing here:
${w}
I can even use expresion interpolation:
Two add three = ${num1 + num2}
or use Tagged template literals
You need to enclose string with the back-tick (\` \`)`;

console.log(long_multiline_string);


3
J'étais enthousiasmé par les littéraux de modèle jusqu'à ce que je voie qu'ils ne fonctionnent que lorsque la chaîne est définie à côté des variables de remplacement. Pour moi, cela les rend pratiquement inutiles; pour une raison ou une autre, la plupart de mes chaînes sont définies séparément du code qui les remplit / les utilise.
Pierre

2

Voici ma version qui est capable d'échapper à «{» et de nettoyer ces espaces réservés non attribués.

function getStringFormatPlaceHolderRegEx(placeHolderIndex) {
    return new RegExp('({)?\\{' + placeHolderIndex + '\\}(?!})', 'gm')
}

function cleanStringFormatResult(txt) {
    if (txt == null) return "";

    return txt.replace(getStringFormatPlaceHolderRegEx("\\d+"), "");
}

String.prototype.format = function () {
    var txt = this.toString();
    for (var i = 0; i < arguments.length; i++) {
        var exp = getStringFormatPlaceHolderRegEx(i);
        txt = txt.replace(exp, (arguments[i] == null ? "" : arguments[i]));
    }
    return cleanStringFormatResult(txt);
}
String.format = function () {
    var s = arguments[0];
    if (s == null) return "";

    for (var i = 0; i < arguments.length - 1; i++) {
        var reg = getStringFormatPlaceHolderRegEx(i);
        s = s.replace(reg, (arguments[i + 1] == null ? "" : arguments[i + 1]));
    }
    return cleanStringFormatResult(s);
}

2

La réponse suivante est probablement la plus efficace mais a la mise en garde de ne convenir qu'à 1 à 1 mappages d'arguments. Cela utilise le moyen le plus rapide de concaténer des chaînes (semblable à un constructeur de chaînes: tableau de chaînes jointes). Ceci est mon propre code. Mais il a probablement besoin d'un meilleur séparateur.

String.format = function(str, args)
{
    var t = str.split('~');
    var sb = [t[0]];
    for(var i = 0; i < args.length; i++){
        sb.push(args[i]);
        sb.push(t[i+1]);
    }
    return sb.join("");
}

Utilisez-le comme:

alert(String.format("<a href='~'>~</a>", ["one", "two"]));

2
La réponse acceptée est la meilleure réponse. Je donne une réponse unique qui est utile dans les scénarios où vous voulez comprimer chaque bit d'efficacité possible (longues boucles) et vous avez un mappage 1: 1 des arguments. Plus efficace cuz replace () et le nouveau Regex () ainsi que l'exécution du regex utilisent certainement plus de cycles CPU que le split simple ().
Skychan

Pas besoin de -1. Non, ce n'est pas largement applicable, mais il a raison, ce serait un peu plus efficace. Maintenant, pour l'auteur, une question d'architecture: quel type d'application traiterait un si grand ensemble de données sur le client que ce type d'optimisation serait nécessaire?
Michael Blackburn

Bon point Michael Blackburn. La plupart des situations sont probablement bien. Dans mon cas, je faisais face à une bonne quantité de données (variations de produits au sein d'un groupe de produits, donc peut-être des centaines de variantes) et mon opinion est que, puisque les utilisateurs ont généralement de nombreux onglets de navigateur ouverts et que chaque site Web a tendance à aspirer beaucoup de CPU qui J'ai préféré cette implémentation pour maintenir une efficacité élevée.
Skychan

2

Cela viole le principe DRY, mais c'est une solution concise:

var button = '<a href="{link}" class="btn">{text}</a>';
button = button.replace('{text}','Authorize on GitHub').replace('{link}', authorizeUrl);

0
<html>
<body>
<script type="text/javascript">
   var str="http://xyz.html?ID={0}&TId={1}&STId={2}&RId={3},14,480,3,38";
   document.write(FormatString(str));
   function FormatString(str) {
      var args = str.split(',');
      for (var i = 0; i < args.length; i++) {
         var reg = new RegExp("\\{" + i + "\\}", "");             
         args[0]=args[0].replace(reg, args [i+1]);
      }
      return args[0];
   }
</script>
</body>
</html>

C'est bien pour les URI, mais pour une utilisation générale, avoir une chaîne contenant à la fois le format et les composants est très fragile. Et si votre chaîne contient des virgules?
Michael Blackburn

0

Je n'ai pas pu obtenir la réponse de Josh Stodola au travail, mais ce qui suit a fonctionné pour moi. Notez la spécification de prototype. (Testé sur IE, FF, Chrome et Safari.):

String.prototype.format = function() {
    var s = this;
    if(t.length - 1 != args.length){
        alert("String.format(): Incorrect number of arguments");
    }
    for (var i = 0; i < arguments.length; i++) {       
        var reg = new RegExp("\\{" + i + "\\}", "gm");
        s = s.replace(reg, arguments[i]);
    }
    return s;
}

sdevrait vraiment être un clone de thisne pas être une méthode destructrice, mais ce n'est pas vraiment nécessaire.


Ce n'est pas une méthode destructrice. Lorsque s est réaffecté avec la valeur de retour de s.replace (), cela reste inchangé.
gpvos

0

Développant la grande réponse d'adamJLev ci - dessus , voici la version TypeScript:

// Extending String prototype
interface String {
    format(...params: any[]): string;
}

// Variable number of params, mimicking C# params keyword
// params type is set to any so consumer can pass number
// or string, might be a better way to constraint types to
// string and number only using generic?
String.prototype.format = function (...params: any[]) {
    var s = this,
        i = params.length;

    while (i--) {
        s = s.replace(new RegExp('\\{' + i + '\\}', 'gm'), params[i]);
    }

    return s;
};

0

J'ai un plongeur qui l'ajoute au prototype de chaîne: string.format Il n'est pas seulement aussi court que certains des autres exemples, mais beaucoup plus flexible.

L'utilisation est similaire à la version c #:

var str2 = "Meet you on {0}, ask for {1}";
var result2 = str2.format("Friday", "Suzy"); 
//result: Meet you on Friday, ask for Suzy
//NB: also accepts an array

En outre, prise en charge supplémentaire de l'utilisation des noms et des propriétés des objets

var str1 = "Meet you on {day}, ask for {Person}";
var result1 = str1.format({day: "Thursday", person: "Frank"}); 
//result: Meet you on Thursday, ask for Frank

0

Vous pouvez également fermer le tableau avec des remplacements comme celui-ci.

var url = '/getElement/_/_/_'.replace(/_/g, (_ => this.ar[this.i++]).bind({ar: ["invoice", "id", 1337],i: 0}))
> '/getElement/invoice/id/1337

ou vous pouvez essayer bind

'/getElement/_/_/_'.replace(/_/g, (function(_) {return this.ar[this.i++];}).bind({ar: ["invoice", "id", 1337],i: 0}))
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.