Tại sao tên bộ ký tự không phải là hằng số?


211

Các vấn đề về bộ ký tự là khó hiểu và phức tạp, nhưng trên hết bạn phải nhớ chính xác tên của bộ ký tự của mình. Phải "utf8"không Hay là "utf-8"? Hoặc có thể "UTF-8"? Khi tìm kiếm trên internet các mẫu mã bạn sẽ thấy tất cả những điều trên. Tại sao không chỉ đặt chúng là hằng số và sử dụng Charset.UTF8?


19
+1: Điều này cũng làm tôi khó chịu mọi lúc. Nhân tiện, câu chuyện tương tự diễn MessageDigest#getInstance()ra.
BalusC

2
Để có câu trả lời thực sự, bạn cần phải hỏi ai đó tại Sun. Chúc may mắn với điều đó :-)
Stephen C

1
Stephen C: Tôi tin rằng nó đã được thảo luận trong danh sách gửi thư công khai. -Một người ở mặt trời.
Tom Hawtin - tackline

Câu trả lời:


160

Câu trả lời đơn giản cho câu hỏi được hỏi là các chuỗi ký tự có sẵn khác nhau tùy theo từng nền tảng.

Tuy nhiên, có sáu yêu cầu phải có mặt, vì vậy hằng số có thể đã được thực hiện cho những người từ lâu. Tôi không biết tại sao họ không.

JDK 1.4 đã làm một điều tuyệt vời bằng cách giới thiệu loại Charset. Tại thời điểm này, họ sẽ không muốn cung cấp các hằng chuỗi nữa, vì mục tiêu là để mọi người sử dụng các phiên bản Charset. Vậy tại sao không cung cấp sáu hằng số Charset tiêu chuẩn? Tôi đã hỏi Martin Buchholz vì anh ta ngồi ngay cạnh tôi và anh ta nói rằng không có lý do nào thực sự đặc biệt, ngoại trừ vào thời điểm đó, mọi thứ vẫn còn nửa vời - quá ít API JDK đã được trang bị thêm chấp nhận Charset, và trong số đó, quá tải Charset thường hoạt động kém hơn một chút.

Thật đáng buồn khi chỉ trong JDK 1.6, cuối cùng họ đã hoàn thành việc trang bị mọi thứ với tình trạng quá tải Charset. Và rằng tình huống hiệu suất ngược này vẫn tồn tại (lý do tại sao cực kỳ kỳ lạ và tôi không thể giải thích nó, nhưng có liên quan đến bảo mật!).

Câu chuyện dài - chỉ cần xác định các hằng số của riêng bạn hoặc sử dụng lớp Charsets của Guava mà Tony the Pony liên kết đến (mặc dù thư viện đó chưa thực sự được phát hành).

Cập nhật: một StandardCharsetslớp nằm trong JDK 7.


Chỉ tò mò, có ý tưởng nào khi có bản phát hành (alpha / beta / gì không) của Guava? Trang chủ của dự án là một chút cộc lốc về điều này.
Jonik

Không có gà tây cho tôi cho đến khi nó ra!
Kevin Bourrillion

lý do tại sao cực kỳ kỳ lạ và tôi không thể giải thích nó, nhưng có liên quan đến bảo mật - bạn có thể tạo Chuỗi có thể sửa đổi thông qua bộ ký tự tùy chỉnh, nhưng chúng có thể đã được thực hiện nhanh hơn cả chuỗi (thực sự tìm kiếm bộ ký tự). Đó là một thiếu sót / bỏ bê cách String(byte bytes[], int offset, int length, Charset charset)thực hiện. Trong thực tế, cú đánh hiệu năng hoàn toàn không tầm thường khi tạo một chuỗi nhỏ từ một byte lớn [].
tốt nhất

7
Không công bằng! Bạn có quyền truy cập vào các tài nguyên tuyệt vời như vậy. = (Tôi đã thấy một câu trả lời khác mà bạn đã từng nói, "Vâng, vì vậy tôi đã hỏi Josh [Bloch] về điều đó ..."
kevinarpe

PrintStream không hỗ trợ Charset
rofrol

102

Hai năm sau, StandardCharsets của Java 7 hiện định nghĩa các hằng số cho 6 bộ ký tự chuẩn.

Nếu bạn đang bị mắc kẹt trên Java 5/6, bạn có thể sử dụng ổi của Bộ ký tự hằng, theo đề nghị của Kevin Bourrillion và Jon Skeet.


29

Tôi cho rằng chúng ta có thể làm tốt hơn thế nhiều ... tại sao các bảng mã được bảo đảm có thể truy cập trực tiếp? Charset.UTF8nên là một tham chiếu đến Charset, không phải là tên như một chuỗi. Bằng cách đó, chúng tôi sẽ không phải xử lý UnsupportedEncodingExceptiontất cả mọi nơi.

Nhắc bạn, tôi cũng nghĩ rằng .NET đã chọn một chiến lược tốt hơn bằng cách mặc định là UTF-8 ở mọi nơi. Sau đó, nó bị hỏng bằng cách đặt tên thuộc tính mã hóa "mặc định của hệ điều hành" Encoding.Default- không phải là mặc định trong chính .NET :(

Quay lại tán gẫu về hỗ trợ bộ ký tự của Java - tại sao không có hàm tạo cho FileWriter/ FileReadercái nào cần Charset? Về cơ bản đó là những lớp gần như vô dụng do hạn chế đó - bạn hầu như luôn cần một InputStreamReaderkhoảngFileInputStream hoặc tương đương với đầu ra :(

Y tá, y tá - thuốc của tôi đâu?

EDIT: Điều này xảy ra với tôi rằng điều này đã không thực sự trả lời câu hỏi. Câu trả lời thực sự có lẽ là "không ai liên quan đến nó" hoặc "ai đó liên quan nghĩ rằng đó là một ý tưởng tồi." Tôi đặc biệt khuyên các lớp tiện ích nội bộ cung cấp tên hoặc bảng mã tránh trùng lặp xung quanh cơ sở mã ... Hoặc bạn chỉ có thể sử dụng lớp mà chúng tôi đã sử dụng tại Google khi câu trả lời này được viết lần đầu tiên . (Lưu ý rằng kể từ Java 7, bạn chỉ cần sử dụng StandardCharsetsthay thế.)


2
+1. Nhưng là một phương thức chứ không phải là một lĩnh vực để cho phép tải lười biếng (được thôi, có lẽ bạn sẽ muốn UTF-8, nhưng có một vài bộ ký tự khác về và bạn có thể muốn các phương tiện tương tự cho chúng). Thật không may, điều này dường như không phổ biến với những người đưa ra quyết định.
Tom Hawtin - tackline

Tôi sẽ đủ hạnh phúc với một phương pháp, mặc dù tôi hy vọng rằng việc háo hức tải những bộ ký tự đó sẽ không phải là một chi phí đáng kể.
Jon Skeet

1
Chúng tôi đang ở trong một cuộc thập tự chinh để ngăn chặn lớp học háo hức. / Vừa thực hiện tìm kiếm JDK cho "UTF-8". Tìm thấy 270 kết quả khớp trong tập tin 165. Mặc dù rất nhiều thứ đó là trong rác cũ của Apache (tôi tin rằng nhóm của tôi đóng góp).
Tom Hawtin - tackline

1
@tackline: Tôi cho rằng tải lớp háo hức là một trong những điều gắn kết theo thời gian. Một vài lớp ở đây, một vài lớp ở đó - mỗi lớp nghe có vẻ vô hại đủ - có thể tạo ra sự khác biệt lớn.
Jon Skeet

Liên kết cuối cùng, đến Guava Charsets, bị hỏng.
LarsH

28

Trong Java 1.7

import java.nio.charset.StandardCharsets

Ví dụ: StandardCharsets.UTF_8 StandardCharsets.US_ASCII


5

Trạng thái hiện tại của API mã hóa để lại thứ gì đó mong muốn. Một số phần của Java 6 API không chấp nhận Charsetở vị trí của một chuỗi (trong logging, dom.ls, PrintStream; có thể có những người khác). Nó không giúp gì cho việc mã hóa được cho là có các tên chính tắc khác nhau cho các phần khác nhau của thư viện chuẩn.

Tôi có thể hiểu làm thế nào mọi thứ đến nơi họ đang ở; không chắc chắn tôi có bất kỳ ý tưởng tuyệt vời nào về cách khắc phục chúng.


Như một bên ...

Bạn có thể tra cứu tên để triển khai Java 6 của Sun tại đây .

Đối với UTF-8, các giá trị chính tắc là "UTF-8"cho java.nio"UTF8"cho java.langjava.io. Các mã hóa duy nhất mà thông số kỹ thuật yêu cầu JRE hỗ trợ là: US-ASCII; ISO-8859-1; UTF-8; UTF-16BE; UTF-16LE; UTF-16 .


2
Tôi không bắt đầu PrintStream, vì lớp này nói rõ ràng "Lớp PrintWriter nên được sử dụng trong các tình huống yêu cầu viết ký tự thay vì byte." (Đó là, giống như, tất cả các tình huống ...)
Kevin Bourrillion

2

Từ lâu tôi đã định nghĩa một lớp tiện ích với các hằng số ký tự UTF_8, ISO_8859_1 và US_ASCII.

Ngoài ra, một thời gian dài trước đây (hơn 2 năm) Tôi đã làm một bài kiểm tra hiệu suất đơn giản giữa new String( byte[], Charset )new String( byte[], String charset_name )và phát hiện ra rằng việc thực hiện sau này là đáng kể nhanh hơn. Nếu bạn nhìn vào phần dưới của mã nguồn, bạn sẽ thấy rằng chúng thực sự đi theo một con đường hoàn toàn khác.

Vì lý do đó, tôi đã bao gồm một tiện ích trong cùng một lớp

public static String stringFromByteArray (
    final byte[] array,
    final Charset charset
)
{
    try
    {
        return new String( array, charset.name( ) )
    }
    catch ( UnsupportedEncodingException ex )
    {
        // cannot happen
    }
}

Tại sao hàm tạo String (byte [], Charset) không làm như vậy, đánh bại tôi.


1
Sự Charsetcần thiết không được đăng ký, vì vậy ngoại lệ có thể xảy ra. IIRC, đã có một số thay đổi trong JDK7 để giúp Charsetviệc triển khai được biết đến nhanh hơn (loại bỏ bản sao bổ sung).
Tom Hawtin - tackline
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.