Tương đương Sprintf trong Java


284

Printf đã được thêm vào Java với bản phát hành 1.5 nhưng dường như tôi không thể tìm thấy cách gửi đầu ra tới một chuỗi thay vì một tệp (đó là những gì sprintf làm trong C). Có ai biết cách để làm điều này không?

Câu trả lời:


473
// Store the formatted string in 'result'
String result = String.format("%4d", i * j);

// Write the result to standard output
System.out.println( result );

Xem định dạngcú pháp của nó


28

Dây là loại bất biến. Bạn không thể sửa đổi chúng, chỉ trả về các trường hợp chuỗi mới.

Do đó, định dạng với một phương thức cá thể có ý nghĩa rất nhỏ, vì nó sẽ phải được gọi như sau:

String formatted = "%s: %s".format(key, value);

Các tác giả Java gốc (và các tác giả .NET) đã quyết định rằng một phương thức tĩnh có ý nghĩa hơn trong tình huống này, vì bạn không sửa đổi mục tiêu, mà thay vào đó gọi một phương thức định dạng và chuyển vào một chuỗi đầu vào.

Dưới đây là một ví dụ về lý do tại sao format()sẽ bị câm như một phương thức ví dụ. Trong .NET (và có lẽ trong Java), Replace()là một phương thức cá thể.

Bạn có thể làm được việc này:

 "I Like Wine".Replace("Wine","Beer");

Tuy nhiên, không có gì xảy ra, bởi vì chuỗi là bất biến. Replace()cố gắng trả về một chuỗi mới, nhưng nó được gán cho không có gì.

Điều này gây ra rất nhiều sai lầm tân binh phổ biến như:

inputText.Replace(" ", "%20");

Một lần nữa, không có gì xảy ra, thay vào đó bạn phải làm:

inputText = inputText.Replace(" ","%20");

Bây giờ, nếu bạn hiểu rằng chuỗi là bất biến, điều đó có ý nghĩa hoàn hảo. Nếu bạn không, thì bạn chỉ bối rối. Vị trí thích hợp Replace()sẽ là nơi format(), như một phương thức tĩnh của String:

 inputText = String.Replace(inputText, " ", "%20");

Bây giờ không có câu hỏi như những gì đang xảy ra.

Câu hỏi thực sự là, tại sao các tác giả của các khung này quyết định rằng một nên là một phương thức cá thể, và các tĩnh khác? Theo tôi, cả hai đều được thể hiện thanh lịch hơn như các phương thức tĩnh.

Bất kể ý kiến ​​của bạn là gì, sự thật là bạn ít mắc lỗi khi sử dụng phiên bản tĩnh và mã dễ hiểu hơn (No Hidden Gotchas).

Tất nhiên, có một số phương thức hoàn hảo như các phương thức cá thể, hãy lấy String.Lạng ()

int length = "123".Length();

Trong tình huống này, rõ ràng chúng tôi không cố gắng sửa đổi "123", chúng tôi chỉ kiểm tra nó và trả lại độ dài của nó. Đây là một ứng cử viên hoàn hảo cho một phương pháp ví dụ.

Các quy tắc đơn giản của tôi cho Phương pháp sơ thẩm về các đối tượng không thay đổi:

  • Nếu bạn cần trả về một thể hiện mới cùng loại, hãy sử dụng một phương thức tĩnh.
  • Mặt khác, sử dụng một phương thức ví dụ.

4
Tôi thấy rằng bằng cách nào đó bạn có ý tưởng tôi đã đề xuất chuỗi định dạng sẽ được sửa đổi. Tôi chưa bao giờ xem xét khả năng ai đó có thể mong đợi một Chuỗi thay đổi, vì tính bất biến của chúng là rất cơ bản.
erickson

4
Xem như chuỗi định dạng thường giống như "Giá là% 4d" chứ không phải "% 4d", tôi vẫn thấy nhiều khả năng gây nhầm lẫn. Bạn có gì chống lại các phương thức tĩnh? :)
FlySwat

44
Câu trả lời này dường như không liên quan gì đến câu hỏi.
Steve McLeod

2
Câu trả lời thậm chí không phải là Java, dường như có liên quan nhiều hơn đến .NET
Photodeus

3
-1. Downvote b / c đó là tiếp tuyến. Và không nhất thiết là phong cách tốt nhất cho bất biến. Phong cách này dài dòng hơn các cuộc gọi phương thức đơn giản, đặc biệt đối với các hoạt động được xâu chuỗi. Và nó từ bỏ tính đa hình thời gian chạy vì nó gọi các phương thức tĩnh, điều này rất có ý nghĩa. YMMV.
Andrew Janke

3

Cả hai giải pháp đều hoạt động để mô phỏng printf, nhưng theo một cách khác. Chẳng hạn, để chuyển đổi một giá trị thành chuỗi hex, bạn có 2 giải pháp sau:

  • với format(), gần nhất với sprintf():

    final static String HexChars = "0123456789abcdef";
    
    public static String getHexQuad(long v) {
        String ret;
        if(v > 0xffff) ret = getHexQuad(v >> 16); else ret = "";
        ret += String.format("%c%c%c%c",
            HexChars.charAt((int) ((v >> 12) & 0x0f)),
            HexChars.charAt((int) ((v >>  8) & 0x0f)),
            HexChars.charAt((int) ((v >>  4) & 0x0f)),
            HexChars.charAt((int) ( v        & 0x0f)));
        return ret;
    }
    
  • với replace(char oldchar , char newchar), hơi nhanh hơn nhưng khá hạn chế:

        ...
        ret += "ABCD".
            replace('A', HexChars.charAt((int) ((v >> 12) & 0x0f))).
            replace('B', HexChars.charAt((int) ((v >>  8) & 0x0f))).
            replace('C', HexChars.charAt((int) ((v >>  4) & 0x0f))).
            replace('D', HexChars.charAt((int) ( v        & 0x0f)));
        ...
    
  • Có một giải pháp thứ ba bao gồm chỉ cần thêm char vào rettừng cái một (char là các số cộng vào nhau !) Chẳng hạn như trong:

    ...
    ret += HexChars.charAt((int) ((v >> 12) & 0x0f)));
    ret += HexChars.charAt((int) ((v >>  8) & 0x0f)));
    ...
    

... nhưng điều đó thực sự xấu xí.


Tất cả các ý tưởng tuyệt vời, nhưng biến mã của bạn thành mã chỉ viết không thể hiểu cho đồng nghiệp của bạn.
Ben

0

Bạn có thể thực hiện một printf cho bất cứ thứ gì là OutputStream với PrintStream. Bằng cách nào đó như thế này, in thành một chuỗi chuỗi:

PrintStream ps = new PrintStream(baos);
ps.printf("there is a %s from %d %s", "hello", 3, "friends");
System.out.println(baos.toString());
baos.reset(); //need reset to write new string
ps.printf("there is a %s from %d %s", "flip", 5, "haters");
System.out.println(baos.toString());
baos.reset();

Luồng chuỗi có thể được tạo như thế này ByteArrayOutputStream:

ByteArrayOutputStream baos = new ByteArrayOutputStream();
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.