Ustawienie domyślnego kodowania znaków Java

362

Jak poprawnie ustawić domyślnie kodowanie znaków używane przez JVM (1.5.x) programowo?

Przeczytałem, że -Dfile.encoding=whateverkiedyś była to droga do starszych JVM. Nie mam tego luksusu z powodów, dla których nie chcę.

Próbowałem:

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

I właściwość zostaje ustawiona, ale nie wydaje się, aby powodowała, że ​​ostatnie getByteswywołanie poniżej używa 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
źródło
Doskonałe komentarze, chłopaki - i rzeczy, o których sam myślałem. Niestety istnieje podstawowe wywołanie String.getBytes (), nad którym nie mam kontroli. Obecnie jedynym sposobem obejścia tego problemu jest programowe ustawienie domyślnego kodowania. Jakieś inne sugestie?
6
może nie ma znaczenia pytanie, ale czy istnieje różnica, gdy UTF8 jest ustawiony na „UTF8”, „UTF-8” lub „utf8”. Ostatnio odkryłem, że kontenery IBM WAS 6.1 EJB i WEB inaczej traktują (na zasadzie rozróżniania wielkości liter) ciągi używane do definiowania kodowania.
igor.beslic
5
Tylko szczegół, ale: wolę UTF-8 od UTF8 (tylko ten pierwszy jest standardem). To nadal obowiązuje w 2012 roku ...
Christophe Roussy
4
Ustawienie lub odczyt file.encodingwłaściwości nie jest obsługiwane .
McDowell,
@erickson Nadal nie jestem jasne w zapytaniu. Czy nie jest prawdą, że „file.encoding” ma znaczenie, gdy używane są strumienie we / wy oparte na znakach (wszystkie podklasy class Reader& class Writer)? Ponieważ class FileInputStreamjest to strumień I / O oparty na bajtach, dlaczego więc należy przejmować się zestawem znaków w strumieniu I / O opartym na bajtach?
nadmierna wymiana

Odpowiedzi:

311

Niestety file.encodingwłaściwość należy określić podczas uruchamiania maszyny JVM; do czasu wprowadzenia głównej metody kodowania znaków używanego przez String.getBytes()domyślnych konstruktorów InputStreamReaderi na OutputStreamWriterstałe zapisywanych w pamięci podręcznej.

Jak zauważa Edward Grech, w takim szczególnym przypadku JAVA_TOOL_OPTIONS można użyć zmiennej środowiskowej, aby określić tę właściwość, ale zwykle robi się tak:

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

Charset.defaultCharset()będzie odzwierciedlać zmiany we file.encodingwłaściwości, ale większość kodu w podstawowych bibliotekach Java, które muszą określić domyślne kodowanie znaków, nie korzysta z tego mechanizmu.

Podczas kodowania lub dekodowania możesz zapytać o file.encodingwłaściwość lub Charset.defaultCharset()znaleźć bieżące domyślne kodowanie i użyć odpowiedniej metody lub przeciążenia konstruktora, aby je określić.

erickson
źródło
9
Dla kompletności chciałbym dodać, że przy odrobinie sztuczki można uzyskać dostęp do faktycznie używanego domyślnego kodowania (tak jak w pamięci podręcznej), dzięki Gary Cronin: byte [] byteArray = {'a'}; InputStream inputStream = new ByteArrayInputStream (byteArray); InputStreamReader reader = new InputStreamReader (inputStream); String defaultEncoding = reader.getEncoding (); lists.xcf.berkeley.edu/lists/advanced-java/1999-October/…
Stijn de Witt
2
JDK-4163515 zawiera więcej informacji na temat ustawiania file.encodingsysprop po uruchomieniu JVM.
Caspar
2
Drapałem się po głowie, bo to polecenie nie działało idealnie w systemach Windows, Linux i Mac ... a potem umieściłem „wokół takiej wartości: java -D” file.encoding = UTF-8 ”
-jar
sprawdź moją odpowiedź w przypadku Java Spring Boot: stackoverflow.com/a/48952844/986160
Michail Michailidis
170

Z dokumentacji interfejsu narzędzia JVM ™

Ponieważ nie zawsze można uzyskać dostęp do wiersza polecenia lub go zmodyfikować, na przykład we wbudowanych maszynach wirtualnych lub po prostu maszynach wirtualnych uruchamianych głęboko w skryptach, JAVA_TOOL_OPTIONSzapewniona jest zmienna umożliwiająca uruchomienie agentów w takich przypadkach.

Po ustawieniu zmiennej środowiskowej (Windows) JAVA_TOOL_OPTIONSna -Dfile.encoding=UTF8, właściwość (Java) Systembędzie ustawiana automatycznie przy każdym uruchomieniu JVM. Będziesz wiedział, że parametr został pobrany, ponieważ następujący komunikat zostanie wysłany do System.err:

Picked up JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF8

Edward Grech
źródło
Czy wiesz, że oświadczenie „Odebrano ...” zostanie wydrukowane w dziennikach Tomcat?
thatidiotguy,
1
Cześć Edward Grech Dziękuję za twoje rozwiązanie. Problem został rozwiązany w innym wpisie na forum. stackoverflow.com/questions/14814230/…
Smaug,
8
UTF8czy UTF-8?
Tiny
1
@Tiny Java rozumie oba. stackoverflow.com/questions/6031877/...
DLight
Twoje rozwiązanie pozwoliło mi zaoszczędzić czas, wielkie dzięki !!
Sobhan
67

Mam hacky sposób, który zdecydowanie działa !!

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

W ten sposób oszukasz JVM, który uznałby, że zestaw znaków nie jest ustawiony, i sprawi, że będzie ustawiony ponownie na UTF-8, w czasie wykonywania!

naskoos
źródło
2
NoSuchFieldException dla mnie
SparK
10
Aby hack zadziałał, musisz założyć, że menedżer bezpieczeństwa jest wyłączony. Jeśli nie masz sposobu, aby ustawić flagę JVM, możesz (prawdopodobnie) również mieć włączony system zarządzania bezpieczeństwem.
Yonatan
3
JDK9 nie akceptuje już tego hacka . 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: To nie jest dobra odpowiedź, to brudny hack i problem, który czeka. Powinno to być stosowane wyłącznie jako środek nadzwyczajny.
sleske,
1
@Enerccio: Można argumentować, czy Java „powinna” mieć sposób na ustawienie tego - można również argumentować, że programiści „powinni” jawnie określać kodowanie, gdy tylko jest to istotne. W każdym razie to rozwiązanie może powodować poważne problemy w dłuższej perspektywie, stąd zastrzeżenie „tylko do użytku awaryjnego”. Faktycznie, nawet użycie awaryjnego jest wątpliwa, ponieważ nie jest obsługiwany sposób to zrobić, ustawiając JAVA_TOOL_OPTIONS jak wyjaśniono w innej odpowiedzi.
sleske
38

Myślę, że lepszym podejściem niż ustawienie domyślnego zestawu znaków platformy, zwłaszcza że wydaje się, że masz ograniczenia w wpływie na wdrażanie aplikacji, nie mówiąc już o platformie, jest wywoływanie znacznie bezpieczniejszego String.getBytes("charsetName") . W ten sposób twoja aplikacja nie jest zależna od rzeczy poza jej kontrolą.

Osobiście uważam, że String.getBytes()powinno to być przestarzałe, ponieważ spowodowało to poważne problemy w wielu przypadkach, w których widziałem, w których deweloper nie uwzględnił domyślnego zestawu znaków, który może się zmienić.

Dow Wasserman
źródło
18

Nie mogę odpowiedzieć na twoje pierwotne pytanie, ale chciałbym zaoferować ci kilka rad - nie zależą od domyślnego kodowania JVM. Zawsze najlepiej jest wyraźnie określić pożądane kodowanie (tj. „UTF-8”) w kodzie. W ten sposób wiesz, że będzie działać nawet w różnych systemach i konfiguracjach JVM.

Marc Nowakowski
źródło
7
Z wyjątkiem, oczywiście, jeśli piszesz aplikację komputerową i przetwarzasz określony przez użytkownika tekst, który nie ma żadnych metadanych kodowania - wtedy domyślne kodowanie platformy najlepiej zgaduje, czego może używać użytkownik.
Michael Borgwardt,
@MichaelBorgwardt „to domyślne kodowanie platformy jest twoim najlepszym odgadnięciem” wydaje się, że radzisz, że zmiana domyślnego ustawienia nie jest dobrym pomysłem. Czy masz na myśli, jeśli to możliwe, używaj jawnego kodowania, używając dostarczonej usterki, gdy nic innego nie jest możliwe?
Raedwald
1
@ Raedwald: tak, o to mi chodziło. Domyślne kodowanie platformy to (przynajmniej na komputerze użytkownika końcowego) to, czego zwykle używają użytkownicy w ustawieniach regionalnych systemu. Są to informacje, których powinieneś użyć, jeśli nie masz lepszych (tj. Specyficznych dla dokumentu) informacji.
Michael Borgwardt,
1
@MichaelBorgwardt Nonsense. Użyj biblioteki, aby automatycznie wykryć kodowanie wejściowe i zapisz jako Unicode z BOM. To jedyny sposób na radzenie sobie i walkę z kodowaniem piekła.
Aleksandr Dubinsky
Myślę, że wasza dwójka nie jest na tej samej stronie. Michael mówi o dekodowaniu, a Raedwald mówi o przetwarzaniu po dekodowaniu.
WesternGun
12

Spróbuj tego :

    new OutputStreamWriter( new FileOutputStream("Your_file_fullpath" ),Charset.forName("UTF8"))
Emmanuel.B
źródło
5

Mieliśmy te same problemy. Metodycznie wypróbowaliśmy kilka sugestii z tego artykułu (i innych), ale bezskutecznie. Próbowaliśmy także dodać -Dfile.encoding=UTF8i nic nie działało.

Dla osób, które mają ten problem, następujący artykuł w końcu pomógł nam wyśledzić opisuje jak ustawienie regionalne mogą się złamać unicode/UTF-8wJava/Tomcat

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

Prawidłowe ustawienie regionalne w ~/.bashrcpliku działało dla nas.

D Bright
źródło
4

Próbowałem wielu rzeczy, ale przykładowy kod tutaj działa idealnie. Połączyć

Sedno kodu to:

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

Jeśli używasz Spring Boot i chcesz przekazać argument file.encodingw JVM, musisz go uruchomić w następujący sposób:

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

było nam to potrzebne, ponieważ korzystaliśmy z JTwigszablonów i system operacyjny miałANSI_X3.4-1968 to sprawdziłSystem.out.println(System.getProperty("file.encoding"));

Mam nadzieję, że to komuś pomoże!

Michail Michailidis
źródło
2

Używam elastycznej fasoli Amazon (AWS) i pomyślnie zmieniłem ją na UTF-8.

W Elastic Beanstalk przejdź do Konfiguracja> Oprogramowanie, „Właściwości środowiska”. Dodaj (nazwa) JAVA_TOOL_OPTIONS z (wartość) -Dfile.encoding = UTF8

Po zapisaniu środowisko uruchomi się ponownie z kodowaniem UTF-8.

Berend Menninga
źródło
1

Nie jest jasne, co robisz i nie masz nad tym kontroli. Jeśli możesz wstawić inną klasę OutputStream do pliku docelowego, możesz użyć podtypu OutputStream, który konwertuje ciągi znaków na bajty pod zdefiniowanym przez ciebie zestawem znaków, domyślnie UTF-8. Jeśli zmodyfikowany UTF-8 jest wystarczający dla twoich potrzeb, możesz użyć 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

Jeśli takie podejście nie jest wykonalne, może pomóc wyjaśnienie tutaj dokładnie tego, co możesz, a czego nie możesz kontrolować, pod względem przepływu danych i środowiska wykonywania (choć wiem, że czasem łatwiej to powiedzieć niż ustalić). Powodzenia.

Dow Wasserman
źródło
5
DataInputStream i DataOutputStream to klasy specjalnego przeznaczenia, których nigdy nie należy używać z plikami zwykłego tekstu. Zmodyfikowany UTF-8, którego używają, nie jest kompatybilny z prawdziwym UTF-8. Poza tym, jeśli OP mógłby użyć twojego rozwiązania, mógłby również użyć odpowiedniego narzędzia do tego zadania: OutputStreamWriter.
Alan Moore,
1
mvn clean install -Dfile.encoding=UTF-8 -Dmaven.repo.local=/path-to-m2

polecenie działało z wtyczką exec-maven w celu rozwiązania następującego błędu podczas konfigurowania zadania 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
źródło
0

Ustawiamy tam dwie właściwości systemu, dzięki czemu system bierze wszystko do utf8

file.encoding=UTF8
client.encoding.overrideUTF-8
Lizi
źródło
7
Wydaje się, że właściwość client.encoding.override jest specyficzna dla WebSphere.
Christophe Roussy,
0

Niedawno wpadłem na system Notes 6.5 lokalnej firmy i dowiedziałem się, że poczta internetowa wyświetla znaki, których nie można zidentyfikować podczas instalacji systemu Windows w środowisku innym niż Zhongwen. Kopałem przez kilka tygodni online, odkryłem to zaledwie kilka minut temu:

We właściwościach Java dodaj następujący ciąg do parametrów środowiska wykonawczego

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

W tym przypadku ustawienie UTF-8 nie działałoby.

midmaestro
źródło
0

Mój zespół napotkał ten sam problem na komputerach z systemem Windows .. następnie udało się go rozwiązać na dwa sposoby:

a) Ustaw zmienną środowiskową (nawet w preferencjach systemu Windows)

JAVA_TOOL_OPTIONS
-Dfile.encoding = UTF8

b) Wprowadź następujący fragment kodu do pliku pom.xml:

 -Dfile.encoding=UTF-8 

W CIĄGU

 <jvmArguments>
 -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=8001
 -Dfile.encoding=UTF-8
 </jvmArguments>
JacobTheKnitter
źródło