การตั้งค่าการเข้ารหัสอักขระ Java เริ่มต้น


362

ฉันจะตั้งค่าการเข้ารหัสอักขระเริ่มต้นที่ใช้โดย JVM (1.5.x) โดยทางโปรแกรมได้อย่างไร

ฉันเคยอ่านที่-Dfile.encoding=whateverเคยเป็นวิธีที่จะไปสำหรับ JVM เก่า ฉันไม่มีความหรูหราด้วยเหตุผลที่ฉันจะไม่เข้าไป

ฉันเหนื่อย:

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

และคุณสมบัติได้รับการตั้งค่า แต่ดูเหมือนจะไม่ทำให้การgetBytesโทรครั้งสุดท้ายด้านล่างใช้ 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());

ความคิดเห็นที่ยอดเยี่ยมพวก - สิ่งที่ฉันคิดอยู่แล้ว น่าเสียดายที่มีการเรียก String.getBytes () พื้นฐานที่ฉันไม่สามารถควบคุมได้ วิธีเดียวที่ฉันเห็นในขณะนี้คือการตั้งค่าการเข้ารหัสเริ่มต้นโดยทางโปรแกรม ข้อเสนอแนะอื่น ๆ ?

6
อาจเป็นคำถามที่ไม่เกี่ยวข้อง แต่มีความแตกต่างเมื่อตั้ง UTF8 ด้วย "UTF8", "UTF-8" หรือ "utf8" เมื่อเร็ว ๆ นี้ฉันพบว่าคอนเทนเนอร์ IBM WAS 6.1 EJB และ WEB แตกต่างกันปฏิบัติสตริง (ในกรณีที่ไวต่อตัวอักษร) ที่ใช้ในการกำหนดการเข้ารหัส
igor.beslic

5
เพียงรายละเอียด แต่: เลือก UTF-8 ถึง UTF8 (เฉพาะรุ่นก่อนคือมาตรฐาน) ยังคงมีผลบังคับใช้ในปี 2012 ...
Christophe Roussy

4
การตั้งค่าหรืออ่านfile.encodingคุณสมบัติได้รับการสนับสนุน
McDowell

@erickson ฉันยังไม่ชัดเจนกับข้อความค้นหาใช่หรือไม่ว่า "file.encoding" มีความเกี่ยวข้องเมื่อมีการใช้ I / O แบบสตรีมตามอักขระ (subclasses ทั้งหมดของclass Reader& class Writer)? เพราะclass FileInputStreamเป็นสตรีม I / O ที่ใช้ไบต์ดังนั้นทำไมจึงควรใส่ใจกับชุดอักขระในสตรีม I / O ที่อิงไบต์
แลกเปลี่ยนที่มากเกินไป

คำตอบ:


311

น่าเสียดายที่ file.encodingต้องระบุคุณสมบัติเป็น JVM เริ่มต้นขึ้น ตามเวลาที่คุณป้อนวิธีการหลักการเข้ารหัสอักขระที่ใช้โดยString.getBytes()และตัวสร้างเริ่มต้นของInputStreamReaderและOutputStreamWriterถูกแคชถาวรอย่างถาวร

ขณะที่เอ็ดเวิร์ด Grech ชี้ให้เห็นในกรณีพิเศษเช่นนี้ตัวแปรสภาพแวดล้อมJAVA_TOOL_OPTIONS สามารถนำมาใช้เพื่อระบุคุณสมบัตินี้ แต่ก็ทำตามปกติเช่นนี้

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

Charset.defaultCharset()จะแสดงถึงการเปลี่ยนแปลงfile.encodingคุณสมบัติ แต่รหัสส่วนใหญ่ในไลบรารี Java หลักที่จำเป็นต้องพิจารณาการเข้ารหัสอักขระเริ่มต้นไม่ใช้กลไกนี้

เมื่อคุณกำลังเข้ารหัสหรือถอดรหัสคุณสามารถสอบถามfile.encodingคุณสมบัติหรือCharset.defaultCharset()ค้นหาการเข้ารหัสเริ่มต้นปัจจุบันและใช้วิธีการที่เหมาะสมหรือตัวสร้างเกินพิกัดเพื่อระบุ


9
เพื่อความสมบูรณ์ฉันอยากจะเพิ่มสิ่งนั้นด้วยเล่ห์เหลี่ยมเล็กน้อยคุณสามารถใช้การเข้ารหัสเริ่มต้นที่ใช้จริง (เช่นแคช) ขอบคุณ Gary Cronin: byte [] byteArray = {'a'}; InputStream inputStream = ใหม่ ByteArrayInputStream (byteArray); InputStreamReader reader = new InputStreamReader (inputStream); String defaultEncoding = reader.getEncoding (); lists.xcf.berkeley.edu/lists/advanced-java/1999-October/...
Stijn เดอวิตต์

2
JDK-4163515มีข้อมูลเพิ่มเติมเกี่ยวกับการตั้งค่าfile.encodingsysprop หลังจากการเริ่มต้น JVM
Caspar

2
ฉันเกาหัวของฉันทำให้คำสั่งนั้นไม่ทำงานบน Windows, linux และ mac อย่างสมบูรณ์ ... จากนั้นฉันใส่ "รอบค่าเช่นนี้: java -D" file.encoding = UTF-8 "-jar
cabaji99

ตรวจสอบคำตอบของฉันในกรณีของ Java Spring Boot: stackoverflow.com/a/48952844/986160
Michail Michailidis

170

จากเอกสารอินเตอร์เฟสเครื่องมือ JVM ™ ...

เนื่องจากบรรทัดคำสั่งไม่สามารถเข้าถึงหรือแก้ไขได้เสมอตัวอย่างเช่นใน VMs ฝังตัวหรือเพียงแค่เปิดตัว VMs ที่อยู่ลึกเข้าไปในสคริปต์จึงมีการJAVA_TOOL_OPTIONSจัดเตรียมตัวแปรเพื่อให้ตัวแทนอาจเปิดใช้งานในกรณีเหล่านี้

โดยการตั้งค่า (Windows) ตัวแปรสภาพแวดล้อมJAVA_TOOL_OPTIONSเพื่อ-Dfile.encoding=UTF8การ (Java) Systemคุณสมบัติที่จะได้รับการตั้งค่าโดยอัตโนมัติทุกครั้งที่มี JVM จะเริ่มต้น คุณจะรู้ว่าพารามิเตอร์ถูกหยิบขึ้นมาเนื่องจากข้อความต่อไปนี้จะถูกโพสต์ไปที่System.err:

Picked up JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF8


คุณรู้หรือไม่ว่าคำสั่ง "Picked up ... " จะถูกพิมพ์ในบันทึก Tomcat?
thatidiotguy

1
สวัสดี Edward Grech ฉันขอขอบคุณสำหรับวิธีการแก้ปัญหาของคุณ มันแก้ไขปัญหาของฉันในโพสต์ฟอรั่มอื่น stackoverflow.com/questions/14814230/…
ม็อก

8
UTF8หรือUTF-8?
เล็ก ๆ

1
@Tiny Java เข้าใจทั้งสองอย่าง stackoverflow.com/questions/6031877/…
DLight

ทางออกของคุณช่วยประหยัดเวลาของฉันขอบคุณมาก !!
Sobhan

67

ฉันมีวิธีแฮ็คที่ใช้งานได้อย่างแน่นอน !!

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

วิธีนี้คุณจะใช้เล่ห์เหลี่ยม JVM ซึ่งจะคิดว่า charset ไม่ได้ถูกตั้งค่าและทำให้มันตั้งค่าอีกครั้งเป็น UTF-8 บนรันไทม์!


2
NoSuchFieldException สำหรับฉัน
SparK

10
เพื่อให้แฮ็คทำงานคุณต้องถือว่าตัวจัดการความปลอดภัยปิดอยู่ หากคุณไม่มีวิธีตั้งค่าธง JVM คุณอาจ (อาจ) มีระบบที่เปิดใช้งานตัวจัดการความปลอดภัยด้วยเช่นกัน
Yonatan

3
JDK9 ไม่อนุมัติแฮ็คนี้อีกต่อไป 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
@Eccccio: นั่นไม่ใช่คำตอบที่ดีนั่นเป็นแฮ็คสกปรกและมีปัญหาที่จะเกิดขึ้น ควรใช้เป็นมาตรการฉุกเฉินเท่านั้น
sleske

1
@Enerccio: เป็นที่โต้แย้งได้ว่า Java "ควร" มีวิธีการตั้งค่านี้ - หนึ่งยังสามารถยืนยันได้ว่านักพัฒนา "ควร" ระบุการเข้ารหัสอย่างชัดเจนทุกครั้งที่เกี่ยวข้อง ไม่ว่าจะด้วยวิธีใดก็ตามโซลูชันนี้มีศักยภาพที่จะก่อให้เกิดปัญหาร้ายแรงในระยะยาวดังนั้นคำเตือน "สำหรับใช้ในกรณีฉุกเฉินเท่านั้น" ที่จริงใช้ในกรณีฉุกเฉินแม้จะเป็นที่น่าสงสัยเพราะมีเป็นวิธีที่ได้รับการสนับสนุนในการดำเนินการ, การตั้งค่า JAVA_TOOL_OPTIONS ที่อธิบายไว้ในคำตอบอื่น
sleske

38

ผมคิดว่าวิธีการที่ดีกว่าการตั้งค่าชุดอักขระเริ่มต้นของแพลตฟอร์มโดยเฉพาะอย่างยิ่งในขณะที่คุณดูเหมือนจะมีข้อ จำกัด String.getBytes("charsetName")ในการส่งผลกระทบต่อการใช้งานโปรแกรมให้อยู่คนเดียวแพลตฟอร์มคือการเรียกปลอดภัยมาก วิธีนี้ทำให้แอปพลิเคชันของคุณไม่ได้ขึ้นอยู่กับสิ่งที่อยู่นอกเหนือการควบคุม

ฉันรู้สึกเป็นส่วนตัวว่าString.getBytes()ควรเลิกใช้เนื่องจากมันก่อให้เกิดปัญหาร้ายแรงในหลายกรณีที่ฉันเห็นซึ่งผู้พัฒนาไม่ได้พิจารณาว่าชุดอักขระเริ่มต้นอาจเปลี่ยนแปลงไป


18

ฉันไม่สามารถตอบคำถามเดิมของคุณได้ แต่ฉันต้องการที่จะให้คำแนะนำกับคุณ - ไม่ขึ้นอยู่กับการเข้ารหัสเริ่มต้นของ JVM เป็นการดีที่สุดที่จะระบุการเข้ารหัสที่ต้องการ (เช่น "UTF-8") ในรหัสของคุณอย่างชัดเจน ด้วยวิธีนี้คุณจะรู้ว่ามันจะทำงานได้แม้ในระบบที่แตกต่างกันและการกำหนดค่า JVM


7
ยกเว้นแน่นอนถ้าคุณกำลังเขียนแอปเดสก์ท็อปและประมวลผลข้อความที่ผู้ใช้ระบุซึ่งไม่มีข้อมูลเมตาเข้ารหัสใด ๆ การเข้ารหัสเริ่มต้นของแพลตฟอร์มคือการคาดเดาที่ดีที่สุดของคุณเกี่ยวกับสิ่งที่ผู้ใช้อาจใช้
Michael Borgwardt

@MichaelBorgwardt "แล้วการเข้ารหัสค่าเริ่มต้นของแพลตฟอร์มคือการคาดเดาที่ดีที่สุด" คุณดูเหมือนจะให้คำแนะนำว่าการเปลี่ยนค่าเริ่มต้นนั้นไม่ใช่ความคิดที่ดี คุณหมายถึงให้ใช้การเข้ารหัสอย่างชัดเจนทุกที่ที่เป็นไปได้โดยใช้ดาฟอลที่ให้มาเมื่อไม่มีสิ่งใดเป็นไปได้?
Raedwald

1
@ Raedwald: ใช่นั่นคือสิ่งที่ฉันหมายถึง การเข้ารหัสเริ่มต้นของแพลตฟอร์มคือ (อย่างน้อยบนเครื่องผู้ใช้ปลายทาง) สิ่งที่ผู้ใช้ในระบบที่ตั้งค่าไว้โดยทั่วไปจะใช้ นั่นคือข้อมูลที่คุณควรใช้หากคุณไม่มีข้อมูลที่ดีกว่า (เช่นเอกสารเฉพาะ)
Michael Borgwardt

1
@MichaelBorgwardt เรื่องไร้สาระ ใช้ไลบรารีเพื่อตรวจจับการเข้ารหัสอินพุตโดยอัตโนมัติและบันทึกเป็น Unicode ด้วย BOM นั่นเป็นวิธีเดียวที่จะรับมือและต่อสู้กับการเข้ารหัสนรก
Aleksandr Dubinsky

ฉันคิดว่าคุณสองคนไม่ได้อยู่ในหน้าเดียวกัน Michael พูดเกี่ยวกับการถอดรหัสขณะที่ Raedwald คุณพูดถึงการประมวลผลหลังจากถอดรหัส
WesternGun


5

เรากำลังมีปัญหาเดียวกัน เราได้ลองหลายข้อเสนอแนะจากบทความนี้ (และอื่น ๆ ) อย่างเป็นระบบโดยไม่มีประโยชน์ เราพยายามเพิ่ม-Dfile.encoding=UTF8และดูเหมือนว่าจะไม่มีอะไรทำงาน

สำหรับคนที่กำลังมีปัญหานี้บทความต่อไปนี้ในที่สุดก็ช่วยให้เราติดตามอธิบายวิธีการตั้งค่าสถานที่เกิดเหตุสามารถทำลายunicode/UTF-8ในJava/Tomcat

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

การตั้งค่าภาษาให้ถูกต้องใน~/.bashrcไฟล์นั้นใช้ได้สำหรับเรา


4

ฉันลองมาหลายอย่างแล้ว แต่โค้ดตัวอย่างที่นี่ใช้ได้ดี ลิงค์

crux ของรหัสคือ:

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

4

ในกรณีที่คุณใช้ Spring Boot และต้องการผ่านการโต้แย้งfile.encodingใน JVM คุณต้องเรียกใช้เช่นนั้น:

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

สิ่งนี้จำเป็นสำหรับเราเนื่องจากเราใช้JTwigเทมเพลตและระบบปฏิบัติการก็มีANSI_X3.4-1968สิ่งที่เราค้นพบSystem.out.println(System.getProperty("file.encoding"));

หวังว่านี่จะช่วยใครซักคน!


2

ฉันกำลังใช้ Elastic Beanstalk ของ Amazon (AWS) และเปลี่ยนเป็น UTF-8 ได้สำเร็จ

ใน Elastic Beanstalk ไปที่การกำหนดค่า> ซอฟต์แวร์ "คุณสมบัติของสภาพแวดล้อม" เพิ่ม (ชื่อ) JAVA_TOOL_OPTIONS ด้วย (ค่า) -Dfile.encoding = UTF8

หลังจากบันทึกสภาพแวดล้อมจะเริ่มต้นใหม่ด้วยการเข้ารหัส UTF-8


1

ไม่ชัดเจนในสิ่งที่คุณทำและไม่สามารถควบคุมได้ในจุดนี้ หากคุณสามารถแทรกแซงคลาส OutputStream ที่แตกต่างกันในไฟล์ปลายทางคุณสามารถใช้ประเภทย่อยของ OutputStream ซึ่งจะแปลง Strings เป็นไบต์ภายใต้ชุดอักขระที่คุณกำหนดให้พูด UTF-8 ตามค่าเริ่มต้น หากการดัดแปลง UTF-8 นั้นไม่เพียงพอสำหรับความต้องการของคุณคุณสามารถใช้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

หากวิธีนี้ไม่เป็นไปได้อาจช่วยได้ถ้าคุณชี้แจงให้ชัดเจนว่าคุณสามารถและไม่สามารถควบคุมในแง่ของการไหลของข้อมูลและสภาพแวดล้อมการดำเนินการได้ (แม้ว่าฉันรู้ว่าบางครั้งพูดง่ายกว่าที่กำหนด) โชคดี.


5
DataInputStream และ DataOutputStream เป็นคลาสวัตถุประสงค์พิเศษที่ไม่ควรใช้กับไฟล์ข้อความธรรมดา UTF-8 ที่ได้รับการดัดแปลงที่ใช้ไม่เข้ากันได้กับ UTF-8 ที่แท้จริง นอกจากนี้หาก OP สามารถใช้โซลูชันของคุณเขาสามารถใช้เครื่องมือที่เหมาะสมสำหรับงานนี้: OutputStreamWriter
อลันมัวร์

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

คำสั่งทำงานร่วมกับ exec-maven-plugin เพื่อแก้ไขข้อผิดพลาดต่อไปนี้ในขณะที่การกำหนดค่างานเจนกินส์

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

เราตั้งค่าคุณสมบัติของระบบไว้สองอย่างด้วยกันและทำให้ระบบนำทุกอย่างเข้าสู่ utf8

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

7
คุณสมบัติ client.encoding.override น่าจะเป็น WebSphere ที่เจาะจง
Christophe Roussy

0

การติดตาม @Caspar ความคิดเห็นเกี่ยวกับคำตอบที่ยอมรับวิธีที่แนะนำในการแก้ไขปัญหานี้ตาม Sun คือ:

"เปลี่ยนโลแคลของแพลตฟอร์มพื้นฐานก่อนเริ่มโปรแกรม Java ของคุณ"

http://bugs.java.com/view_bug.do?bug_id=4163515

สำหรับนักเทียบท่าดู:

http://jaredmarkell.com/docker-and-locales/


0

เมื่อเร็ว ๆ นี้ฉันชนเข้ากับระบบ Notes 6.5 ของ บริษัท ท้องถิ่นและพบว่าเว็บเมลจะแสดงอักขระที่ไม่สามารถระบุตัวตนได้บนการติดตั้ง Windows ที่ไม่ใช่ Zhongwen มีการขุดเป็นเวลาหลายสัปดาห์ออนไลน์คิดออกเพียงไม่กี่นาทีที่ผ่านมา:

ในคุณสมบัติ Java ให้เพิ่มสตริงต่อไปนี้ในพารามิเตอร์รันไทม์

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

การตั้งค่า UTF-8 จะไม่ทำงานในกรณีนี้


0

ทีมของฉันพบปัญหาเดียวกันในเครื่องที่ใช้ Windows .. จากนั้นจัดการเพื่อแก้ไขปัญหาได้สองวิธี:

a) ตั้งค่าตัวแปรสภาพแวดล้อม (แม้ในการตั้งค่าระบบ Windows)

JAVA_TOOL_OPTIONS
-Dfile.encoding = UTF8

b) แนะนำข้อมูลโค้ดต่อไปนี้ให้ pom.xml ของคุณ:

 -Dfile.encoding=UTF-8 

ภายใน

 <jvmArguments>
 -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8001
 -Dfile.encoding=UTF-8
 </jvmArguments>
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.