Pytanie Odpowiednik JavaScript do printf / String.Format


Szukam dobrego odpowiednika języka C / PHP w języku JavaScript printf() lub dla programistów C # / Java, String.Format() (IFormatProvider dla .NET).

Moim podstawowym wymaganiem jest na razie format separatora tysiąca liczb, ale coś, co obsługuje wiele kombinacji (w tym daty) byłoby dobre.

Zdaję sobie sprawę z Microsoftu Ajax biblioteka zapewnia wersję String.Format(), ale nie chcemy całej tej struktury.


1610
2018-01-12 20:02


pochodzenie


Oprócz wszystkich wspaniałych odpowiedzi poniżej, możesz rzucić okiem na ten jeden: stackoverflow.com/a/2648463/1712065 które IMO jest najbardziej efektywnym rozwiązaniem tego problemu. - Annie
Napisałem tani używającej składni printf w stylu C. - Braden Best
var search = [$ scope.dog, "1"]; var url = vsprintf ("earth / Services / dogSearch.svc / FindMe /% s /% s ";, Szukaj); *** Dla węzła można uzyskać moduł przez "npm install sprintf-js" - Jenna Leaf


Odpowiedzi:


Próbować sprintf () dla JavaScript.


AktualizacjaOk, jeśli naprawdę chcesz samodzielnie wykonać metodę prostego formatowania, nie rób kolejnych wymian, ale rób je jednocześnie.

Ponieważ większość innych wspomnianych propozycji zawodzi, gdy zastępujący ciąg poprzedniego zamiennika zawiera również sekwencję formatowania, taką jak ta:

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

Normalnie można oczekiwać, że wyjście będzie {1}{0} ale rzeczywisty wynik jest {1}{1}. Czyli zamiast tego zastępowanie jednocześnie, jak w propozycja lęku.


687



Został ponownie przeniesiony: epeli.github.com/underscore.string - Maksymilian Majer
Jeśli pożądana jest tylko pewna prosta konwersja liczb na łańcuch, num.toFixed() metoda może wystarczyć! - heltonbiker
@MaksymilianMajer, który wydaje się być czymś zupełnie innym. - Evan Carroll
@EvanCarroll masz rację. W tym czasie napisałem komentarz repozytorium sprintf() for JavaScript nie był dostępny. underscore.string ma więcej funkcji poza sprintf, na którym się opiera sprintf() for JavaScript realizacja. Poza tym biblioteka to zupełnie inny projekt. - Maksymilian Majer
@MaksymilianMajer rację, po prostu mówiąc, że ta odpowiedź jest martwa, a link się zepsuł. To musi być całkowicie oczyszczone. - Evan Carroll


Opierając się na wcześniej sugerowanych rozwiązaniach:

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

wyniki

ASP nie działa, ale ASP.NET żyje! ASP {2}


Jeśli nie chcesz modyfikować Stringprototyp:

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

Daje ci dużo więcej znajomości:

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

z tym samym wynikiem:

ASP nie działa, ale ASP.NET żyje! ASP {2}


1279



the || trik nie działa, jeśli argumentem [liczba] jest 0. Powinno to być wyraźne jeśli (), aby sprawdzić, czy (argumenty [liczba] === niezdefiniowane). - fserb
w cudzysłowie skrótu, jeśli, dlaczego nie po prostu zrobić "mecz" zamiast "" {'+ numer + "}" ". Dopasowanie powinno być równe temu ciągowi. - mikeycgto
@ckozl Proces formatowania kodu unieważnia odpowiedź. Zmienia wyjście kodu. Przetestuj swój kod przed zastosowaniem zmian. Dziękuję Ci. - fearphage
@ckozl - nie należy powtarzać formatowania tej odpowiedzi wbrew woli osoby, która je opuściła. Zamykam to na następny dzień i jeśli chcesz wyjaśnić, dlaczego to robisz, zrób to Meta. - Brad Larson♦
The (!String.prototype.format) Sprawdzanie jest złym pomysłem, chyba że chodzi o polyfills, ponieważ oznacza to, że nowsze przeglądarki mogą pojawiać się i łamać twoją stronę z niekompatybilnymi format metoda. Sugeruję usunięcie go. - Simon Lindholm


To zabawne, ponieważ Stack Overflow ma swoją własną funkcję formatowania dla String zwany prototypem formatUnicorn. Spróbuj! Przejdź do konsoli i wpisz coś takiego:

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

Firebug

Otrzymujesz to wyjście:

Hello, Gabriel, are you feeling OK?

Jako argumentów możesz używać obiektów, tablic i łańcuchów! Dostałem jego kod i przerobiłem go na nową wersję 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;
};

Zwróć uwagę na sprytnego Array.prototype.slice.call(arguments) call - oznacza to, że jeśli wrzucisz argumenty będące ciągami lub liczbami, a nie pojedynczym obiektem w stylu JSON, otrzymasz C # String.Format zachowanie prawie dokładnie.

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

To jest ponieważ Array„s slice zmusi to, co jest w środku arguments w Array, czy to pierwotnie, czy nie, i key będzie indeksem (0, 1, 2 ...) każdego elementu tablicy przekształconego w ciąg znaków (np. "0", więc "\\{0\\}" dla twojego pierwszego wzorca regexp).

Schludny.


366



Fajnie jest odpowiedzieć na pytanie dotyczące stackoverflow za pomocą kodu ze stackoverflow, +1 - Sneakyness
@JamesManning Regex zezwala na flagę globalną (g), które mogą zastąpić ten sam klucz więcej niż jeden raz. W powyższym przykładzie możesz użyć {name} wiele razy w tym samym zdaniu i wszystkie je zastąpić. - Greggg
@ DineiA.Rockenbach, ale kod for (arg in args)  zdefiniuje globalną zmienną o nazwie arg. Jeśli nie używasz var arg w pierwszej linii powinieneś użyć for(var arg in args) aby uniknąć definiowania zmiennej globalnej. - RainChen
To wydaje się okropnie delikatne, szczerze mówiąc. Co dzieje się na przykład, jeśli name jest "blah {adjective} blah"? - sam hocevar
@ruffin "trochę hiperboliczny"? Kod, który jest oszukiwany w interpretowaniu danych użytkownika jako ciągów formatów, jest całością kategoria luk w zabezpieczeniach. 98,44% to ponad przeciętność. - sam hocevar


Formatowanie liczb w JavaScript

Dostałem się na tę stronę z pytaniami, mając nadzieję, że dowiem się, jak to zrobić Numery w formacie w JavaScript, bez wprowadzania kolejnej biblioteki. Oto co znalazłem:

Zaokrąglanie liczb zmiennoprzecinkowych

Odpowiednik sprintf("%.2f", num) w JavaScript wydaje się być num.toFixed(2), które formatuje num do 2 miejsc po przecinku, z zaokrągleniem (ale patrz komentarz na temat @ ars265 Math.round poniżej).

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

Formularz wykładniczy

Odpowiednik sprintf("%.2e", num) jest num.toExponential(2).

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

Szesnastkowe i inne podstawy

Aby wydrukować liczby w bazie B, spróbuj num.toString(B). JavaScript obsługuje automatyczną konwersję do iz baz od 2 do 36 (dodatkowo niektóre przeglądarki mają ograniczone wsparcie dla kodowania base64).

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

Strony referencyjne

Szybki samouczek dotyczący formatowania liczb JS

Strona referencyjna Mozilli dla ToFixed () (z linkami do: Precision (), ToExponential (), toLocaleString (), ...)


288



Czy nie byłoby lepiej zamknąć literalną liczbę w nawiasie, zamiast pozostawić tam dziwną białą przestrzeń? - rmobis
Prawdopodobnie wyglądałoby to lepiej, prawda. Ale moim celem jest wskazanie pułapki błędów składni. - rescdsk
Nawiasem mówiąc, jeśli korzystasz ze starszej przeglądarki lub obsługujesz starsze przeglądarki, niektóre przeglądarki są zaimplementowane nieprawidłowo w Ustawieniach, użycie Math.round zamiast opcji ToFixed jest lepszym rozwiązaniem. - ars265
@Raphael_ i @rescdsk: .. działa również: 33333..toExponential(2); - Peter Jaric
Lub (33333). Do Wykładniczy (2) - Jonathan


Od ES6 możesz użyć ciągi szablonów:

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

Pamiętaj, że ciągi szablonów są otoczony backtickami `zamiast (pojedynczych) cytatów.

W celu uzyskania dalszych informacji:

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

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

Uwaga: Sprawdź witrynę mozilla, aby znaleźć listę obsługiwanych przeglądarek.


170



Problem z ciągami szablonów polega na tym, że wydają się być natychmiast wykonywane, co sprawia, że ​​ich użycie jako, powiedzmy, tabeli ciągów podobnych do i18n jest całkowicie bezwartościowe. Nie mogę wcześnie zdefiniować napisu i podać parametry, które będą używane później i / lub wielokrotnie. - Tustin2121
@ Tustin2121 Masz rację, że nie są one zbudowane tak, aby przypisywać je do zmiennej, co jest nieco kłopotliwe dla umysłu, ale łatwo jest pracować z szablonami ofertowymi szablonów, jeśli ukryjesz je w funkcji. Widzieć jsfiddle.net/zvcm70pa - inanutshellus
@ Tustin2121 nie ma różnicy między używaniem łańcucha szablonu lub łączenia starego stylu, a jego cukru dla tego samego. Będziesz musiał owijać stary generator ciągów znaków prostą funkcją i to samo działa dobrze z szablonami ciągów. const compile = (x, y) => `I can call this template string whenever I want.. x=${x}, y=${y}` ... compile(30, 20) - cchamberlain
to rozwiązanie nie będzie działać dla łańcucha formatu przekazanego w zmiennej (na przykład z serwera) - user993954
@inanutshellus Działa to dobrze, jeśli twoja funkcja szablonu jest zdefiniowana na tym samym komputerze, na którym jest wykonywana. O ile wiem, nie można przekazać funkcji jako JSON, więc przechowywanie funkcji szablonów w bazie danych nie działa dobrze. - styfle


jsxt, Zippo

Ta opcja pasuje lepiej.

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

Dzięki tej opcji mogę zastąpić ciągi takie jak te:

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

Z twoim kodem drugie {0} nie zostanie zastąpione. ;)


168



Podoba mi się to, ponieważ globalnie zastępuje tokeny formatu. - Jarrod Dixon♦
gist.github.com/1049426Zaktualizowałem twój przykład tym podejściem. Wiele korzyści, w tym zapisywanie implementacji natywnej, jeśli istnieje, stringifying itp. Próbowałem usunąć wyrażeń regularnych, ale rodzaj welp potrzebne do globalnej wymiany. : - / - tbranyen
jsxt jest niestety na licencji GPL - AndiDog
nie określasz zmiennej wewnątrz pętli for - brielov


Używam tej prostej funkcji:

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

Jest to bardzo podobne do string.format:

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

91



czemu +=?, powinien formatted = this.replace("{" + arg + "}", arguments[arg]); - guilin 桂林
Myślę, że kod nadal nie jest poprawny. Prawdziwy jeden powinien być podobny Filipiz opublikowany. - wenqiang
Na przykład, for...in nie będzie działać w każdej przeglądarce, tak jak oczekuje tego kod. Będzie pętla nad wszystkimi właściwościami przeliczalnymi, które w niektórych przeglądarkach będą zawierać arguments.length, aw innych nawet nie uwzględnią samych argumentów. W każdym razie, jeśli Object.prototype zostanie dodany, wszelkie dodatki będą prawdopodobnie zawarte w pakiecie. Kod powinien używać standardu for pętli, a nie for...in. - cHao
Nie powiedzie się, jeśli poprzedni zamiennik zawiera również ciąg formatu: "{0} is dead, but {1} is alive!".format("{1}", "ASP.NET") === "ASP.NET is dead, but ASP.NET is alive!" - Gumbo
Zmienna arg jest globalny. Zamiast tego musisz to zrobić: for (var arg in arguments) { - Pauan


Tutaj jest minimalny implementacja sprintf w JavaScript: robi tylko "% s" i "% d", ale zostawiłem miejsce na rozszerzenie. Jest bezużyteczny dla PO, ale mogą na nim skorzystać inne osoby, które natkną się na ten wątek pochodzący od Google.

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

Przykład:

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

W przeciwieństwie do podobnych rozwiązań w poprzednich odpowiedziach, to robi wszystkie substytucje za jednym razem, więc nie zastąpi części wcześniej zastąpionych wartości.


48





Dla Node.js użytkownicy tam są util.format który ma funkcję podobną do printf:

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

47



To nie obsługuje% x od węzła v0.10.26 - Max Krohn


Jestem zaskoczony, że nikt go nie użył reducejest to natywna zwięzła i wydajna funkcja JavaScript.

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"

Jak to działa:

zmniejszyć stosuje funkcję względem akumulatora i każdego elementu w tablicy (od lewej do prawej), aby zmniejszyć ją do pojedynczej wartości.

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


36



Oto wersja, która używa tego podejścia do tworzenia uproszczonego printf funkcjonować: jsfiddle.net/11szrbx9 - Dem Pilafian
A tutaj jest jeszcze jeden używający ES6, w jednej linii: (...a) => {return a.reduce((p: string, c: any) => p.replace(/%s/, c)); - Bhyd
Nie ma potrzeby String.prototype.format w ES6: ((a,b,c)=>`${a}, ${b} and ${c}`)(...['me', 'myself', 'I']) (zwróć uwagę, że jest to nieco zbędne, aby lepiej pasowało do twojego przykładu) - Tino
Będziesz musiał zaimplementować funkcje zastępcze dla każdego z nich printfSpecyfikatory typów i zawierają logikę dla prefiksów dopełnienia. Iterowanie ciągu ciągu formatów w rozsądny sposób wydaje się tutaj niewielkim wyzwaniem, imho. Czyste rozwiązanie, jeśli potrzebujesz tylko wymiany smyczków. - collapsar