Odpowiednik String.format w jQuery


Odpowiedzi:


193

The Kod źródłowy dla ASP.NET AJAX jest dostępne dla odniesienia, dzięki czemu można podnieść przez to i zawierać elementy, które chcesz dalej używać w oddzielnym pliku JS. Lub możesz przenieść je do jQuery.

Oto funkcja formatu ...

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

A oto cele i funkcje prototypowe ...

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
Nie wygląda na to, żeby było w tym wiele. Wersja JavaScript nie zawiera oczywiście wszystkich elementów do fantazyjnego formatowania liczb. blog.stevex.net/index.php/string-formatting-in-csharp
Nosredna

Wow, właściwie już o tym myślałem, ale także myślałem, że to nie jest możliwe z powodu licencji, nie wiedziałem, że wydali ją na Microsoft Permissive License, wielkie dzięki za to
Waleed Eissa

23
Licencja lub brak licencji .. jest tylko jeden właściwy sposób na napisanie czegoś tak prostego
adamJLev

1
Konstruowanie (a następnie odrzucanie) obiektu RegEx dla każdego argumentu za każdym razem, gdy wywoływany jest format, może przeciążać śmietnik.
mckoss

14
Ostrzeżenie: To sformatuje rekurencyjnie: więc jeśli tak {0}{1}, {0}najpierw zostanie zamieniony, a następnie wszystkie wystąpienia {1}zarówno już podstawionego tekstu, jak i oryginalnego formatu zostaną zastąpione.
Zenexer

147

Jest to szybsza / prostsza (i prototypowa) odmiana funkcji, którą opublikował Josh:

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

Stosowanie:

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

Używam tego do tego stopnia, że ​​po prostu go aliasuję f, ale możesz też użyć bardziej szczegółowego format. na przykład'Hello {0}!'.format(name)


1
W nowoczesnych przeglądarkach są jeszcze prostsze podejścia: stackoverflow.com/a/41052964/120296
David

3
@david Ciąg szablonów wcale nie jest tym samym. Są cukrem syntaktycznym do łączenia łańcuchów, co jest miłe, ale nie takie samo jak formatowanie. Działają natychmiast, dlatego zamienniki muszą być objęte zakresem w momencie definicji. Z tego powodu nie można przechowywać ich w pliku tekstowym lub bazie danych. W rzeczywistości jedynym sposobem na przechowywanie ich w dowolnym miejscu jest włączenie ich w funkcję. Sformatowana funkcja łańcucha przyjmuje zamiany pozycyjne, które nie dbają o nazwę zmiennej.
krowe2

131

Wiele z powyższych funkcji (oprócz Juliana Jelfa) zawiera następujący błąd:

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

Lub w przypadku wariantów, które liczą się wstecz od końca listy argumentów:

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

Oto poprawna funkcja. Jest to prototypowa odmiana kodu Juliana Jelfsa, którą ulepszyłem:

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

A oto nieco bardziej zaawansowana wersja tego samego, która pozwala ci uciec od nawiasów klamrowych poprzez ich podwojenie:

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

Działa to poprawnie:

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

Oto kolejna dobra implementacja Blair Mitchelmore, z kilkoma fajnymi dodatkowymi funkcjami: https://web.archive.org/web/20120315214858/http://blairmitchelmore.com/javascript/string.format


I kolejny, na który nie przyjrzałem się zbyt dokładnie, ale który wydaje się implementować formaty takie jak {0: + $ #, 0,00; - $ #, 0,00; 0}: masterdata.dyndns.org/r/string_format_for_javascript
gpvos

Ooh, i taki, który używa formatu interpolacji w języku Python: code.google.com/p/jquery-utils/wiki/…
gpvos

Implementacja formatu, o której wspomniałem dwa komentarze, została przeniesiona do masterdata.se/r/string_format_for_javascript
gpvos

Uwaga: odpowiedź ianj w innym miejscu na tej stronie umożliwia użycie nazwanych zamiast parametrów numerycznych. Jeśli zmienisz jego metodę na wykorzystującą prototypy, będziesz musiał zmienić drugi parametr na funkcję slice.call z 1 na 0.
gpvos

48

Utworzono funkcję formatu, która przyjmuje jako argument kolekcję lub tablicę

Stosowanie:

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

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

Kod:

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
Fajny, brakuje tylko: String.prototype.format = function (col) {return format (this, col);}
Erik

10
wolę nie przedłużać łańcucha
ianj

3
w użyciu jest mała literówka: 2. wiersz powinien brzmieć: format („Mogę mówić {0}, ponieważ byłem {1}”, „javascript”, 10);
Guillaume Gendre

Dlaczego nie preferujesz przedłużania łańcucha String.prototype?
Kiquenet

36

Istnieje (nieco) oficjalna opcja: jQuery.validator.format .

Pochodzi z jQuery Validation Plugin 1.6 (przynajmniej).
Całkiem podobny do String.Formatznalezionego w .NET.

Edytuj Naprawiono uszkodzony link.



13

Chociaż nie do końca to, o co prosił Q, zbudowałem taki, który jest podobny, ale używa nazwanych symboli zastępczych zamiast numerowanych. Ja osobiście wolę nazywać argumenty i po prostu wysłać obiekt jako argument (bardziej szczegółowy, ale łatwiejszy do utrzymania).

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

Oto przykładowe użycie ...

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

8

Korzystając z nowoczesnej przeglądarki obsługującej EcmaScript 2015 (ES6), możesz cieszyć się ciągami szablonów . Zamiast formatowania możesz bezpośrednio wstrzyknąć do niego wartość zmiennej:

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

Zauważ, że ciąg szablonu musi być napisany przy pomocy tyknięć (`).


6

Żadna z przedstawionych do tej pory odpowiedzi nie ma oczywistej optymalizacji wykorzystania załączników do jednorazowej inicjalizacji i przechowywania wyrażeń regularnych dla kolejnych zastosowań.

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

Żaden z przykładów nie uwzględnia implementacji formatu (), jeśli już istnieje.


2
rx2 jest niepotrzebny, zobacz moją implementację; masz (nawiasy) w rx1, ale nie używaj wartości, które przekazują do funkcji wewnętrznej. Myślę też, że jest to oczywista optymalizacja w silniku JavaScript . Czy na pewno nowoczesne przeglądarki nie przeprowadzają już tej optymalizacji za sceną? Perl zrobił to w 1990 roku. Masz rację, że wokół funkcji powinno być opakowanie, aby sprawdzić, czy jest już zaimplementowane.
gpvos,

1
Również test (args [idx] === "") wydaje mi się zbędny: jest już objęty pierwszym testem w tym wierszu, ponieważ jest niezdefiniowany! == "".
gpvos

4

To moje:

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

Nie jest kuloodporny, ale działa, jeśli używasz go rozsądnie.


4

Minęło późne zakończenie sezonu, ale właśnie patrzyłem na udzielone odpowiedzi i mam wartość mojej dwupłatności:

Stosowanie:

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

Metoda:

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

Wynik:

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

3

Teraz możesz używać literałów szablonów :

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
Byłem podekscytowany literałami szablonów, dopóki nie zobaczyłem, że działają one tylko wtedy, gdy ciąg jest zdefiniowany obok zmiennych zastępujących. Dla mnie to czyni ich prawie bezużytecznymi; z tego czy innego powodu większość moich ciągów jest zdefiniowana oddzielnie od kodu, który je wypełnia / używa.
kamień

2

Oto moja wersja, która jest w stanie uciec z „{” i wyczyścić nieprzypisane miejsca.

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

Poniższa odpowiedź jest prawdopodobnie najskuteczniejsza, ale ma zastrzeżenie, że nadaje się tylko do mapowania argumentów od 1 do 1. Wykorzystuje to najszybszy sposób konkatenacji łańcuchów (podobny do budowniczego łańcuchów: tablica łańcuchów połączonych). To jest mój własny kod. Prawdopodobnie potrzebuje jednak lepszego separatora.

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

Użyj tego jak:

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

2
Zaakceptowana odpowiedź jest najlepszą odpowiedzią. Daję unikalną odpowiedź, która jest przydatna w scenariuszach, w których chcesz wycisnąć każdą możliwą wydajność (długie pętle) i masz mapowanie argumentów 1: 1. Bardziej wydajne cuz replace () i nowa Regex () wraz z wykonywaniem wyrażenia regularnego zdecydowanie zużywa więcej cykli procesora niż pojedynczy split ().
Skychan

Nie ma potrzeby -1. Nie, nie ma szerokiego zastosowania, ale ma rację, byłoby to nieco bardziej wydajne. Dla autora pytanie architektoniczne: jaki rodzaj aplikacji miałby do czynienia z tak dużym zestawem danych na kliencie, że taka optymalizacja byłaby wymagana?
Michael Blackburn

Dobry punkt Michael Blackburn. Większość sytuacji jest prawdopodobnie w porządku. W moim przypadku miałem do czynienia z dużą ilością danych (warianty produktów w ramach grupy produktów, więc być może setki wariantów) i moim zdaniem jest to, że ponieważ użytkownicy zazwyczaj mają otwartych wiele kart przeglądarki, a każda witryna ma tendencję do zużywania dużej ilości procesora, który Wolałem tę implementację, aby zachować wysoką wydajność.
Skychan

2

Narusza to zasadę SUCHO, ale jest to zwięzłe rozwiązanie:

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>

Jest to w porządku dla identyfikatorów URI, ale do ogólnego użytku, mając jeden ciąg zawierający zarówno format, jak i komponenty, jest bardzo kruchy. Co jeśli Twój ciąg zawiera przecinki?
Michael Blackburn,

0

Nie mogłem uzyskać odpowiedzi Josha Stodoli, ale następujące działało dla mnie. Zwróć uwagę na specyfikację prototype. (Testowane na IE, FF, Chrome i 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;
}

snaprawdę powinno być klonem z thistak aby nie być metoda destrukcyjna, ale nie jest to naprawdę konieczne.


To nie jest destrukcyjna metoda. Kiedy s zostanie ponownie przypisane z wartością zwracaną przez s.replace (), pozostaje to niezmienione.
gpvos

0

Rozwijając świetną odpowiedź adamJLev powyżej , oto wersja 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

Mam plunker, który dodaje go do prototypu łańcucha: string.format Jest nie tylko tak krótki jak niektóre inne przykłady, ale o wiele bardziej elastyczny.

Użycie jest podobne do wersji 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

Dodano także obsługę nazw i właściwości obiektów

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

Możesz także zamknąć tablicę z takimi zamiennikami.

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

lub możesz spróbować bind

'/getElement/_/_/_'.replace(/_/g, (function(_) {return this.ar[this.i++];}).bind({ar: ["invoice", "id", 1337],i: 0}))
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.