Contents
Giới thiệu
Có nhiều cách định dạng Chuỗi trong Java. Một số trong số chúng là trường học cũ và vay mượn trực tiếp từ các tác phẩm kinh điển cũ (chẳng hạn như printf từ C) trong khi những cái khác thiên về lập trình hướng đối tượng, chẳng hạn như MessageFormat lớp.
Trong bài viết này, chúng tôi sẽ điểm qua một số cách tiếp cận này. Chúng tôi sẽ chỉ ra một số chi tiết cụ thể về cách mỗi kỹ thuật có thể được sử dụng và trong những trường hợp nào. Sử dụng kiến thức này, bạn sẽ biết cách tiếp cận các Chuỗi định dạng và sử dụng các kỹ thuật nào.
System.out.printf ()
Hãy bắt đầu với cổ điển cũ, printf(). Như đã nhắc đến trước đó, printf() đến từ ngôn ngữ lập trình C và là viết tắt của in định dạng. Dưới mui xe, printf() sử dụng java.util.Formattermà chúng ta sẽ nói sau.
Cách printf() công trình có thể được giải thích bằng các lập luận của nó. Cách sử dụng phổ biến nhất printf() là như sau:
System.out.printf(String format, String... arguments);
Chúng ta có thể thấy rằng phương pháp mong đợi một format và một con kỳ đà arguments. Các format đối số xác định cách bạn muốn Chuỗi được định dạng – a bản mẫu để biết kết quả cuối cùng.
Ví dụ: bạn có thể muốn in một số thập phân với chính xác bảy chữ số thập phân hoặc một số ở dạng biểu diễn thập lục phân. Hoặc, bạn có thể có một thông báo được xác định trước để chào mừng người dùng, nhưng muốn định dạng nó để bao gồm tên người dùng.
Các arguments vararg mong đợi một cách thuận tiện các đối số (tức là giá trị) cho Chuỗi mẫu. Ví dụ: nếu mẫu có trình giữ chỗ cho hai số, thì printf() phương pháp cũng sẽ mong đợi hai số như arguments:
System.out.printf("%d %d", 42, 23);
Chúng tôi đã đặt hai %d ký hiệu trong chuỗi mẫu. Hai biểu tượng này đại diện cho trình giữ chỗ cho một loại giá trị nhất định. Ví dụ, %d là một trình giữ chỗ cho một giá trị số thập phân. Vì chúng tôi có hai trong số chúng, nên chúng tôi phải chuyển hai đối số tương ứng với các giá trị số, chẳng hạn như 42 và 23.
Chạy mã này sẽ mang lại:
42 23
Định dạng chỉ định
Với printf()bạn có thể in các giá trị như số, Chuỗi, ngày, v.v. Để cho phương thức biết chính xác những gì bạn đang cố in, bạn cần cung cấp định dạng định dạng cho mỗi giá trị. Hãy xem một ví dụ:
System.out.printf("Hello, %s!", "reader");
Nếu được thực thi, mã này sẽ in Hello, reader vào bảng điều khiển. Các %s biểu tượng đại diện cho một chỉ định định dạng cho Chuỗi, tương tự như cách %d đại diện cho một chỉ định định dạng cho các số thập phân.
Có nhiều định dạng chúng ta có thể sử dụng. Dưới đây là một số cái phổ biến:
- %c – Tính cách
- %d – Số thập phân (cơ số 10)
- %e – Số dấu phẩy động hàm mũ
- %f – Số điểm nổi
- %i – Số nguyên (cơ số 10)
- %o – Số bát phân (cơ số 8)
- %s – Sợi dây
- %u – Số thập phân (số nguyên) không dấu
- %x – Số thập lục phân (cơ số 16)
- %t – Ngày giờ
- %n – Dòng mới
Nếu chúng ta muốn in, ví dụ, một ký tự và một số bát phân, chúng ta sẽ sử dụng %c và %o các thông số kỹ thuật, tương ứng. Bạn có thể nhận thấy điều gì đó bất thường: mã định nghĩa dòng mới. Nếu bạn không quen printf()hành vi của C, có vẻ hơi kỳ lạ khi phải chỉ định những thứ như thế này.
Tốt, printf() không viết một dòng mới theo mặc định. Trên thực tế, nó gần như không có gì theo mặc định. Về cơ bản, nếu bạn muốn điều gì đó xảy ra, bạn phải tự làm cho nó xảy ra.
Điều đó có nghĩa là – nếu chúng ta có nhiều printf() các câu lệnh không có mã định nghĩa dòng mới:
System.out.printf("Hello, %s!", "Michael Scott"); System.out.printf("Hello, %s!", "Jim"); System.out.printf("Hello, %s!", "Dwight");
Kết quả sẽ là:
Hello, Michael Scott!Hello, Jim!Hello, Dwight!
Tuy nhiên, nếu chúng ta bao gồm ký tự dòng mới:
System.out.printf("Hello, %s!%n", "Michael Scott"); System.out.printf("Hello, %s!%n", "Jim"); System.out.printf("Hello, %s!%n", "Dwight");
Sau đó, kết quả sẽ là:
Hello, Michael Scott! Hello, Jim! Hello, Dwight!
Ghi chú: %n là một định dạng đặc biệt có thể là rn hoặc chỉ n. n là biểu tượng dòng mới thực tế, trong khi r là ký hiệu xuống dòng. Thông thường, bạn nên sử dụng n vì nó hoạt động như mong đợi trên tất cả các hệ thống, không giống như %n mà có thể được hiểu là một trong hai. Thêm về điều này sau.
Nhân vật thoát
Ngoài các ký hiệu định dạng được nêu ở trên, có một loại ký hiệu định dạng khác: Nhân vật thoát.
Hãy tưởng tượng rằng chúng ta muốn in “ biểu tượng sử dụng printf(). Chúng tôi có thể thử một cái gì đó như:
System.out.printf(""");
Nếu bạn thử chạy điều này, trình biên dịch của bạn chắc chắn sẽ ném ra một ngoại lệ. Nếu bạn nhìn kỹ, ngay cả mã đánh dấu mã trên trang này cũng sẽ nổi bật ); dưới dạng một Chuỗi, và không phải là dấu ngoặc đóng của phương thức.
Điều đã xảy ra là chúng tôi đã thử in một biểu tượng có ý nghĩa đặc biệt, riêng biệt. Dấu ngoặc kép được sử dụng để biểu thị phần đầu và phần cuối của một Chuỗi.
Chúng tôi đã bắt đầu và kết thúc một chuỗi “”sau đó chúng tôi đã mở một cái khác “ nhưng chưa đóng nó. Điều này làm cho việc in các ký tự dành riêng như thế này Không thể nàosử dụng cách tiếp cận này.
Cách để vượt qua điều này là trốn thoát. Để in các ký tự đặc biệt (chẳng hạn như “) trực tiếp, chúng ta cần phải thoát khỏi các hiệu ứng của nó trước tiên và trong Java điều đó có nghĩa là đặt tiền tố nó bằng một dấu gạch chéo ngược (). To legally print a quotation mark in Java we would do the following:
System.out.printf(""");
Sự kết hợp của and “ đặc biệt cho trình biên dịch biết rằng chúng tôi muốn chèn “ nhân vật ở vị trí đó và nó sẽ coi “ như một giá trị cụ thể, không phải là một biểu tượng dành riêng.
Áp dụng ký tự thoát can invoke different effects based on the subsequent one. Passing a regular character (non-reserved) won’t do anything and will be treated as a value.
Though, certain combinations (also called commands) have a different meaning to the compiler:
- b – Chèn backspace
- f – Ký tự đầu tiên của dòng tiếp theo bắt đầu ở bên phải ký tự cuối cùng của dòng hiện tại
- n – Chèn dòng mới
- r – Chèn ký tự xuống dòng
- t – Chèn tab
- – Chèn dấu gạch chéo ngược
- %% – Chèn dấu phần trăm
Vì vậy, bạn sẽ sử dụng n để in dấu phân cách dòng vào bảng điều khiển, bắt đầu một cách hiệu quả bất kỳ nội dung mới nào từ đầu dòng tiếp theo. Tương tự, để thêm các tab, bạn sẽ sử dụng t người chỉ định.
Bạn có thể đã nhận thấy %% là sự kết hợp cuối cùng.
Tại sao thế này? Tại sao không % sử dụng đơn giản?
Các % nhân vật đã là một nhân vật thoát dành riêng cho printf() phương pháp. Theo sau là các ký tự như d, i, fv.v., trình định dạng trong thời gian chạy biết cách xử lý các giá trị này.
Các character, however, is meant for the compiler. It tells it where and what to insert. The % chỉ đơn giản là không được xác định và chúng tôi sử dụng % nhân vật thoát để thoát khỏi ảnh hưởng của phần tiếp theo % nhân vật – nếu điều đó có ý nghĩa.
Đối với trình biên dịch, % không phải là một nhân vật đặc biệt, nhưng is. Also, it’s convention that special characters escape themselves. escapes and % trốn thoát %.
Cách sử dụng cơ bản
Hãy định dạng một Chuỗi với nhiều đối số thuộc các kiểu khác nhau:
System.out.printf("The quick brown %s jumps %d times over the lazy %s.n", "fox", 2, "dog");
Đầu ra sẽ là:
The quick brown fox jumps 2 times over the lazy dog.
Phao và độ chính xác kép
Với printf()chúng tôi có thể xác định độ chính xác tùy chỉnh cho các số dấu phẩy động:
double a = 35.55845;
double b = 40.1245414;
System.out.printf("a = %.2f b = %.4f", a, b);
Từ %f được sử dụng cho phao, chúng tôi có thể sử dụng nó để in doubleS. Tuy nhiên, bằng cách thêm một .nở đâu n là số vị trí thập phân, chúng tôi có thể xác định độ chính xác tùy chỉnh.
Chạy mã này mang lại:
a = 35.56
b = 40.1245
Định dạng đệm
Chúng tôi cũng có thể thêm phần đệm, bao gồm cả Chuỗi đã truyền:
System.out.printf("%10sn", "stack");
Đây, sau khi % ký tự, chúng tôi đã chuyển một số và một định dạng. Cụ thể, chúng tôi muốn một Chuỗi với 10, theo sau là một dòng mới. Từ stack chỉ chứa 5 ký tự, 5 ký tự khác được thêm vào làm phần đệm để “lấp đầy” Chuỗi vào mục tiêu ký tự:
stack
Bạn cũng có thể thêm phần đệm bên phải để thay thế:
System.out.printf("%-10sn", "stack");
Ngôn ngữ
Chúng tôi cũng có thể vượt qua một Locale làm đối số đầu tiên, định dạng Chuỗi theo nó:
System.out.printf(Locale.US, "%,dn", 5000);
System.out.printf(Locale.ITALY, "%,dn", 5000);
Điều này sẽ tạo ra hai số nguyên có định dạng khác nhau:
5,000
5.000
Chỉ mục đối số
Nếu không có chỉ mục đối số nào được cung cấp, các đối số sẽ đơn giản tuân theo thứ tự hiện diện trong lệnh gọi phương thức:
System.out.printf("First argument is %d, second argument is %d", 2, 1);
Điều này sẽ dẫn đến:
First argument is 2, argument number is 1
Tuy nhiên, sau khi % ký tự thoát và trước mã định dạng, chúng ta có thể thêm một lệnh khác. $n sẽ chỉ định chỉ mục đối số:
System.out.printf("First argument is %2$d, second argument is %1$d", 2, 1);
Đây, 2$ nằm giữa % và d. 2$ chỉ định rằng chúng tôi muốn đính kèm thứ hai đối số từ danh sách các đối số đến cái này người chỉ định. Tương tự, 1$ chỉ định rằng chúng tôi muốn đính kèm đối số đầu tiên từ danh sách vào trình xác định khác.
Chạy mã này dẫn đến:
First argument is 1, second argument is 2
Bạn có thể trỏ cả hai từ chỉ định vào cùng một đối số. Trong trường hợp của chúng tôi, điều đó có nghĩa là chúng tôi chỉ sử dụng một đối số duy nhất được cung cấp trong danh sách. Điều đó hoàn toàn tốt – mặc dù chúng tôi vẫn phải cung cấp tất cả các đối số có trong chuỗi mẫu:
System.out.printf("First argument is %2$d, second argument is %2$d", 2, 1);
Điều này sẽ dẫn đến:
First argument is 1, second argument is 1
System.out.format ()
Trước khi nói về System.out.format()chúng ta hãy tập trung ngắn gọn vào System.out.
Tất cả các hệ thống UNIX đều có ba đường ống chính – ống đầu vào tiêu chuẩn (stdin), ống đầu ra tiêu chuẩn (stdout) và đường ống lỗi tiêu chuẩn (stderr). Các out Tương ứng với stdout ống và là của PrintStream gõ phím.
Lớp này có nhiều phương thức khác nhau để in các biểu diễn dựa trên văn bản được định dạng vào một luồng, một số trong số đó là format() và printf().
Theo tài liệu, cả hai đều hoạt động trong chính xác theo cùng một cách. Điều này có nghĩa là không có sự khác biệt giữa hai và có thể được sử dụng cho các kết quả giống nhau. Tất cả những gì chúng tôi đã nói cho đến nay về printf() cũng hoạt động cho format().
Cả hai printf() và System.out.format() in ra stdout pipe, thường nhắm vào bảng điều khiển / thiết bị đầu cuối.
String.format ()
Một cách khác để định dạng Chuỗi là với String.format() phương pháp nội bộ cũng sử dụng java.util.Formattermà chúng ta sẽ khám phá trong phần tiếp theo.
Ưu điểm chính của String.format() kết thúc printf() là kiểu trả về của nó – nó trả về một String. Thay vì chỉ in nội dung trên ống xuất tiêu chuẩn và không có kiểu trả về (void) như printf() làm, String.format() được sử dụng để định dạng một Chuỗi có thể được sử dụng hoặc tái sử dụng trong tương lai:
String formattedString = String.format("Local time: %tT", Calendar.getInstance());
Bây giờ bạn có thể làm bất cứ điều gì bạn muốn formattedString. Bạn có thể in nó, bạn có thể lưu nó vào một tệp, bạn có thể thay đổi nó hoặc duy trì nó trong một cơ sở dữ liệu. Việc in nó sẽ dẫn đến:
Local time: 16:01:42
Các String.format() phương pháp sử dụng cùng một nguyên tắc cơ bản như printf() phương pháp. Cả hai đều sử dụng nội bộ Formatter lớp để thực sự định dạng các Chuỗi. Vì vậy, tất cả mọi thứ nói cho printf() cũng áp dụng cho String.format() phương pháp.
Sử dụng printf(), String.format() hoặc Formatter về cơ bản là cùng một thứ. Điều duy nhất khác biệt là kiểu trả về – printf() in ra luồng đầu ra tiêu chuẩn (thường là bảng điều khiển của bạn) và String.format() trả về một định dạng String.
Điều đó đang được nói, String.format() linh hoạt hơn vì bạn thực sự có thể sử dụng kết quả theo nhiều cách.
Lớp định dạng
Vì tất cả các phương thức trên đều gọi Formatterchỉ cần biết một nghĩa là bạn biết tất cả chúng.
Việc sử dụng Formatter là khá giống với các kỹ thuật khác được hiển thị trước đây. Sự khác biệt lớn nhất là để sử dụng nó, người ta cần khởi tạo một Formatter:
Formatter f = new Formatter();
f.format("There are %d planets in the Solar System. Sorry, Pluto", 8);
System.out.println(f);
Điều này đặt ra câu hỏi:
Tại sao tôi không luôn sử dụng các phương pháp trước, vì chúng ngắn gọn hơn?
Có một điểm khác biệt quan trọng nữa khiến Formatter lớp khá linh hoạt:
StringBuilder sb = new StringBuilder();
Formatter formatter = new Formatter(sb);
formatter.format("%d, %d, %d...n", 1, 2, 3);
Thay vì chỉ làm việc với StringS, Formatter cũng có thể làm việc với StringBuilder điều này làm cho nó có thể (lại) sử dụng cả hai lớp một cách hiệu quả.
Trên thực tế, Formatter có thể làm việc với bất kỳ lớp nào triển khai Appendable. Một ví dụ như đã nói ở trên StringBuildernhưng các ví dụ khác bao gồm các lớp như BufferedWriter, FileWriter, PrintStream, PrintWriter, StringBufferv.v. Danh sách đầy đủ có thể được tìm thấy trong tài liệu.
Cuối cùng, tất cả các chỉ định định dạng, ký tự thoát, v.v. cũng hợp lệ cho Formatter vì đây là logic chính để định dạng Chuỗi trong cả ba trường hợp: String.format(), printf()và Formatter.
MessageFormat
Cuối cùng, hãy trình bày một kỹ thuật định dạng cuối cùng không sử dụng Formatter dưới mui xe.
MessageFormat được tạo ra để sản xuất và cung cấp các thông điệp được nối theo cách trung lập về ngôn ngữ. Điều này có nghĩa là định dạng sẽ giống nhau, bất kể bạn đang sử dụng Java, Python hay một số ngôn ngữ khác hỗ trợ MessageFormat.
MessageFormat mở rộng phần tóm tắt Format lớp học, chỉ như thế nào DateFormat và NumberFormat làm. Các Format lớp có nghĩa là để định dạng các đối tượng nhạy cảm với ngôn ngữ thành Chuỗi.
Hãy xem một ví dụ hay, lịch sự của MessageFormat‘S tài liệu.
int planet = 7;
String event = "a disturbance in the Force";
String result = MessageFormat.format(
"At {1, time} on {1, date}, there was {2} on planet {0, number, integer}.",
planet, new Date(), event
);
Mã tín dụng: Tài liệu Oracle
Đầu ra là:
At 11:52 PM on May 4, 2174, there was a disturbance in the Force on planet 7.
Thay vì chỉ định tỷ lệ phần trăm mà chúng ta đã thấy cho đến nay, ở đây chúng tôi đang sử dụng dấu ngoặc nhọn cho mỗi đối số. Hãy lập luận đầu tiên, {1, time}. Con số 1 đại diện cho chỉ mục của đối số sẽ được sử dụng ở vị trí của nó. Trong trường hợp của chúng tôi, các đối số là planet, new Date()và event.
Phần thứ hai, time, đề cập đến loại giá trị. Các loại định dạng cấp cao nhất là number, date, timevà choice. Đối với mỗi giá trị, một lựa chọn cụ thể hơn có thể được thực hiện, chẳng hạn như với{0, number, integer} điều này nói rằng giá trị không chỉ được coi là một số mà còn là một số nguyên.
Bạn có thể tìm thấy tập hợp đầy đủ các kiểu định dạng và kiểu con trong tài liệu.
Sự kết luận
Trong bài viết này, chúng tôi đã điểm qua một số cách hợp lý để định dạng Chuỗi trong Java lõi.
Mỗi kỹ thuật mà chúng tôi đã trình bày đều có lý do tồn tại của riêng nó. printf()ví dụ, gợi nhớ đến phương thức C kiểu cũ có cùng tên từ.
Các cách tiếp cận khác, chẳng hạn như Formatter hoặc MessageFormat đưa ra một cách tiếp cận hiện đại hơn nhằm khai thác một số lợi ích của lập trình hướng đối tượng.
Mỗi kỹ thuật có các trường hợp sử dụng cụ thể, vì vậy hy vọng, bạn sẽ có thể biết khi nào nên sử dụng từng trường hợp trong tương lai.