Problem znaków UTF-8 w Scali i Javie pod Mac OS-X

Opublikowane przez Jarosław Zabiełło Tue, 08 Sep 2009 01:15:00 GMT

Wczoraj, na polskim kanale IRC #scala.pl mieliśmy małą dyskusję na temat złego wyświetlania polskich znaków w Scali pod systemem Mac OS-X Leopard (pod Linuksem jest OK). Co dziwne, mimo, że zmienna środowiska LANG pokazuje wartość pl_PL.UTF-8, próby wyświetlenia polskich napisów nie działają. Więcej, w konsoli Scali w ogóle nie ma możliwości wprowadzenia polskich znaków, ani bezpośrednio z klawiatury, ani poprzez wklejenie ze schowka. Wszystkie polskie ogonki zamieniane są na znak zapytania.

     
println(java.nio.charset.Charset.defaultCharset) 
// x-MacCentralEurope

println("Zażółć gęślą jaźń")
// Za???? g??l? ja??

Jak podaje Mac Dev Center domyślnym kodowaniem znaków w Javie pod systemem Mac OS-X jest… MacRoman. (Tak jest oczywiśie tylko w wypadku angielskiego interfejsu. Po przełączeniu na polski, domyślnym kodowaniem znaków pod Javą staje się x-MacCentralEurope).

Najgorsze, że nie pomaga, pozornie oczywiste, użycie javowego API zmieniające własność file.encoding.

     
System.setProperty("file.encoding", "UTF8")
println(java.nio.charset.Charset.defaultCharset) 
// x-MacCentralEurope 

Dla sprawdzenie, czy problem ten dotyczy tylko Scali, skompilowałem testowy kod Scali do Javy i uruchomiłem go z jej poziomu.

     
// plik Znaki.scala
object Znaki extends Application {
  println(java.nio.charset.Charset.defaultCharset) 
  println("Zażółć gęślą jaźń")
  System.setProperty("file.encoding", "UTF8")
  println(java.nio.charset.Charset.defaultCharset) 
  val s = "Zażółć gęślą jaźń" 
  println(new String (s.getBytes(), "UTF-8"))    
}
$ scalac Znaki.scala
$ java -cp $SCALA_HOME/lib/scala-library.jar:. Znaki

x-MacCentralEurope
Za???? g??l? ja??
x-MacCentralEurope
Za? g??l? ja??

Po obejrzeniu pliku java/nio/charset/Charset.java wydawałby się że można problem rozwiązać za pomocą refleksji.

                                              
// plik java/nio/charset/Charset.java

private static volatile Charset defaultCharset;

public static Charset defaultCharset() {
    if (defaultCharset == null) {
        synchronized (Charset.class) {
            java.security.PrivilegedAction pa =
                new GetPropertyAction("file.encoding");
            String csn = (String)AccessController.doPrivileged(pa);
            Charset cs = lookup(csn);
            if (cs != null)
                defaultCharset = cs;
            else 
                defaultCharset = forName("UTF-8");
        }
    }
    return defaultCharset;
}

Niestety, mimo że za pomocą refleksji można zmienić wartość defaultCharset, nadal to nie rozwiązuje poprawnego wyświetlania polskich znaków.

import java.nio.charset._

println(Charset.defaultCharset) 
// x-MacCentralEurope 

val f = classOf[Charset].getDeclaredField("defaultCharset")
f.setAccessible(true)
// f.set(null, null)
// System.setProperty( "file.encoding", "utf-8")
// lub krócej:
f.set(null, Charset.forName("utf-8"))

println(Charset.defaultCharset) 
// UTF-8

println("Zażółć gęślą jaźń")
// Za???? g??l? ja?? 

Okazuje się, że jedynym, skutecznym i zarazem prostym rozwiązaniem tego problemu jest dodanie flagi -Dfile-encoding=utf8 do wywolania kodu Javy.

$ java -Dfile.encoding=utf8 -cp $SCALA_HOME/lib/scala-library.jar:. Znaki
UTF-8
Zażółć gęślą jaźń
UTF-8
Zażółć gęślą jaźń

W wypadku Scali, powyższą flagę dodajemy do skryptu shellowego scala. Przy okazji naprawi się wpisywanie i wyświetlanie polskich znaków w interaktywnej konsoli Scali.

Oczywiście powyższy problem ze znakami UTF-8 ostatecznie nie ma ze Scalą nic wspólnego. To wina takiego, a nie innego, zachowania się Javy w Mac OS-X. Scala “z rozpędu” ten problem tylko “odziedziczyła”, gdyż działa na bazie JVM.

Tagi , , , , ,  | 9 comments

Comments

  1. Avatar clondike powiedział about 5 hours later:

    Przeglądając newsy java/mac dochodzę do wniosku, że apple przestało się troszczyć javą na swoim systemie (sporo błędów, stare wydania, regresje). Zauważasz coś takiego?

  2. Avatar Red powiedział about 6 hours later:

    Bolesne, trochę mnie zdziwiło że Apple nie przeszło na UTF8, reszta świata powoli już zapomina o bólach związanych ze stronami kodowymi..

    Sprawdzałeś może czy problem dotyczy tylko wyjścia na terminal (println), czy też wszystkich operacji plikowych?

    Bo jeśli to tylko terminal to pół biedy.

  3. Avatar i2av powiedział about 8 hours later:

    Dzięki za wskazówki. -Dfile.encoding=utf-8 na moim macu nie poprawia sytuacji z konsolą. Za to wyświetla polskie znaczki ze skompilowanego kodu.

    Moim głównym problemem było tworzenie servletów j2ee z utf-8. Rozwiązaniem (oprócz -Dfile.encoding=utf-8, bo bez tego kompilator się wykrzacza) było wstawienie request.setCharacterEncoding(“UTF-8”)

  4. Avatar dmilith powiedział about 11 hours later:

    wystarczy dodać do ~/.zshrc (jak ktoś używa zsh) albo ~/.bashrc (jak bash’a), albo do /etc/profile linijkę:

    export JAVA_OPTS=”-Dfile.encoding=utf8”

    i każdy proces javy będzie odpalany z kodowaniem utf8 (po przelogowaniu w całym środowisku). Choć przyznam, że nie wiem dlaczego to ustawienie jest nierekomendowane przez Apple.

  5. Avatar i2av powiedział about 11 hours later:

    Dzięki dmilith – działa.

  6. Avatar jacek powiedział about 11 hours later:

    @clondike: Od kiedy pamietam, Apple nigdy nie troszczylo sie o Jave (vide ostatnia poprawka bezpieczenstwa, na ktora trzeba bylo czekac nie wiadomo ile).

    Na szczescie pod Lin/Win sytuacja wyglada inaczej :-)

  7. Avatar Jiima powiedział 1 day later:

    @JZ Ciekawe. file.property jest cechą wskazującą na “natywny” system kodowania znaków w OS. Na przykład na winzgrozie wynosi on Cp1250 (przy ustawionych polskich lokalach). Oznaczałoby to niestety że OSX posiada swoistą schizofrenię – sam pracuje w UTF-8, podczas gdy systemowa wartość encodingu ustawiona jest na własnościową stronę kodową Apple. Niewątpliwie jest to błąd w JVM, a dokładniej w jej OSX-owej “lokalizacji”.

    Co do Windows, schiza jest nieco większa wbrew niektórym komentarzom – shell cmd.exe w ogóle mało potrafi wyświetlić, zwłaszcza że nawet w Viście nie korzysta z Cp1250 o żadnym nowszym kodowaniu nie wspominając.

    BTW, obsługa lokali pod Lin’em poprawiła się dopiero niedawno. Jeszcze jakieś 2 lata temu pamiętam jakie były jazdy z konfigurowaniem JVM by pracowała na innych lokalach niż en-US…

  8. Avatar miłośnik_django powiedział 1 day later:

    No i wyszło tak zwane szydło z wora. Niby Mak lepszy, tańszy, a tu taka ‘niespodzianka’ – problemy z kodowaniem UTF8. Ciekawym jak działa na maku kodowanie prawdziwym unicodem 4.0 (ISO 10646)?

  9. Avatar Jiima powiedział 1 day later:

    @Miłośnik Problem nie tyle leży w Maku (nie wiem jakiego kodowania używa OSX, ale sądząc po shellu, jest to UCode), co w JVM dla Maka (jak widać, problem ten obejmuje również Windoze, tam ustawione jest niesławne Cp1250, chociaż Windoza od czasu NT4 używa wewnętrznie UCodu, za wyjątkiem legacy API). Co używa wewnętrznie “prawdziwego UCode”? bo Java chyba nie do końca… Jako miłośnik Django nie musisz się przejmować, Python chyba nie ma tych problemów (JZ jako pytonista już dawno by je wytknął), chyba że koniecznie chcesz odpalać coś pod Jythonem

(leave url/email »)

   Pomoc języka formatowania Obejrzyj komentarz