Giải Thích Chi Tiết Cú Pháp Chuỗi Định Dạng
Phương thức format sử dụng một chuỗi định dạng (format string) để xác định cách hiển thị các giá trị đầu ra. Chuỗi định dạng bao gồm văn bản cố định và các đặc tả định dạng (format specifiers).
Các định dạng format dành cho các loại dữ liệu chung, ký tự, và số có cú pháp như sau:
Cú pháp
%[argument_index$][flags][width][.precision]conversion
Dưới đây là giải thích chi tiết về cú pháp này:
↳ %: Ký tự báo hiệu bắt đầu một chuỗi định dạng.
↳ argument_index (Tùy chọn): Một số nguyên thập phân chỉ định vị trí của đối số trong danh sách các đối số.
↳ Flags (Tùy chọn): Một tập hợp các ký tự định dạng ảnh hưởng đến cách dữ liệu được hiển thị trong đầu ra. Các cờ này phụ thuộc vào loại chuyển đổi và có thể bao gồm các ký tự như -, +, 0, ,, (, mỗi ký tự có vai trò định dạng riêng.
↳ width (Tùy chọn): Một số nguyên thập phân dương chỉ định số ký tự tối thiểu cần được ghi ra đầu ra. Nếu giá trị ngắn hơn độ rộng này, sẽ có thêm các ký tự đệm để bù.
↳ precision (Tùy chọn): Một số nguyên thập phân không âm, thường được sử dụng để giới hạn số lượng ký tự hoặc chữ số. Tác dụng cụ thể của nó phụ thuộc vào loại chuyển đổi, chẳng hạn như giới hạn số chữ số thập phân cho các số dạng floating-point.
↳ conversion (Bắt buộc): Một ký tự chỉ định cách đối số cần được định dạng (ví dụ: d cho số nguyên, f cho số thập phân, s cho chuỗi). Bộ ký tự hợp lệ phụ thuộc vào kiểu dữ liệu của đối số.
Bây giờ chúng ta sẽ đi sâu vào chi tiết từng phần của cú pháp của định dạng chuổi:
Argument Index (Chỉ mục đối số) trong đặc tả định dạng
Trong Java, các định dạng format (format specifiers) có thể tham chiếu đến các đối số theo ba cách khác nhau:
(1) Chỉ mục tường minh (Explicit Indexing):
Chỉ mục tường minh được sử dụng khi định dạng format có chứa một chỉ mục đối số. Chỉ mục đối số là một số nguyên thập phân chỉ vị trí của đối số trong danh sách các đối số. Đối số đầu tiên được tham chiếu bởi "1$", đối số thứ hai là "2$", v.v. Một đối số có thể được tham chiếu nhiều lần.
Ví dụ
Formatter.format("%4$s %3$s %2$s %1$s %4$s %3$s %2$s %1$s", "a", "b", "c", "d");
// -> "d c b a d c b a"
(2) Chỉ mục tương đối (Relative Indexing):
Chỉ mục tương đối được sử dụng khi định dạng format có chứa ký tự '<' (\u003c), gây ra việc sử dụng lại đối số của định dạng format trước đó. Nếu không có đối số trước đó, một ngoại lệ MissingFormatArgumentException sẽ được ném ra.
Ví dụ
formatter.format("%s %s %<s %<s", "a", "b", "c", "d");
// -> "a b b b"
// "c" và "d" bị bỏ qua vì chúng không được tham chiếu
(3) Chỉ mục thông thường (Ordinary Indexing):
Chỉ mục thông thường được sử dụng khi định dạng format không chứa chỉ mục đối số hay ký tự '<' nào. Mỗi định dạng format sử dụng chỉ mục thông thường được gán một chỉ mục ngầm định theo thứ tự tuần tự vào danh sách đối số, độc lập với các chỉ mục được sử dụng bởi chỉ mục tường minh hay tương đối.
Ví dụ
formatter.format("%s %s %s %s", "a", "b", "c", "d");
// -> "a b c d"
(4) Kết hợp các hình thức chỉ mục
Bạn có thể có một chuỗi định dạng sử dụng cả ba hình thức chỉ mục, ví dụ:
Ví dụ
formatter.format("%2$s %s %<s %s", "a", "b", "c", "d");
// -> "b a a b"
// "c" và "d" bị bỏ qua vì chúng không được tham chiếu
Giới hạn và ngoại lệ
Số lượng đối số tối đa bị giới hạn bởi kích thước tối đa của mảng Java như được định nghĩa trong "The Java™ Virtual Machine Specification". Nếu chỉ mục đối số không tương ứng với một đối số khả dụng, một ngoại lệ MissingFormatArgumentException sẽ được ném ra.
Nếu có nhiều đối số hơn các định dạng format, các đối số thừa sẽ bị bỏ qua.
Trừ khi được chỉ định khác, việc truyền đối số null cho bất kỳ phương thức hoặc constructor nào trong lớp này sẽ gây ra ngoại lệ NullPointerException.
Flags (Các cờ) định dạng trong đặc tả định dạng
Các cờ là các ký tự tùy chọn được sử dụng để điều chỉnh cách định dạng giá trị. Cú pháp của cờ được đặt giữa ký tự % và thông tin về chiều rộng hoặc độ chính xác.
| Flags (Các cờ) | Mô tả | Hỗ trợ |
|---|---|---|
| - | Căn trái (mặc định là căn phải) | General, Character, Integral, Floating Point |
| + | Luôn hiển thị dấu | Integral, Floating Point |
| (Khoảng trắng) | Khoảng trắng cho số dương | Integral, Floating Point |
| 0 | Điền số 0 thay vì khoảng trắng | Integral, Floating Point |
| , | Sử dụng dấu phân cách nhóm theo locale | Integral, Floating Point |
| ( | Đặt số âm trong ngoặc | Integral, Floating Point |
Dưới đây là các ví dụ về các cờ định dạng trong Java mà bạn đã liệt kê:
1. - (Căn trái): Kết quả sẽ được căn trái trong không gian đầu ra.
Ví dụ
System.out.printf("%-10s|\n", "Hello");
System.out.printf("%-10d|\n", 123);
// In ra:
Hello |
123 |
2. + (Hiển thị dấu cho cả số dương và số âm): Luôn hiển thị dấu + cho số dương và dấu - cho số âm.
Ví dụ
System.out.printf("%+d\n", 123);
System.out.printf("%+d\n", -123);
// In ra:
+123
-123
3. (Khoảng trắng) (Thêm khoảng trắng trước số dương): Thêm khoảng trắng trước số dương thay vì dấu +.
Ví dụ
System.out.printf("% d\n", 123);
System.out.printf("% d\n", -123);
// In ra:
123
-123
4. 0 (Điền số 0 thay vì khoảng trắng để đạt chiều rộng tối thiểu): Điền số 0 vào trước số để đảm bảo chiều rộng tối thiểu được đáp ứng.
Ví dụ
System.out.printf("%05d\n", 123);
System.out.printf("%05d\n", -123);
// In ra:
00123
-0123
5. , (Sử dụng dấu phân cách nhóm theo locale): Sử dụng dấu phân cách nhóm (thường là dấu phẩy ,) để ngăn cách các nhóm hàng nghìn.
Ví dụ
System.out.printf("%,d\n", 1234567);
// In ra: 1,234,567
6. ( (Đặt số âm trong ngoặc): Đặt số âm trong ngoặc thay vì hiển thị dấu -.
Ví dụ
System.out.printf("%(d\n", -123);
// In ra:(123)
Lưu ý:
↳ Các cờ có thể được kết hợp với nhau.
↳ Không phải tất cả các cờ đều hỗ trợ cho tất cả các kiểu dữ liệu.
↳ Sử dụng cờ một cách hợp lý để tạo ra định dạng phù hợp với yêu cầu.
Width (Chiều rộng) trong đặc tả định dạng
Chiều rộng là một số nguyên dương chỉ định số ký tự tối thiểu sẽ được viết ra đầu ra. Nếu giá trị được định dạng ngắn hơn chiều rộng, khoảng trắng sẽ được thêm vào để đạt được chiều rộng tối thiểu.
Cách hoạt động của Width trong định dạng:
↳ Giá trị có độ dài lớn hơn hoặc bằng chiều rộng: Nếu giá trị được định dạng có độ dài lớn hơn hoặc bằng chiều rộng tối thiểu đã chỉ định, giá trị sẽ được in ra nguyên vẹn, không có thêm ký tự đệm nào.
↳ Giá trị có độ dài nhỏ hơn chiều rộng: (Mặc định) Nếu giá trị ngắn hơn chiều rộng tối thiểu, các ký tự đệm (thường là khoảng trắng) sẽ được thêm vào phía trước giá trị. Điều này đảm bảo rằng tổng số ký tự in ra đạt đủ chiều rộng đã chỉ định.
↳ Cờ -: Nếu cờ - được sử dụng, các ký tự đệm sẽ được thêm vào phía sau giá trị, căn chỉnh giá trị về phía bên trái.
Ví dụ
// Giá trị lớn hơn chiều rộng
System.out.printf("%5d\n", 12345); // In số có 5 ký tự, chiều rộng là 5
// Giá trị nhỏ hơn chiều rộng (căn phải mặc định)
System.out.printf("%5d\n", 42); // In số có 2 ký tự, chiều rộng là 5
// Giá trị nhỏ hơn chiều rộng với cờ -
System.out.printf("%-5d\n", 42); // In số có 2 ký tự, chiều rộng là 5, căn trái
Khi chương trình chạy là:
42
42
Giải thích:
↳ %5d: Số 12345 chỉ có 5 ký tự, vì vậy 12345 có độ dài bằng với chiều rộng tối thiểu, nên nó được in ra nguyên vẹn.
↳ %5d: Số 42 chỉ có 2 ký tự, vì vậy 3 ký tự khoảng trắng được thêm vào phía trước để đạt chiều rộng tối thiểu là 5.
↳ %5d: Số 42 chỉ có 2 ký tự, Với cờ -, vì vậy 42 được căn trái, 3 ký tự khoảng trắng được thêm vào phía sau.
Precision (Độ chính xác) trong đặc tả định dạng
Độ chính xác là một số nguyên không âm xác định số lượng ký tự tối đa được viết ra đầu ra. Hành vi cụ thể của độ chính xác phụ thuộc vào kiểu chuyển đổi.
1. Với các kiểu dữ liệu chung:
Precision quy định số lượng ký tự tối đa sẽ được in ra. Nếu chuỗi dữ liệu dài hơn số ký tự này, nó sẽ bị cắt bớt. Nếu chuỗi ngắn hơn hoặc bằng, nó sẽ được in ra nguyên vẹn.
Ví dụ
System.out.printf("%.5s\n", "Hello, World!");
// Kết quả: Hello
Giải thích: Chuỗi "Hello, World!" có độ dài hơn 5 ký tự, nên nó sẽ bị cắt xuống còn "Hello"
2. Với các kiểu chuyển đổi số thập phân ('a', 'A', 'e', 'E', 'f'):
Precision quy định số lượng chữ số sẽ được in ra sau dấu thập phân. Nếu giá trị có ít hơn số chữ số này, các số 0 sẽ được thêm vào cho đủ. Nếu có nhiều hơn, số sẽ bị làm tròn.
Ví dụ
System.out.printf("%.3f\n", 3.14159);
// Kết quả: 3.142
Giải thích: Số 3.14159 sẽ được làm tròn xuống còn 3.142 với 3 chữ số sau dấu thập phân.
3. Với các kiểu chuyển đổi số kiểu khoa học ('g', 'G'):
Precision quy định tổng số chữ số sẽ xuất hiện trong kết quả (bao gồm cả phần nguyên và phần thập phân) sau khi đã làm tròn.
Ví dụ
System.out.printf("%.4g\n", 12345.6789);
// Kết quả: 1.235e+04
Giải thích: Precision là 4, tổng cộng 4 chữ số sẽ xuất hiện trong kết quả, bao gồm cả phần nguyên và phần thập phân. Kết quả sẽ là dạng số mũ khoa học.
4. Với các kiểu ký tự, số nguyên, ngày/giờ, dấu phần trăm, và dòng mới:
Precision không áp dụng. Nếu bạn cố gắng cung cấp precision cho các loại này, một ngoại lệ sẽ được ném ra.
Ví dụ
try {
System.out.printf("%.3d\n", 42); // Số nguyên với precision không hợp lệ
} catch (Exception e) {
System.out.println("Ngoại lệ: " + e.getMessage());
}
// kết quả: Ngoại lệ: 3
Giải thích: Khi sử dụng precision với số nguyên ('d'), một ngoại lệ sẽ được ném ra vì precision không áp dụng cho kiểu dữ liệu này.
Conversions (Các kiểu chuyển đổi) trong đặc tả định dạng
Đặc tả định dạng trong Java bao gồm các ký tự chuyển đổi (conversion) để xác định cách định dạng giá trị. Các chuyển đổi này được chia thành các nhóm sau: Chuyển đổi định dạng chung (General), chuyển đổi ký tự (Character), chuyển đổi số học (Numeric), chuyển đổi số thực (Floating Point), chuyển đổi ngày/giờ (Date/Time), chuyển đổi phần trăm (Percent) và chuyển đổi ký tự xuống dòng (Line Separator). Dưới đây là chi tiết về từng loại chuyển đổi.
1. Chuyển đổi định dạng chung (General):
Trong Java, các chuyển đổi (conversion) kiểu chung (general conversions) có thể được áp dụng cho bất kỳ kiểu đối số nào và gồm các ký tự chuyển đổi như 'b', 'B', 'h', 'H', 's', 'S'. Dưới đây là các chi tiết về các chuyển đổi này:
| Ký tự | Mô tả | Ví dụ |
|---|---|---|
| 'b' (thường) | Sinh ra chuỗi "true" hoặc "false" như Boolean.toString(). Nếu đối số không phải kiểu boolean, kết quả là "true". | %b |
| 'B' (hoa) | Giống 'b', nhưng kết quả là chữ hoa ("TRUE" hoặc "FALSE"). | %B |
| 'h' (thường) | Sinh ra giá trị mã băm (hash code) của đối tượng. Nếu đối số là null, kết quả là "null". | %h |
| 'H' (hoa) | Giống 'h', nhưng kết quả được chuyển thành chữ hoa. | %H |
| 's' (thường) | Sinh ra chuỗi đại diện cho đối tượng, sử dụng phương thức toString(). Nếu đối số là null, kết quả là "null". | %s |
| 'S' (hoa) | Giống 's', nhưng kết quả được chuyển thành chữ hoa. | %S |
2. Chuyển đổi định dạng ký tự (Character):
Áp dụng cho các kiểu dữ liệu cơ bản đại diện cho ký tự Unicode: char, Character, byte, Byte, short, và Short.
Chuyển đổi này cũng có thể áp dụng cho các kiểu int và Integer khi Character.isValidCodePoint(int) trả về true.
Cờ # không có tác dụng.
Độ chính xác không áp dụng.
Ví dụ: %c (ký tự).
3. Chuyển đổi định dạng số học (Numeric):
Áp dụng cho các kiểu số nguyên trong Java: byte, Byte, short, Short, int, Integer, long, Long, và BigInteger (nhưng không áp dụng cho char hoặc Character). Các loại số này sẽ được định dạng theo thuật toán như sau:
(1) Thuật toán định dạng số (Number Localization Algorithm)
Sau khi các chữ số được lấy từ phần nguyên, phần thập phân và số mũ (tùy thuộc vào kiểu dữ liệu), mỗi ký tự chữ số d trong chuỗi sẽ được thay thế bằng một chữ số cụ thể theo locale, được tính toán dựa trên chữ số 0 hiện tại của locale đó, tức là d - '0' + z, trong đó z là chữ số 0 của locale hiện tại.
↳ Thay thế chữ số dựa trên locale: Sau khi các chữ số được lấy từ phần nguyên, phần thập phân và số mũ (tùy thuộc vào kiểu dữ liệu), mỗi ký tự chữ số d trong chuỗi sẽ được thay thế bằng một chữ số cụ thể theo locale, được tính toán dựa trên chữ số 0 hiện tại của locale đó, tức là d - '0' + z, trong đó z là chữ số 0 của locale hiện tại.
↳ Thay thế dấu phân cách thập phân: Nếu có dấu phân cách thập phân, nó sẽ được thay thế bằng dấu phân cách thập phân cụ thể theo locale.
↳ Cờ , (\u002c): Nếu cờ này được cung cấp, thì dấu phân cách nhóm theo locale sẽ được chèn vào bằng cách quét phần nguyên của chuỗi từ chữ số ít quan trọng nhất đến chữ số quan trọng nhất và chèn một dấu phân cách tại các khoảng cách được xác định bởi kích thước nhóm của locale.
↳ Cờ 0: Nếu cờ này được cung cấp, thì các chữ số 0 theo locale sẽ được chèn sau ký tự dấu (nếu có) và trước chữ số không phải 0 đầu tiên, cho đến khi chiều dài của chuỗi bằng với chiều rộng trường được yêu cầu.
↳ Cờ (: Nếu giá trị âm và cờ ( được cung cấp, thì dấu mở ngoặc ( (\u0028) sẽ được chèn vào đầu và dấu đóng ngoặc ) (\u0029) sẽ được chèn vào cuối.
↳ Dấu -: Nếu giá trị âm (hoặc là số thập phân có dấu âm) và cờ ( không được cung cấp, thì dấu - (\u002d) sẽ được chèn vào đầu.
↳ Cờ +: Nếu cờ này được cung cấp và giá trị là số dương hoặc bằng 0 (hoặc là số thập phân dương bằng 0), thì dấu + (\u002b) sẽ được chèn vào đầu.
↳ Giá trị NaN hoặc Infinity: Nếu giá trị là NaN hoặc dương vô cùng (positive infinity), thì chuỗi "NaN" hoặc "Infinity" tương ứng sẽ được xuất ra. Nếu giá trị là âm vô cùng (negative infinity), thì chuỗi "(Infinity)" sẽ được xuất ra nếu cờ ↳ được cung cấp, ngược lại chuỗi "-Infinity" sẽ được xuất ra. Những giá trị này không bị ảnh hưởng bởi locale.
(2) Các chuyển đổi định dạng số nguyên (Integral)
Các chuyển đổi sau đây có thể được áp dụng cho các kiểu dữ liệu byte, Byte, short, Short, int, Integer, long, và Long trong Java:
d (Decimal)
↳ Định dạng đối số dưới dạng số nguyên thập phân. Thuật toán localization (định dạng theo locale) sẽ được áp dụng.
↳ Nếu cờ 0 được cung cấp và giá trị là số âm, thì các số 0 sẽ được chèn sau dấu âm để đạt chiều rộng mong muốn.
↳ Nếu cờ # được cung cấp, sẽ xảy ra FormatFlagsConversionMismatchException.
Ví dụ
System.out.printf("%08d%n", 123); // Kết quả: 00000123
System.out.printf("%08d%n", -123); // Kết quả: -0000123
o (Octal - Cơ số 8)
↳ Định dạng đối số dưới dạng số nguyên trong cơ số tám. Không áp dụng localization.
↳ Nếu x là số âm, kết quả sẽ là giá trị không dấu được tạo ra bằng cách thêm 2^n vào giá trị, trong đó n là số bit của kiểu dữ liệu tương ứng (như được trả về bởi trường tĩnh SIZE trong các lớp Byte, Short, Integer, hoặc Long).
↳ Nếu cờ 0 được cung cấp, kết quả sẽ được điền số 0 ở đầu để đạt chiều rộng yêu cầu, sau ký hiệu dấu nếu có.
↳ Nếu cờ (, +, hoặc , được cung cấp, sẽ xảy ra FormatFlagsConversionMismatchException.
Ví dụ
System.out.printf("%#08o%n", 123); // Kết quả: 00000173
System.out.printf("%#08o%n", -123); // Kết quả: 037777777605 (Với 32-bit integer)
x (Hexadecimal - Cơ số 16)
↳ Định dạng đối số dưới dạng số nguyên trong cơ số mười sáu. Không áp dụng localization.
↳ Nếu x là số âm, kết quả sẽ là giá trị không dấu được tạo ra bằng cách thêm 2^n vào giá trị, trong đó n là số bit của kiểu dữ liệu tương ứng.
↳ Nếu cờ # được cung cấp, kết quả sẽ luôn bắt đầu bằng ký hiệu cơ số "0x".
↳ Nếu cờ 0 được cung cấp, kết quả sẽ được điền số 0 ở đầu để đạt chiều rộng yêu cầu sau ký hiệu cơ số hoặc dấu (nếu có).
↳ Nếu cờ (, +, hoặc , được cung cấp, sẽ xảy ra FormatFlagsConversionMismatchException.
Ví dụ
System.out.printf("%#08x%n", 123); // Kết quả: 0x00007b
System.out.printf("%#08x%n", -123); // Kết quả: 0xfffff85 (Với 32-bit integer)
X (Uppercase Hexadecimal - Cơ số 16 viết hoa)
↳ Biến thể viết hoa của 'x'. Toàn bộ chuỗi đại diện cho số sẽ được chuyển thành chữ hoa, bao gồm cả ký hiệu "x" và các chữ số thập lục phân 'a' - 'f'.
↳ Các cờ áp dụng giống như với x, nhưng kết quả sẽ là chữ hoa.
Ví dụ
System.out.printf("%#08X%n", 123); // Kết quả: 0X00007B
System.out.printf("%#08X%n", -123); // Kết quả: 0XFFFFF85 (Với 32-bit integer)
(3) Các chuyển đổi định dạng cho BigInteger
'd' (Số nguyên thập phân)
↳ Định dạng giá trị dưới dạng số nguyên thập phân.
↳ Áp dụng thuật toán định dạng theo địa phương.
↳ Nếu cờ '#' được sử dụng, sẽ ném ra ngoại lệ FormatFlagsConversionMismatchException.
Ví dụ
BigInteger bigInt = new BigInteger("123456789");
System.out.printf("%d%n", bigInt); // Kết quả: 123456789
'o' (Hệ bát phân - octal)
↳ Định dạng giá trị dưới dạng số nguyên hệ bát phân.
↳ Không áp dụng định dạng theo địa phương.
↳ Nếu giá trị âm, kết quả sẽ có dấu '-'.
↳ Nếu giá trị dương hoặc bằng 0 và có cờ '+', kết quả sẽ bắt đầu bằng dấu '+'.
↳ Nếu cờ '#' được sử dụng, kết quả sẽ bắt đầu bằng tiền tố '0'.
↳ Nếu cờ '0' được sử dụng, kết quả sẽ được đệm bằng số 0 để đạt chiều rộng trường.
Ví dụ
BigInteger bigInt = new BigInteger("123456");
System.out.printf("%o%n", bigInt); // Kết quả: 364100
System.out.printf("%#o%n", bigInt); // Kết quả: 0364100
System.out.printf("%+o%n", bigInt); // Kết quả: +364100
System.out.printf("%010o%n", bigInt); // Kết quả: 0000364100
'x' (Hệ mười sáu phân - hexadecimal)
↳ Định dạng giá trị dưới dạng số nguyên hệ mười sáu phân.
↳ Không áp dụng định dạng theo địa phương.
↳ Nếu giá trị âm, kết quả sẽ có dấu '-'.
↳ Nếu giá trị dương hoặc bằng 0 và có cờ '+', kết quả sẽ bắt đầu bằng dấu '+'.
↳ Nếu cờ '#' được sử dụng, kết quả sẽ bắt đầu bằng tiền tố '0x'.
↳ Nếu cờ '0' được sử dụng, kết quả sẽ được đệm bằng số 0 để đạt chiều rộng trường.
Ví dụ
BigInteger bigInt = new BigInteger("123456");
System.out.printf("%x%n", bigInt); // Kết quả: 1e240
System.out.printf("%#x%n", bigInt); // Kết quả: 0x1e240
System.out.printf("%+x%n", bigInt); // Kết quả: +1e240
System.out.printf("%010x%n", bigInt); // Kết quả: 0000001e240
'X' (Hexadecimal, Upper Case)
↳ Tương tự như 'x', nhưng chuyển toàn bộ kết quả sang chữ hoa, bao gồm cả tiền tố '0x' và tất cả các chữ số hexadecimal từ 'a' đến 'f'.
Ví dụ
BigInteger bigInt = new BigInteger("123456");
System.out.printf("%X%n", bigInt); // Kết quả: 1E240
System.out.printf("%#X%n", bigInt); // Kết quả: 0X1E240
System.out.printf("%+X%n", bigInt); // Kết quả: +1E240
System.out.printf("%010X%n", bigInt); // Kết quả: 0000001E240
4. Chuyển đổi định dạng số thực (floating point):
Áp dụng cho các kiểu số thực trong Java: float, Float, double, Double, và BigDecimal.
(1) Các chuyển đổi định dạng cho float và double
'e' (Ký hiệu khoa học - scientific notation)
↳ Định dạng giá trị bằng ký hiệu khoa học.
↳ Áp dụng thuật toán định dạng theo địa phương.
↳ Nếu giá trị là NaN hoặc vô cực, kết quả sẽ là "NaN" hoặc "Infinity" không được định dạng theo địa phương.
↳ Nếu giá trị bằng không (0), chỉ số mũ là "+00".
↳ Kết quả sẽ bao gồm phần nguyên và phần thập phân của giá trị, theo định dạng m và chỉ số mũ.
↳ Số chữ số sau dấu thập phân tương ứng với độ chính xác (precision), mặc định là 6. Nếu độ chính xác nhỏ hơn số chữ số thập phân trong Float.toString(float) hoặc Double.toString(double), giá trị sẽ được làm tròn. Nếu độ chính xác lớn hơn, số không có thể được thêm vào để đạt được độ chính xác yêu cầu.
↳ Nếu cờ ',' được sử dụng, sẽ ném ra FormatFlagsConversionMismatchException.
Ví dụ
double value = 1234.56789;
System.out.printf("%e%n", value); // Kết quả: 1.234568e+03
'E' (Ký hiệu khoa học, chữ hoa)
↳ Tương tự như 'e', nhưng ký hiệu mũ là 'E' (chữ hoa).
Ví dụ
double value = 1234.56789;
System.out.printf("%E%n", value); // 1.234568E+03
'g' (Ký hiệu khoa học tổng quát - general scientific notation)
↳ Định dạng giá trị theo ký hiệu khoa học tổng quát.
↳ Áp dụng thuật toán định dạng theo địa phương.
↳ Nếu giá trị lớn hơn hoặc bằng 10^-4 nhưng nhỏ hơn 10^precision, sử dụng định dạng thập phân.
↳ Nếu giá trị nhỏ hơn 10^-4 hoặc lớn hơn hoặc bằng 10^precision, sử dụng ký hiệu khoa học.
↳ Tổng số chữ số quan trọng là độ chính xác (precision), mặc định là 6. Nếu độ chính xác là 0, nó được coi là 1.
↳ Nếu cờ '#' được sử dụng, sẽ ném ra FormatFlagsConversionMismatchException.
Ví dụ
double value = 0.0001234;
System.out.printf("%g%n", value); // Kết quả: 0.0001234
// Vì giá trị nhỏ hơn 10^-4, nó được định dạng bằng số thập phân.
double value = 123456.789;
System.out.printf("%g%n", value); // 123456
// Vì giá trị lớn hơn 10^4, nó được định dạng bằng ký hiệu khoa học.
'G' (Ký hiệu khoa học tổng quát, chữ hoa)
↳ Tương tự như 'g', nhưng ký hiệu mũ là chữ hoa.
Ví dụ
double value = 0.0001234;
System.out.printf("%G%n", value); // 0.0001234
// Tương tự như 'g', nhưng ký hiệu mũ là chữ hoa 'E'.
'f' (Định dạng thập phân - decimal format)
↳ Định dạng giá trị dưới dạng số thập phân.
↳ Áp dụng thuật toán định dạng theo địa phương.
↳ Nếu giá trị là NaN hoặc vô cực, kết quả sẽ là "NaN" hoặc "Infinity" không được định dạng theo địa phương.
↳ Phần nguyên và phần thập phân của giá trị sẽ được định dạng.
↳ Số chữ số sau dấu thập phân tương ứng với độ chính xác (precision), mặc định là 6. Nếu độ chính xác nhỏ hơn số chữ số thập phân trong Float.toString(float) hoặc Double.toString(double), giá trị sẽ được làm tròn. Nếu độ chính xác lớn hơn, số không có thể được thêm vào để đạt được độ chính xác yêu cầu.
Ví dụ
double value = 1234.56789;
System.out.printf("%f%n", value); // 1234.567890
'a' (Ký hiệu khoa học hệ thập lục phân - hexadecimal exponential)
↳ Định dạng giá trị bằng ký hiệu khoa học hệ thập lục phân.
↳ Không áp dụng định dạng theo địa phương.
↳ Nếu giá trị âm, kết quả sẽ bắt đầu với dấu '-'.
↳ Nếu giá trị dương và cờ '+' được sử dụng, kết quả sẽ bắt đầu với dấu '+'.
↳ Nếu giá trị là NaN hoặc vô cực, kết quả sẽ là "NaN" hoặc "Infinity".
↳ Nếu giá trị là 0, nó sẽ được biểu diễn là "0x0.0p0".
↳ Nếu giá trị là số thực chuẩn (normalized), phần định lượng được biểu diễn với "0x1." và phần số mũ.
↳ Nếu giá trị là số thực không chuẩn (subnormal), và độ chính xác không nằm trong khoảng từ 1 đến 12, nó sẽ được biểu diễn với "0x0." và số mũ "p-1022". Nếu độ chính xác nằm trong khoảng này, giá trị không chuẩn sẽ được chuẩn hóa với "0x1." và số mũ điều chỉnh theo.
Ví dụ
double value = 1234.56789;
System.out.printf("%a%n", value); // 0x1.20f14p+10
'A' (Ký hiệu khoa học hệ thập lục phân, chữ hoa):
↳ Tương tự như 'a', nhưng toàn bộ kết quả sẽ được chuyển sang chữ hoa.
Ví dụ
double value = 1234.56789;
System.out.printf("%A%n", value); // 0x1.20F14P+10
(2) Các chuyển đổi định dạng cho BigDecimal
Các chuyển đổi định dạng cho BigDecimal tương tự như các chuyển đổi cho float và double trong Java, với một số điểm khác biệt như hỗ trợ định dạng thập phân hệ cơ số 16 ('a' và 'A') và độ chính xác có thể được chỉ định cho từng loại định dạng.
'a' (Định dạng thập phân hệ cơ số 16 - hexadecimal exponential form)
Sử dụng định dạng thập phân hệ cơ số 16. Không có định dạng địa phương áp dụng.
Ví dụ
BigDecimal value = new BigDecimal("1234.56789");
System.out.printf("%a%n", value); // 0x1.20f1p+10
'A' (Định dạng thập phân hệ cơ số 16, chữ hoa)
Tương tự như 'a', nhưng ký tự chữ cái được chuyển thành chữ hoa. Ví dụ minh họa
Ví dụ
BigDecimal value = new BigDecimal("1234.56789");
System.out.printf("%A%n", value); // 0X1.20F1P+10
5. Chuyển đổi định dạng ngày/giờ (Date/Time)
Áp dụng cho các kiểu Java có khả năng mã hóa ngày hoặc giờ: long, Long, Calendar, Date, và TemporalAccessor.
Đặc tả định dạng ngày tháng và thời gian sử dụng tiền tố t hoặc T theo sau bởi một ký tự chỉ định kiểu định dạng.
Dưới đây là bảng chứa thông tin về các ký tự định dạng thời gian trong Java:
| Ký tự | Mô tả |
|---|---|
| H | Giờ trong ngày (24 giờ), định dạng hai chữ số (00-23) |
| I | Giờ trong ngày (12 giờ), định dạng hai chữ số (01-12) |
| k | Giờ trong ngày (24 giờ), không có số 0 dẫn đầu (0-23) |
| l | Giờ trong ngày (12 giờ), không có số 0 dẫn đầu (1-12) |
| M | Phút trong giờ, định dạng hai chữ số (00-59) |
| S | Giây trong phút, định dạng hai chữ số (00-60) |
| L | Mili giây trong giây, định dạng ba chữ số (000-999) |
| N | Nano giây trong giây, định dạng chín chữ số (000000000-999999999) |
| p | Con trỏ buổi sáng hoặc chiều theo locale (am/pm) |
| z | Múi giờ theo RFC 822 (ví dụ: -0800) |
| Z | Viết tắt của múi giờ |
| s | Số giây kể từ 1/1/1970 00:00:00 UTC |
| Q | Số mili giây kể từ 1/1/1970 00:00:00 UTC |
Dưới đây là bảng chứa thông tin về các ký tự định dạng ngày trong Java:
| Ký tự | Mô tả |
|---|---|
| B | Tên tháng đầy đủ theo locale (Ví dụ: January, February) |
| b | Tên tháng viết tắt theo locale (Ví dụ: Jan, Feb) |
| A | Tên ngày trong tuần đầy đủ theo locale (Ví dụ: Monday, Tuesday) |
| a | Tên ngày trong tuần viết tắt theo locale (Ví dụ: Mon, Tue) |
| C | Thế kỷ (năm chia cho 100) |
| Y | Năm với ít nhất 4 chữ số (Ví dụ: 2024) |
| y | Hai chữ số cuối của năm (Ví dụ: 24 cho năm 2024) |
| j | Ngày trong năm (001-366, Ví dụ: 032 cho ngày 1 tháng 2) |
| m | Tháng (01-12, Ví dụ: 01 cho tháng 1) |
| d | Ngày trong tháng (01-31, Ví dụ: 09) |
| e | Ngày trong tháng (1-31, không có số 0 phía trước) |
Dưới đây là bảng chứa thông tin về các ký tự định dạng kết hợp trong Java:
| Ký tự | Mô tả |
|---|---|
| R | Thời gian 24 giờ (HH:MM) |
| T | Thời gian 24 giờ (HH:MM:SS) |
| r | Thời gian 12 giờ (HH:MM:SS am/pm) |
| D | Ngày theo định dạng MM/dd/yy |
| F | Ngày theo định dạng ISO 8601 (YYYY-MM-dd) |
| c | Ngày và giờ đầy đủ |
6. Chuyển đổi định dạng phần trăm (Percent)
Kết quả là một ký tự phần trăm % ('\u0025').
Chiều rộng (width): Số lượng ký tự tối thiểu phải được ghi ra bao gồm cả dấu %. Nếu độ dài của giá trị nhỏ hơn chiều rộng, kết quả sẽ được thêm khoảng trắng ('\u0020') ở bên trái cho đủ chiều rộng.
Cờ (flags): Cờ '-' (dấu trừ) có thể áp dụng để căn lề bên trái. Các cờ khác sẽ gây ra lỗi FormatFlagsConversionMismatchException.
Độ chính xác (precision): Không áp dụng. Nếu độ chính xác được chỉ định, sẽ gây ra lỗi IllegalFormatPrecisionException.
Ví dụ
System.out.printf("%5%");
// Kết quả: " %"
7. Chuyển đổi ký tự xuống dòng (Line Separator)
Kết quả là ký tự phân cách dòng của nền tảng hiện tại, lấy từ System.getProperty("line.separator").
Cờ (flags), chiều rộng (width), và độ chính xác (precision): Không áp dụng. Nếu bất kỳ cờ, chiều rộng hoặc độ chính xác nào được cung cấp, sẽ gây ra lỗi tương ứng là IllegalFormatFlagsException, IllegalFormatWidthException, và IllegalFormatPrecisionException.
Ví dụ
System.out.printf("%n");
// Kết quả: (Dấu phân cách dòng của hệ điều hành, ví dụ: "\n" trên Unix/Linux hoặc "\r\n" trên Windows)
Trong ví dụ này, "%n" sẽ in ký tự phân cách dòng của hệ điều hành hiện tại, không cần phải chỉ định chiều rộng hoặc độ chính xác.
Tóm tắt các chuyển đổi: Các chuyển đổi được ký hiệu bằng chữ cái viết hoa (như 'B', 'H', 'S', 'C', 'X', 'E', 'G', 'A', và 'T') tương đương với các ký tự chuyển đổi viết thường tương ứng nhưng kết quả sẽ được chuyển thành chữ hoa theo quy tắc của Locale hiện hành. Kết quả tương đương với việc gọi phương thức String.toUpperCase().