dagblog.
← Archiv

Java — znakové kódování


Jedno z nejčastěji tázaných témat, v diskusních skupinách věnovaných Jave,

se točí kolem kódování znaků. Nejčastěji se to týká kódování při čtení/zápisu

servletu (JSP), databáze a souborů. Obecně to může generalizovat na operace

probíhající na hranici mezi JVM a okolním světem, kde dochází převodu bytové

reprezentace na znakovou reprezentaci a naopak. Mě osobně nejvíce

problematiku kódování v Jave osvětlil Makub v diskusní skupině

na serveru java.cz a tak se pokusím podělit.

Nemám problém protože všechno je Unicode

Výše uvedený nadpis si najdete v kdejakém pamfletu o Jave. Ano je to skutečně

tak, že Java pracuje vnitřně se všemi znaky jako s Unicode. Unicode přiřazuje každému znaku

jedinečné číslo a definuje tři formy, jak může být toto číslo reprezentováno

v byte, word nebo double word formě (8, 16, 32).

To že Java pracuje vnitřně s Unicode (používá UTF-16), je tak omlácenou

frází, že vede mnoho vývojářů k přesvědčení, že se nemusí o kódování starat.

Opak je pravdou. Při jakékoliv probíhající na hranici mezi JVM a okolním

světem, například čtení textového souboru nebo zápis do databáze, dochází k

převodu z vnitřní unicodovské reprezentace znaku na jeho bytovou reprezentaci

a naopak.

Vnější bytová reprezentace pak odpovídá zvolenému kódování. Pokud toto

kódování neurčíme, použije JVM defaultní kódování, které závisí na prostředí,

v němž běží. Právě při převodu znaku na bajtovou vnější reprezentace a naopak

dochází k problémům. Pokud se defaultní kódování JVM liší s kódováním, které

chceme na vstupu/výstupu a my toto kódování explicitně Jave neurčíme, dojde k

převodu na bytovou reprezentaci či naopak, podle defaultního kódování.

Klasickým příkladem je zpracování HTTP požadavku v servletu. Pokud začneme

číst parametry HTTP požadavku, před tím než zavoláme metodu

setCharacterEncoding, dojde k tomu, že servletový kontejner

použije defaultní kódování pro všechny parametry.

Jednoduchá rada, plynoucí z výše uvedeného, tedy zní:Explicitně

určujte kódování pro všechny vstupně/výstupní operace mimo JVM.

Problém této rady je v tom, že vývojáři si často neuvědomí co je mimo JVM.

Takže jedná se například o práci s databází, souborovým systémem nebo

zpracování HTTP požadavku.

Na závěr jsem si nechal pár praktických rad, které řeší 90% problémů.

Obecné — žádné texty ve zdrojových kódech

Vyčleňte všechny aplikační texty do resource bundle a ušetříte si s tím

plno problému.

Obecné — čtení/zápis textových zdrojů

Používejte třídy java.io.InputStreamReader a java.io.OutputStreamWriter.

Obecné — nastavte kódování pro kompiler

Pokud používáte ve zdrojovém kódu aplikační texty např. pro výjimky,

nezapomeňte určit kompileru kódování. Slouží pro to parametr

encoding.

Obecné — zkonvertujte properties resource bundle před použitím

Pokud používáte resource bundle založený na properties souboru, prožeňte 
jej, v rámci build procesu, přes utilitku native2ascii. Ta je

standardní součástí Java SDK.

Antovský task:

<target name="native2ascii"> 
    <native2ascii 
 encoding="utf-8" 
 src="${src.dir}" 
 dest="${web.dir}/WEB-INF/classes" 
 includes="**/*.properties" 
 ext=".properties"/> 
</target>
 
Server - nastavte kódování pro HTTP request/response
Před tím než přečtete první parametr a zapíšete první znak na výstup
nastavte pro oba požadované kódování. V případě HTTP požadavku se k tomu dá
elegantně využít servletový filtr. Filtrem zamezíte případným problémům,
které mohou nastat v případě, že si na parametry "šáhne" někdo před vámi,
např. validátor vstupních parametrů.

public class RequestEncodindFilter implements Filter{    
  public void doFilter(
  ServletRequest rq, 
  ServletResponse re, 
  FilterChain chain) 
    throws IOException, ServletException {
        rq.setCharacterEncoding("utf-8");//kodovani          
        chain.doFilter(rq,re);  

}

public void init(FilterConfig fc) throws ServletException {} public void destroy() {} }

web.xml


<filter>
<filter-name>RequestEncodindFilter</filter-name>

<filter-class>filterpackage.RequestEncodindFilter</filter-class> </filter>

<filter-mapping> <filter-name>RequestEncodindFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>

Pro JSP stránky používejte direktivu page a atribut
pageEncoding a contentType

<%@ page contentType=“text/html; charset=utf-8” pageEncoding=“utf-8” %>
Databáze - nastavte kódování pro JDBC ovladač
JTDS ovladače mají parametr, kterým určíte kódování pro práci s
CHAR/VARCHAR/TEXT typy.
Connection string MySQL JDBC ovladače:
jdbc:mysql://localhost/devdb1?useUnicode=true&characterEncoding=UTF-8

Verze na Medium · Archiv původní stránky

Související články

Související epizody CZ Podcastu