Đặt mã hóa ký tự Java mặc định


362

Làm cách nào để đặt đúng mã hóa ký tự mặc định được sử dụng bởi JVM (1.5.x) theo chương trình?

Tôi đã đọc được rằng đó -Dfile.encoding=whateverlà cách để sử dụng các JVM cũ hơn. Tôi không có sự xa xỉ đó vì những lý do tôi sẽ không tham gia.

Tôi đã thử:

System.setProperty("file.encoding", "UTF-8");

Và thuộc tính được đặt, nhưng dường như không gây ra getBytescuộc gọi cuối cùng bên dưới để sử dụng UTF8:

System.setProperty("file.encoding", "UTF-8");

byte inbytes[] = new byte[1024];

FileInputStream fis = new FileInputStream("response.txt");
fis.read(inbytes);
FileOutputStream fos = new FileOutputStream("response-2.txt");
String in = new String(inbytes, "UTF8");
fos.write(in.getBytes());

Những người bình luận tuyệt vời - và những điều tôi đã tự suy nghĩ. Thật không may, có một lệnh gọi String.getBytes () bên dưới mà tôi không kiểm soát được. Cách duy nhất hiện tại tôi thấy để giải quyết vấn đề là đặt mã hóa mặc định theo chương trình. Bất cứ một đề nghị nào khác?

6
có thể câu hỏi không liên quan nhưng, có sự khác biệt khi UTF8 được đặt với "UTF8", "UTF-8" hoặc "utf8". Gần đây, tôi thấy rằng các bộ chứa IBM WAS 6.1 EJB và WEB xử lý các chuỗi khác nhau (theo cách phân biệt chữ hoa chữ thường) được sử dụng để xác định mã hóa.
igor.beslic

5
Chỉ là một chi tiết nhưng: thích UTF-8 hơn UTF8 (chỉ trước đây là tiêu chuẩn). Điều này vẫn được áp dụng vào năm 2012 ...
Barshe Roussy

4
Cài đặt hoặc đọc thuộc file.encodingtính không được hỗ trợ .
McDowell

@erickson Vẫn chưa rõ ràng với truy vấn, Có đúng không, "file.encoding" có liên quan khi các luồng I / O dựa trên ký tự được sử dụng (tất cả các lớp con của class Reader& class Writer)? Vì class FileInputStreamlà luồng I / O dựa trên byte, vậy tại sao người ta nên quan tâm đến bộ ký tự trong luồng I / O dựa trên byte?
trao đổi quá mức

Câu trả lời:


311

Thật không may, thuộc file.encodingtính phải được chỉ định khi JVM khởi động; tại thời điểm phương thức chính của bạn được nhập, mã hóa ký tự được sử dụng bởi String.getBytes()và các hàm tạo mặc định của InputStreamReaderOutputStreamWriterđã được lưu trữ vĩnh viễn.

Như Edward Grech chỉ ra, trong trường hợp đặc biệt như thế này, biến môi trường JAVA_TOOL_OPTIONS có thể được sử dụng để chỉ định thuộc tính này, nhưng nó thường được thực hiện như thế này:

java -Dfile.encoding=UTF-8  com.x.Main

Charset.defaultCharset()sẽ phản ánh các thay đổi đối với thuộc file.encodingtính, nhưng hầu hết mã trong các thư viện Java cốt lõi cần xác định mã hóa ký tự mặc định không sử dụng cơ chế này.

Khi bạn đang mã hóa hoặc giải mã, bạn có thể truy vấn thuộc file.encodingtính hoặc Charset.defaultCharset()để tìm mã hóa mặc định hiện tại và sử dụng phương thức thích hợp hoặc quá tải hàm tạo để chỉ định nó.


9
Để hoàn thiện, tôi muốn thêm rằng với một chút mánh khóe, bạn có thể sử dụng mã hóa mặc định thực sự được sử dụng (như được lưu trong bộ nhớ cache), nhờ Gary Cronin: byte [] byteArray = {'a'}; InputStream inputStream = new ByteArrayInputStream (byteArray); Đầu đọc InputStreamReader = new InputStreamReader (inputStream); Chuỗi defaultEncoding = reader.getEncoding (); liệt kê.xcf.ber siêu.edu /lists / adv cân
Stijn de Witt

2
JDK-4163515 có thêm một số thông tin về việc thiết lập file.encodingsysprop sau khi khởi động JVM.
Caspar

2
Tôi đã gãi đầu vì lệnh đó không hoạt động hoàn hảo trên Windows, linux và mac ... sau đó tôi đặt "xung quanh giá trị như thế này: java -D" file.encoding = UTF-8 "
-jar

kiểm tra câu trả lời của tôi trong trường hợp Java Spring Boot: stackoverflow.com/a/48952844/986160
Michail Michailidis

170

Từ tài liệu Giao diện công cụ JVM ™

Do dòng lệnh không thể luôn luôn được truy cập hoặc sửa đổi, ví dụ như trong các máy ảo nhúng hoặc đơn giản là các máy ảo được khởi chạy sâu bên trong các tập lệnh, một JAVA_TOOL_OPTIONSbiến được cung cấp để các tác nhân có thể được khởi chạy trong các trường hợp này.

Bằng cách đặt biến môi trường (Windows) JAVA_TOOL_OPTIONSthành -Dfile.encoding=UTF8, thuộc tính (Java) Systemsẽ được đặt tự động mỗi khi JVM được khởi động. Bạn sẽ biết rằng tham số đã được chọn vì thông báo sau sẽ được đăng lên System.err:

Picked up JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF8


Bạn có biết rằng câu lệnh "Nhặt ..." sẽ được in trong nhật ký Tomcat không?
thatidiotguy

1
Xin chào Edward Grech Tôi cảm ơn bạn cho giải pháp của bạn. Nó đã được giải quyết vấn đề của tôi trong một bài đăng diễn đàn khác. stackoverflow.com/questions/14814230/
Mạnh

8
UTF8hay UTF-8?
Tiny


Giải pháp của bạn đã tiết kiệm thời gian của tôi, cảm ơn rất nhiều !!
Sobhan

67

Tôi có một cách hacky chắc chắn hoạt động !!

System.setProperty("file.encoding","UTF-8");
Field charset = Charset.class.getDeclaredField("defaultCharset");
charset.setAccessible(true);
charset.set(null,null);

Theo cách này, bạn sẽ lừa JVM, người sẽ nghĩ rằng bộ ký tự không được đặt và làm cho nó được đặt lại thành UTF-8, trong thời gian chạy!


2
NoSuchFieldException cho tôi
SparK

10
Để hack hoạt động, bạn cần giả sử trình quản lý bảo mật tắt. Nếu bạn không có cách đặt cờ JVM, bạn cũng có thể có một hệ thống kích hoạt trình quản lý bảo mật.
Yonatan

3
JDK9 không chấp nhận hack này nữa. WARNING: An illegal reflective access operation has occurred • WARNING: Illegal reflective access by [..] • WARNING: Please consider reporting this to the maintainers of [..] • WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations • WARNING: All illegal access operations will be denied in a future release
dotwin

1
@Enerccio: Đó không phải là một câu trả lời hay, đó là một vụ hack bẩn thỉu và một vấn đề đang chờ xảy ra. Điều đó chỉ nên được sử dụng như một biện pháp khẩn cấp.
sleske

1
@Enerccio: Không thể tranh cãi liệu Java "nên" có cách nào để thiết lập điều này hay không - người ta cũng có thể lập luận rằng các nhà phát triển "nên" chỉ định rõ ràng mã hóa bất cứ khi nào có liên quan. Ở mức độ nào, giải pháp này có khả năng gây ra sự cố nghiêm trọng trong thời gian dài hơn, do đó hãy cẩn thận "chỉ sử dụng trong trường hợp khẩn cấp". Trên thực tế, thậm chí sử dụng tình trạng khẩn cấp là vấn đề, bởi vì có một cách hỗ trợ để làm việc đó, thiết lập JAVA_TOOL_OPTIONS như được giải thích trong câu trả lời khác.
sleske

38

Tôi nghĩ rằng một cách tiếp cận tốt hơn so với việc đặt bộ ký tự mặc định của nền tảng, đặc biệt là khi bạn dường như có các hạn chế ảnh hưởng đến việc triển khai ứng dụng, chứ đừng nói đến nền tảng, là gọi an toàn hơn nhiều String.getBytes("charsetName"). Bằng cách đó, ứng dụng của bạn không phụ thuộc vào những thứ nằm ngoài sự kiểm soát của nó.

Cá nhân tôi cảm thấy điều đó String.getBytes()nên được phản đối, vì nó đã gây ra vấn đề nghiêm trọng trong một số trường hợp tôi đã thấy, trong đó nhà phát triển không tính đến bộ ký tự mặc định có thể thay đổi.


18

Tôi không thể trả lời câu hỏi ban đầu của bạn nhưng tôi muốn cung cấp cho bạn một số lời khuyên - đừng phụ thuộc vào mã hóa mặc định của JVM. Luôn luôn tốt nhất là chỉ định rõ ràng mã hóa mong muốn (tức là "UTF-8") trong mã của bạn. Theo cách đó, bạn biết nó sẽ hoạt động ngay cả trên các hệ thống và cấu hình JVM khác nhau.


7
Tất nhiên, ngoại trừ, nếu bạn đang viết một ứng dụng máy tính để bàn và xử lý một số văn bản do người dùng chỉ định không có bất kỳ siêu dữ liệu mã hóa nào - thì mã hóa mặc định của nền tảng là dự đoán tốt nhất của bạn về những gì người dùng có thể đang sử dụng.
Michael Borgwardt

@MichaelBorgwardt "thì mã hóa mặc định của nền tảng là phỏng đoán tốt nhất của bạn" dường như bạn đang khuyên rằng muốn thay đổi mặc định không phải là một ý tưởng hay. Bạn có nghĩa là, sử dụng một mã hóa rõ ràng bất cứ nơi nào có thể, sử dụng dafault được cung cấp khi không có gì khác có thể?
Raedwald

1
@Raedwald: vâng, đó là những gì tôi muốn nói. Mã hóa mặc định của nền tảng là (ít nhất là trên máy người dùng cuối) những gì người dùng trong ngôn ngữ mà hệ thống được đặt thường sử dụng. Đó là thông tin bạn nên sử dụng nếu bạn không có thông tin tốt hơn (cụ thể là tài liệu).
Michael Borgwardt

1
@MichaelBorgwardt Vô nghĩa. Sử dụng thư viện để tự động phát hiện mã hóa đầu vào và lưu dưới dạng Unicode với BOM. Đó là cách duy nhất để đối phó và chống lại địa ngục mã hóa.
Alexanderr Dubinsky 16/12/13

Tôi nghĩ rằng hai bạn không ở cùng một trang. Michael nói về việc giải mã trong khi Raedwald bạn nói về việc xử lý sau khi giải mã.
WesternGun

12

Thử cái này :

    new OutputStreamWriter( new FileOutputStream("Your_file_fullpath" ),Charset.forName("UTF8"))

5

Chúng tôi đã có những vấn đề tương tự. Chúng tôi đã thử một cách có phương pháp một số đề xuất từ ​​bài viết này (và những người khác) nhưng không có kết quả. Chúng tôi cũng đã cố gắng thêm -Dfile.encoding=UTF8và dường như không có gì là làm việc.

Đối với những người đang gặp vấn đề này, bài viết sau đây cuối cùng đã giúp chúng tôi theo dõi mô tả cách thiết lập miền địa phương có thể phá vỡ unicode/UTF-8trongJava/Tomcat

http://www.jvmhost.com/articles/locale-breaks-unicode-utf-8-java-tomcat

Đặt ngôn ngữ chính xác trong ~/.bashrctệp làm việc cho chúng tôi.


4

Tôi đã thử rất nhiều thứ, nhưng mã mẫu ở đây hoạt động hoàn hảo. Liên kết

Mấu chốt của mã là:

String s = "एक गाव में एक किसान";
String out = new String(s.getBytes("UTF-8"), "ISO-8859-1");

4

Trong trường hợp bạn đang sử dụng Spring Boot và muốn truyền đối số file.encodingtrong JVM, bạn phải chạy nó như thế:

mvn spring-boot:run -Drun.jvmArguments="-Dfile.encoding=UTF-8"

điều này là cần thiết cho chúng tôi vì chúng tôi đang sử dụng các JTwigmẫu và hệ điều hành ANSI_X3.4-1968mà chúng tôi đã tìm ra thông quaSystem.out.println(System.getProperty("file.encoding"));

Hy vọng điều này sẽ giúp được ai đó!


2

Tôi đang sử dụng Bean Beanalk của Amazon (AWS) và đã đổi thành công thành UTF-8.

Trong Bean Beanalk, đi đến Cấu hình> Phần mềm, "Thuộc tính môi trường". Thêm (tên) JAVA_TOOL_OPTIONS với (giá trị) -Dfile.encoding = UTF8

Sau khi lưu, môi trường sẽ khởi động lại với mã hóa UTF-8.


1

Không rõ ràng về những gì bạn làm và không có quyền kiểm soát tại thời điểm này. Nếu bạn có thể xen kẽ một lớp OutputStream khác vào tệp đích, bạn có thể sử dụng một kiểu con của OutputStream để chuyển Chuỗi thành byte theo một bộ ký tự bạn xác định, nói theo UTF-8 theo mặc định. Nếu UTF-8 được sửa đổi phù hợp với nhu cầu của bạn, bạn có thể sử dụng DataOutputStream.writeUTF(String):

byte inbytes[] = new byte[1024];
FileInputStream fis = new FileInputStream("response.txt");
fis.read(inbytes);
String in = new String(inbytes, "UTF8");
DataOutputStream out = new DataOutputStream(new FileOutputStream("response-2.txt"));
out.writeUTF(in); // no getBytes() here

Nếu cách tiếp cận này không khả thi, có thể hữu ích nếu bạn làm rõ ở đây chính xác những gì bạn có thể và không thể kiểm soát về mặt luồng dữ liệu và môi trường thực thi (mặc dù tôi biết rằng đôi khi nói dễ hơn xác định). Chúc may mắn.


5
DataInputStream và DataOutputStream là các lớp có mục đích đặc biệt không bao giờ được sử dụng với các tệp văn bản thuần túy. UTF-8 được sửa đổi mà họ sử dụng không tương thích với UTF-8 thực. Ngoài ra, nếu OP có thể sử dụng giải pháp của bạn, anh ta cũng có thể sử dụng công cụ phù hợp cho công việc này: một OutputStreamWriter.
Alan Moore

1
mvn clean install -Dfile.encoding=UTF-8 -Dmaven.repo.local=/path-to-m2

lệnh đã làm việc với exec-maven-plugin để giải quyết lỗi sau trong khi định cấu hình tác vụ jenkins.

Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=512m; support was removed in 8.0
Error occurred during initialization of VM
java.nio.charset.IllegalCharsetNameException: "UTF-8"
    at java.nio.charset.Charset.checkName(Charset.java:315)
    at java.nio.charset.Charset.lookup2(Charset.java:484)
    at java.nio.charset.Charset.lookup(Charset.java:464)
    at java.nio.charset.Charset.defaultCharset(Charset.java:609)
    at sun.nio.cs.StreamEncoder.forOutputStreamWriter(StreamEncoder.java:56)
    at java.io.OutputStreamWriter.<init>(OutputStreamWriter.java:111)
    at java.io.PrintStream.<init>(PrintStream.java:104)
    at java.io.PrintStream.<init>(PrintStream.java:151)
    at java.lang.System.newPrintStream(System.java:1148)
    at java.lang.System.initializeSystemClass(System.java:1192)

0

Chúng tôi đặt hai thuộc tính hệ thống với nhau và nó làm cho hệ thống đưa mọi thứ vào utf8

file.encoding=UTF8
client.encoding.overrideUTF-8

7
Thuộc tính client.encoding.override dường như là đặc thù của WebSphere.
Barshe Roussy


0

Gần đây, tôi tình cờ gặp một hệ thống Ghi chú 6.5 của một công ty địa phương và phát hiện ra webmail sẽ hiển thị các ký tự không xác định được trên bản cài đặt Windows không phải địa phương của Zhongwen. Đã đào trong vài tuần trực tuyến, tìm ra nó chỉ vài phút trước:

Trong các thuộc tính Java, thêm chuỗi sau vào Tham số thời gian chạy

-Dfile.encoding=MS950 -Duser.language=zh -Duser.country=TW -Dsun.jnu.encoding=MS950

Cài đặt UTF-8 sẽ không hoạt động trong trường hợp này.


0

Nhóm của tôi gặp phải vấn đề tương tự trong các máy có Windows .. sau đó quản lý để giải quyết vấn đề theo hai cách:

a) Đặt biến môi trường (ngay cả trong tùy chọn hệ thống Windows)

JAVA_TOOL_OPTIONS
-Dfile.encoding = UTF8

b) Giới thiệu đoạn mã sau cho tệp pom.xml của bạn:

 -Dfile.encoding=UTF-8 

TRONG

 <jvmArguments>
 -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8001
 -Dfile.encoding=UTF-8
 </jvmArguments>
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.