Câu hỏi JavaScript tương đương với printf / String.Format


Tôi đang tìm kiếm một JavaScript tương đương tốt với C / PHP printf() hoặc cho lập trình viên C # / Java, String.Format() (IFormatProvider cho .NET).

Yêu cầu cơ bản của tôi là một nghìn định dạng dấu phân cách cho các số bây giờ, nhưng một cái gì đó xử lý nhiều kết hợp (bao gồm cả ngày) sẽ là tốt.

Tôi nhận ra Microsoft Ajax thư viện cung cấp một phiên bản String.Format(), nhưng chúng tôi không muốn toàn bộ chi phí của khuôn khổ đó.


1610
2018-01-12 20:02


gốc


Ngoài tất cả các câu trả lời tuyệt vời bên dưới, bạn có thể muốn xem câu trả lời này: stackoverflow.com/a/2648463/1712065 IMO là giải pháp hiệu quả nhất cho vấn đề này. - Annie
Tôi đã viết một Rẻ tiền sử dụng cú pháp printf giống C. - Braden Best
var search = [$ scope.dog, "1"]; var url = vsprintf ("trái đất / Dịch vụ / dogSearch.svc / FindMe /% s /% s ";, Tìm kiếm); *** Đối với nút, bạn có thể tải mô-đun của mình bằng "npm install sprintf-js" - Jenna Leaf


Các câu trả lời:


Thử sprintf () cho JavaScript.


Cập nhậtOk, nếu bạn thực sự muốn tự mình thực hiện một phương pháp định dạng đơn giản, đừng thực hiện thay thế liên tục nhưng thực hiện chúng đồng thời.

Bởi vì hầu hết các đề xuất khác được đề cập không thành công khi một chuỗi thay thế thay thế trước đó cũng chứa một chuỗi định dạng như sau:

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

Thông thường bạn sẽ mong đợi đầu ra được {1}{0} nhưng đầu ra thực tế là {1}{1}. Vì vậy, hãy thay thế đồng thời thay vì thích đề nghị của người sợ hãi.


687



Nó đã được di chuyển một lần nữa: epeli.github.com/underscore.string - Maksymilian Majer
Nếu chỉ một số chuyển đổi đơn giản thành chuỗi được mong muốn, num.toFixed() phương pháp có thể là đủ! - heltonbiker
@ MaksymilianMajer mà có vẻ là một cái gì đó ồ ạt khác nhau. - Evan Carroll
@EvanCarroll bạn đúng. Vào thời điểm tôi viết bình luận kho lưu trữ của sprintf() for JavaScript không có sẵn. underscore.string có nhiều tính năng hơn ngoài sprintf dựa trên sprintf() for JavaScript thực hiện. Khác hơn là thư viện là một dự án hoàn toàn khác. - Maksymilian Majer
@MaksymilianMajer đúng, chỉ cần nói câu trả lời này đã chết, và liên kết đã bị mục nát. Nó cần phải được hoàn toàn thanh tẩy. - Evan Carroll


Xây dựng dựa trên các giải pháp được đề xuất trước đây:

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

đầu ra

ASP đã chết, nhưng ASP.NET vẫn còn sống! ASP {2}


Nếu bạn không muốn sửa đổi Stringnguyên mẫu của:

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

Giúp bạn quen thuộc hơn nhiều:

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

với cùng một kết quả:

ASP đã chết, nhưng ASP.NET vẫn còn sống! ASP {2}


1279



|| lừa không hoạt động nếu args [number] là 0. Nên làm rõ ràng if () để xem liệu (args [number] === undefined). - fserb
trong tuyên bố khác của viết tắt nếu, tại sao không chỉ làm "khớp" thay vì "'{' + number + '}'". khớp phải bằng chuỗi đó. - mikeycgto
@ckozl Định dạng mã của bạn sẽ làm mất hiệu lực câu trả lời. Nó thay đổi đầu ra của mã. Vui lòng kiểm tra mã của bạn trước khi áp dụng chỉnh sửa. Cảm ơn bạn. - fearphage
@ckozl - Vui lòng không lặp đi lặp lại ghi đè định dạng của câu trả lời này theo ý muốn của người đã rời khỏi nó. Tôi sẽ khóa nó cho ngày hôm sau, và nếu bạn muốn giải thích tại sao bạn làm điều này, hãy làm như vậy Meta. - Brad Larson♦
Các (!String.prototype.format) kiểm tra là một ý tưởng tồi trừ khi giao dịch với các polyfills, vì nó có nghĩa là các trình duyệt mới hơn có thể đi cùng và phá vỡ trang web của bạn với một không tương thích format phương pháp. Tôi đề nghị loại bỏ nó. - Simon Lindholm


Thật buồn cười vì Stack Overflow thực sự có chức năng định dạng riêng cho String nguyên mẫu gọi là formatUnicorn. Thử nó! Đi vào giao diện điều khiển và nhập một cái gì đó như:

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

Firebug

Bạn nhận được kết quả này:

Hello, Gabriel, are you feeling OK?

Bạn có thể sử dụng các đối tượng, mảng và chuỗi làm đối số! Tôi lấy mã của nó và làm lại nó để tạo ra một phiên bản mới của 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;
};

Lưu ý thông minh Array.prototype.slice.call(arguments) gọi - điều đó có nghĩa là nếu bạn ném vào các đối số là chuỗi hoặc số, không phải là một đối tượng kiểu JSON, bạn sẽ nhận được C # String.Format hành vi gần như chính xác.

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

Đó là bởi vì Array'S slice sẽ buộc bất cứ điều gì trong arguments vào một Array, dù là ban đầu hay không, và key sẽ là chỉ mục (0, 1, 2 ...) của mỗi phần tử mảng được ép buộc thành một chuỗi (ví dụ: "0", vì vậy "\\{0\\}" cho mẫu regexp đầu tiên của bạn).

Khéo léo.


366



Thật thú vị khi trả lời câu hỏi về stackoverflow với mã từ stackoverflow, +1 - Sneakyness
@ JamesManning Regex cho phép cờ toàn cầu (g), có thể thay thế cùng một khóa nhiều lần. Trong ví dụ trên, bạn có thể sử dụng {name} nhiều lần trong cùng một câu và có tất cả thay thế. - Greggg
@ DineiA.Rockenbach nhưng mã for (arg in args)  sẽ định nghĩa một biến toàn cục có tên arg. Nếu bạn không sử dụng var arg ở dòng đầu tiên, sau đó bạn nên sử dụng for(var arg in args) để tránh xác định biến toàn cục. - RainChen
Điều này có vẻ rất mong manh, thành thật mà nói. Điều gì sẽ xảy ra ví dụ nếu name Là "blah {adjective} blah"? - sam hocevar
@ruffin "một chút hyperbolic"? Mã bị lừa thành diễn giải dữ liệu người dùng dưới dạng chuỗi định dạng là toàn bộ loại lỗ hổng. 98,44% là vượt xa tầm thường. - sam hocevar


Định dạng số trong JavaScript

Tôi đã đến trang câu hỏi này hy vọng tìm cách định dạng số trong JavaScript, mà không giới thiệu thêm một thư viện nào khác. Dưới đây là những gì tôi đã tìm thấy:

Làm tròn số dấu phẩy động

Tương đương với sprintf("%.2f", num) trong JavaScript có vẻ là num.toFixed(2)định dạng nào num đến 2 chữ số thập phân, với làm tròn (nhưng xem nhận xét của @ ars265 về Math.round phía dưới).

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

Dạng số mũ

Tương đương với sprintf("%.2e", num) Là num.toExponential(2).

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

Hệ thập lục phân và các căn cứ khác

Để in số trong cơ sở B, hãy thử num.toString(B). JavaScript hỗ trợ chuyển đổi tự động đến và từ các căn cứ 2 đến 36 (ngoài ra, một số trình duyệt có hỗ trợ giới hạn cho mã hóa base64).

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

Trang tham khảo

Hướng dẫn nhanh về định dạng số JS

Trang tham chiếu của Mozilla cho toFixed () (có liên kết tới toPrecision (), toExponential (), toLocaleString (), ...)


288



Nó sẽ không được tốt hơn để kèm theo chữ số trong dấu ngoặc đơn, thay vì để lại một khoảng trắng lạ ở đó? - rmobis
Điều đó có lẽ sẽ tốt hơn, đúng. Nhưng mục tiêu của tôi là chỉ để chỉ ra cái bẫy lỗi cú pháp. - rescdsk
Chỉ cần một lưu ý phụ nếu bạn đang sử dụng một trình duyệt cũ hơn, hoặc hỗ trợ các trình duyệt cũ hơn, một số trình duyệt được triển khai đểFixed không chính xác, sử dụng Math.round thay cho toFixed là một giải pháp tốt hơn. - ars265
@Raphael_ và @rescdsk: .. cũng hoạt động: 33333..toExponential(2); - Peter Jaric
Hoặc (33333) .toExponential (2) - Jonathan


Từ ES6 bạn có thể sử dụng chuỗi mẫu:

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

Lưu ý rằng các chuỗi mẫu là bao quanh bởi backticks `thay vì dấu nháy đơn.

Để biết thêm thông tin:

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

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

Chú thích: Kiểm tra trang mozilla để tìm danh sách các trình duyệt được hỗ trợ.


170



Vấn đề với các chuỗi mẫu là chúng dường như được thực hiện ngay lập tức, làm cho việc sử dụng của chúng như là, một bảng chuỗi giống như i18n hoàn toàn vô giá trị. Tôi không thể xác định chuỗi sớm và cung cấp các thông số để sử dụng sau và / hoặc nhiều lần. - Tustin2121
@ Tustin2121 Bạn nói đúng là chúng không được xây dựng để gán cho một biến, đó là một chút lộn xộn, nhưng thật dễ dàng để làm việc với các khuynh hướng thực thi ngay lập tức của chuỗi nếu bạn ẩn chúng trong một hàm. Xem jsfiddle.net/zvcm70pa - inanutshellus
@ Tustin2121 không có sự khác biệt giữa việc sử dụng một chuỗi mẫu hoặc nối chuỗi kiểu cũ, đường của nó cho cùng một điều. Bạn sẽ phải quấn một trình tạo chuỗi kiểu cũ trong một hàm đơn giản và cùng một thứ hoạt động tốt với các mẫu chuỗi. const compile = (x, y) => `I can call this template string whenever I want.. x=${x}, y=${y}` ... compile(30, 20) - cchamberlain
giải pháp này sẽ không hoạt động đối với chuỗi định dạng được chuyển trong biến (từ máy chủ chẳng hạn) - user993954
@inanutshellus Điều đó hoạt động tốt nếu chức năng mẫu của bạn được xác định trên cùng một máy nơi nó được thực hiện. Theo tôi biết, bạn không thể chuyển một hàm dưới dạng JSON, vì vậy việc lưu trữ các hàm mẫu trong cơ sở dữ liệu không hoạt động tốt. - styfle


jsxt, Zippo

Tùy chọn này phù hợp hơn.

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

Với tùy chọn này, tôi có thể thay thế các chuỗi như sau:

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

Với mã của bạn, {0} thứ hai sẽ không được thay thế. ;)


168



Tôi thích điều này, vì nó thay thế toàn bộ các thẻ định dạng. - Jarrod Dixon♦
gist.github.com/1049426 Tôi đã cập nhật ví dụ của bạn với cách tiếp cận này. Nhiều lợi ích bao gồm lưu bản triển khai gốc nếu nó tồn tại, xâu chuỗi, v.v. Tôi đã thử xóa các biểu thức chính quy, nhưng loại cần thiết để thay thế toàn cầu. : - / - tbranyen
jsxt không được cấp giấy phép GPL - AndiDog
bạn không khai báo biến trong vòng lặp for - brielov


Tôi sử dụng chức năng đơn giản này:

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

Điều đó rất giống với string.format:

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

91



tại sao +=?, phải không? formatted = this.replace("{" + arg + "}", arguments[arg]); - guilin 桂林
Tôi nghĩ rằng mã vẫn không chính xác. Đúng một người nên giống như Filipiz đã đăng. - wenqiang
Để tham khảo, for...in sẽ không hoạt động trong mọi trình duyệt như mã này mong đợi. Nó sẽ lặp qua tất cả các thuộc tính đếm được, trong một số trình duyệt sẽ bao gồm arguments.lengthvà ở những người khác thậm chí sẽ không bao gồm các đối số. Trong mọi trường hợp, nếu Object.prototype được thêm vào, mọi bổ sung có thể sẽ được đưa vào nhóm. Mã nên sử dụng một tiêu chuẩn for vòng lặp, thay vì for...in. - cHao
Điều này không thành công nếu một thay thế trước đó có chứa một chuỗi định dạng: "{0} is dead, but {1} is alive!".format("{1}", "ASP.NET") === "ASP.NET is dead, but ASP.NET is alive!" - Gumbo
Biến arg là toàn cầu. Bạn cần phải làm điều này thay vào đó: for (var arg in arguments) { - Pauan


Đây là một tối thiểu thực hiện chạy nước rút trong JavaScript: nó chỉ làm "% s" và "% d", nhưng tôi đã để lại không gian cho nó được mở rộng. Nó là vô dụng đối với OP, nhưng những người khác vấp ngã qua chủ đề này đến từ Google có thể hưởng lợi từ nó.

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

Thí dụ:

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

Ngược lại với các giải pháp tương tự trong các câu trả lời trước, điều này thực hiện tất cả các thay thế chỉ trong một bước, do đó, nó sẽ không thay thế các phần của các giá trị được thay thế trước đó.


48





Dành cho Node.js người dùng ở đó util.format có chức năng giống như printf:

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

47



Điều này không hỗ trợ% x như của Node v0.10.26 - Max Krohn