Mengatur penyandian karakter Java default

362

Bagaimana cara mengatur pengkodean karakter default yang digunakan oleh JVM (1.5.x) secara terprogram?

Saya telah membaca bahwa -Dfile.encoding=whateverdulu cara untuk pergi untuk JVM yang lebih tua. Saya tidak memiliki kemewahan itu untuk alasan saya tidak mau masuk.

Saya telah mencoba:

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

Dan properti diatur, tetapi sepertinya tidak menyebabkan getBytespanggilan terakhir di bawah ini untuk menggunakan 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());
Willi Mentzel
sumber
Komentar yang sangat baik kawan - dan hal-hal yang sudah saya pikirkan sendiri Sayangnya ada panggilan String.getBytes () yang mendasarinya yang saya tidak punya kendali atas. Satu-satunya cara yang saya lihat saat ini untuk mengatasinya adalah dengan mengatur pengodean default secara terprogram. Ada saran lain?
6
mungkin pertanyaan yang tidak relevan tetapi, apakah ada perbedaan ketika UTF8 diatur dengan "UTF8", "UTF-8" atau "utf8". Baru-baru ini saya menemukan bahwa wadah IBM WAS 6.1 EJB dan WEB secara berbeda memperlakukan string (dengan sensitivitas case) yang digunakan untuk mendefinisikan penyandian.
igor.beslic
5
Hanya detail tetapi: lebih suka UTF-8 ke UTF8 (hanya yang pertama adalah standar). Ini masih berlaku pada tahun 2012 ...
Christophe Roussy
4
Pengaturan atau membaca file.encodingproperti tidak didukung .
McDowell
@erickson Masih belum jelas dengan kueri, Apakah tidak benar bahwa, "file.encoding" relevan ketika aliran I / O berbasis karakter digunakan (semua subclass dari class Reader& class Writer)? Karena class FileInputStreamstream I / O berbasis byte, jadi mengapa kita harus peduli dengan set karakter dalam stream I / O berbasis byte?
overexchange

Jawaban:

312

Sayangnya, file.encodingproperti harus ditentukan saat JVM dijalankan; pada saat metode utama Anda dimasukkan, pengkodean karakter yang digunakan oleh String.getBytes()dan konstruktor default dari InputStreamReaderdan OutputStreamWritertelah di-cache secara permanen.

Seperti yang ditunjukkan Edward Grech, dalam kasus khusus seperti ini, variabel lingkungan JAVA_TOOL_OPTIONS dapat digunakan untuk menentukan properti ini, tetapi biasanya dilakukan seperti ini:

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

Charset.defaultCharset()akan mencerminkan perubahan pada file.encodingproperti, tetapi sebagian besar kode di pustaka Java inti yang perlu menentukan pengkodean karakter default tidak menggunakan mekanisme ini.

Saat Anda menyandikan atau mendekode, Anda bisa menanyakan file.encodingproperti atau Charset.defaultCharset()menemukan encoding default saat ini, dan menggunakan metode yang sesuai atau overload konstruktor untuk menentukannya.

erickson
sumber
9
Untuk kelengkapan saya ingin menambahkan bahwa dengan sedikit tipu daya Anda bisa mendapatkan ke encoding default yang sebenarnya digunakan (seperti yang di-cache), terima kasih kepada Gary Cronin: byte [] byteArray = {'a'}; InputStream inputStream = ByteArrayInputStream (byteArray) baru; InputStreamReader reader = baru InputStreamReader (inputStream); String defaultEncoding = reader.getEncoding (); lists.xcf.berkeley.edu/lists/advanced-java/1999-October/…
Stijn de Witt
2
JDK-4163515 memiliki beberapa info lebih lanjut tentang pengaturan file.encodingsysprop setelah startup JVM.
Caspar
2
Saya menggaruk-garuk kepala saya karena perintah itu tidak berfungsi pada Windows, linux dan mac dengan sempurna ... lalu saya meletakkan "di sekitar nilai seperti ini: java -D" file.encoding = UTF-8 "-jar
cabaji99
periksa jawaban saya jika ada Java Spring Boot: stackoverflow.com/a/48952844/986160
Michail Michailidis
170

Dari dokumentasi JVM ™ Tool Interface ...

Karena baris perintah tidak selalu dapat diakses atau dimodifikasi, misalnya dalam VM tertanam atau hanya VM diluncurkan jauh di dalam skrip, JAVA_TOOL_OPTIONSvariabel disediakan sehingga agen dapat diluncurkan dalam kasus ini.

Dengan mengatur variabel lingkungan (Windows) JAVA_TOOL_OPTIONSke -Dfile.encoding=UTF8, properti (Java) Systemakan diatur secara otomatis setiap kali JVM dimulai. Anda akan tahu bahwa parameter telah diambil karena pesan berikut akan dikirim ke System.err:

Picked up JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF8

Edward Grech
sumber
Apakah Anda tahu bahwa pernyataan "Diambil ..." akan dicetak dalam log Tomcat?
thatidiotguy
1
Hai Edward Grech, terima kasih atas solusinya. Itu memecahkan masalah saya di posting forum lain. stackoverflow.com/questions/14814230/…
Smaug
8
UTF8atau UTF-8?
Tiny
1
@Tiny Java mengerti keduanya. stackoverflow.com/questions/6031877/…
DLight
Solusi Anda menghemat waktu saya, terima kasih banyak !!
Sobhan
67

Saya punya cara hacky yang pasti berhasil !!

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

Dengan cara ini Anda akan menipu JVM yang akan berpikir bahwa charset tidak disetel dan membuatnya untuk mengaturnya kembali ke UTF-8, saat runtime!

naskoos
sumber
2
NoSuchFieldException untuk saya
SparK
10
Agar peretasan berhasil, Anda harus menganggap manajer keamanan tidak aktif. Jika Anda tidak memiliki cara untuk mengatur flag JVM, Anda mungkin (mungkin) memiliki sistem yang mengaktifkan manajer keamanan juga.
Yonatan
3
JDK9 tidak menyetujui hack ini lagi. 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: Itu bukan jawaban yang bagus, itu hack kotor, dan masalah menunggu untuk terjadi. Itu seharusnya hanya digunakan sebagai tindakan darurat.
sleske
1
@Enerccio: Dapat diperdebatkan apakah Java "harus" memiliki cara untuk mengatur ini - orang juga bisa berpendapat bahwa pengembang "harus" secara eksplisit menentukan pengkodean kapan pun relevan. Bagaimanapun, solusi ini berpotensi menyebabkan masalah serius dalam jangka panjang, karenanya peringatan "hanya untuk penggunaan darurat". Sebenarnya, penggunaan darurat bahkan dipertanyakan, karena ada adalah cara didukung melakukannya, pengaturan JAVA_TOOL_OPTIONS seperti yang dijelaskan dalam jawaban lain.
sleske
38

Saya pikir pendekatan yang lebih baik daripada pengaturan set karakter default platform, terutama karena Anda tampaknya memiliki batasan untuk mempengaruhi penyebaran aplikasi, apalagi platform, adalah untuk memanggil yang jauh lebih aman String.getBytes("charsetName"). Dengan begitu aplikasi Anda tidak tergantung pada hal-hal di luar kendali.

Saya pribadi merasa itu String.getBytes()harus dihentikan, karena telah menyebabkan masalah serius dalam sejumlah kasus yang saya lihat, di mana pengembang tidak memperhitungkan charset default yang mungkin berubah.

Dov Wasserman
sumber
18

Saya tidak dapat menjawab pertanyaan awal Anda, tetapi saya ingin menawarkan beberapa saran - jangan bergantung pada penyandian standar JVM. Itu selalu terbaik untuk secara eksplisit menentukan pengkodean yang diinginkan (yaitu "UTF-8") dalam kode Anda. Dengan begitu, Anda tahu itu akan bekerja bahkan di berbagai sistem dan konfigurasi JVM.

Marc Novakowski
sumber
7
Kecuali, tentu saja, jika Anda menulis aplikasi desktop dan memproses beberapa teks yang ditentukan pengguna yang tidak memiliki metadata penyandian - maka penyandian default platform adalah tebakan terbaik Anda tentang apa yang mungkin digunakan pengguna.
Michael Borgwardt
@MichaelBorgwardt "maka pengkodean default platform adalah tebakan terbaik Anda" Anda tampaknya memberi saran bahwa ingin mengubah default bukanlah ide yang bagus. Maksud Anda, gunakan pengkodean eksplisit sedapat mungkin, menggunakan dafault yang disediakan saat tidak ada lagi yang mungkin?
Raedwald
1
@Redwald: ya, itulah yang saya maksud. Pengkodean platform default adalah (setidaknya pada mesin pengguna akhir) apa yang biasanya digunakan pengguna di lokal sistem. Itu adalah informasi yang harus Anda gunakan jika Anda tidak memiliki informasi yang lebih baik (khusus dokumen).
Michael Borgwardt
1
@MichaelBorgwardt Nonsense. Gunakan pustaka untuk mendeteksi secara otomatis penyandian input, dan simpan sebagai Unicode dengan BOM. Itu adalah satu-satunya cara untuk menangani dan melawan penyandian neraka.
Aleksandr Dubinsky
Saya pikir kalian berdua tidak di halaman yang sama. Michael berbicara tentang decoding sementara Raedwald Anda berbicara tentang pemrosesan setelah decoding.
WesternGun
12

Coba ini :

    new OutputStreamWriter( new FileOutputStream("Your_file_fullpath" ),Charset.forName("UTF8"))
Emmanuel.B
sumber
5

Kami mengalami masalah yang sama. Kami secara metodis mencoba beberapa saran dari artikel ini (dan lainnya) tanpa hasil. Kami juga mencoba menambahkan -Dfile.encoding=UTF8dan sepertinya tidak ada yang berhasil.

Bagi orang-orang yang mengalami masalah ini, artikel berikut akhirnya membantu kami melacak menjelaskan bagaimana pengaturan lokal dapat mematahkan unicode/UTF-8diJava/Tomcat

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

Mengatur lokal dengan benar dalam ~/.bashrcfile berhasil bagi kami.

D Cerah
sumber
4

Saya telah mencoba banyak hal, tetapi kode sampel di sini berfungsi dengan sempurna. Tautan

Inti dari kode ini adalah:

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

Jika Anda menggunakan Spring Boot dan ingin meneruskan argumen file.encodingdi JVM Anda harus menjalankannya seperti itu:

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

ini diperlukan bagi kami karena kami menggunakan JTwigtemplate dan sistem operasi ANSI_X3.4-1968yang kami temukanSystem.out.println(System.getProperty("file.encoding"));

Semoga ini bisa membantu seseorang!

Michail Michailidis
sumber
2

Saya menggunakan Amazon (AWS) Elastic Beanstalk dan berhasil mengubahnya menjadi UTF-8.

Di Elastic Beanstalk, buka Configuration> Software, "Properties properties". Tambahkan (nama) JAVA_TOOL_OPTIONS dengan (nilai) -Dfile.encoding = UTF8

Setelah menyimpan, lingkungan akan memulai kembali dengan pengkodean UTF-8.

Berend Menninga
sumber
1

Tidak jelas tentang apa yang Anda lakukan dan tidak memiliki kendali pada saat ini. Jika Anda dapat menempatkan kelas OutputStream yang berbeda pada file tujuan, Anda bisa menggunakan subtipe dari OutputStream yang mengubah Strings menjadi byte di bawah charset yang Anda tentukan, katakan UTF-8 secara default. Jika modifikasi UTF-8 memadai untuk kebutuhan Anda, Anda dapat menggunakan 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

Jika pendekatan ini tidak layak, mungkin membantu jika Anda mengklarifikasi di sini apa yang Anda bisa dan tidak bisa kendalikan dalam hal aliran data dan lingkungan eksekusi (meskipun saya tahu itu kadang-kadang lebih mudah diucapkan daripada ditentukan). Semoga berhasil.

Dov Wasserman
sumber
5
DataInputStream dan DataOutputStream adalah kelas tujuan khusus yang tidak boleh digunakan dengan file teks biasa. UTF-8 yang dimodifikasi yang mereka gunakan tidak kompatibel dengan UTF-8 yang asli. Selain itu, jika OP dapat menggunakan solusi Anda, ia juga dapat menggunakan alat yang tepat untuk pekerjaan ini: OutputStreamWriter.
Alan Moore
1
mvn clean install -Dfile.encoding=UTF-8 -Dmaven.repo.local=/path-to-m2

perintah bekerja dengan exec-maven-plugin untuk mengatasi kesalahan berikut saat mengkonfigurasi tugas 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)
prabushi samarakoon
sumber
0

Kami mengatur di sana dua sifat sistem bersama-sama dan itu membuat sistem mengambil semuanya menjadi utf8

file.encoding=UTF8
client.encoding.overrideUTF-8
lizi
sumber
7
Properti client.encoding.override tampaknya spesifik WebSphere.
Christophe Roussy
0

Baru-baru ini saya bertemu dengan sistem Catatan 6.5 perusahaan lokal dan menemukan bahwa webmail akan menunjukkan karakter yang tidak dapat diidentifikasi pada instalasi Windows lokal non-Zhongwen. Gali selama beberapa minggu online, temukan beberapa menit yang lalu:

Di properti Java, tambahkan string berikut ke Parameter Runtime

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

Pengaturan UTF-8 tidak akan berfungsi dalam kasus ini.

midmaestro
sumber
0

Tim saya mengalami masalah yang sama pada mesin dengan Windows .. kemudian berhasil menyelesaikannya dengan dua cara:

a) Tetapkan variabel lingkungan (bahkan dalam preferensi sistem Windows)

JAVA_TOOL_OPTIONS
-Dfile.encoding = UTF8

b) Perkenalkan cuplikan berikut ke pom.xml Anda:

 -Dfile.encoding=UTF-8 

DALAM

 <jvmArguments>
 -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8001
 -Dfile.encoding=UTF-8
 </jvmArguments>
JacobTheKnitter
sumber