Eğitim Portalı/Java/Unicode ve Karakter Kodlama
Java01-java/87-unicode-ve-karakter-kodlama

Unicode ve Karakter Kodlama

Metin işlemenin altında, çoğu zaman görmezden geldiğimiz ama hata yaptığında baş ağrıtan bir katman vardır: karakter kodlama. "Türkçe karakterler neden bozuk çıktı?", "emoji'nin uzunluğu neden 2?", "bu dosya neden anl…

Unicode ve Karakter Kodlama

Metin işlemenin altında, çoğu zaman görmezden geldiğimiz ama hata yaptığında baş ağrıtan bir katman vardır: karakter kodlama. "Türkçe karakterler neden bozuk çıktı?", "emoji'nin uzunluğu neden 2?", "bu dosya neden anlaşılmıyor?" gibi soruların yanıtı buradadır. Bu konu, Unicode'un ne olduğunu, Java'nın karakterleri nasıl tuttuğunu ve metni byte'lara çevirirken neye dikkat etmen gerektiğini ele alır.

Unicode ve kod noktası (code point)

Unicode, dünyadaki her karaktere benzersiz bir sayı — kod noktası (code point) — atayan evrensel standarttır:

  • 'A' → U+0041, 'ç' → U+00E7, '€' → U+20AC, '😀' → U+1F600

Java'da \uXXXX kaçışıyla bir karakter kodla yazılabilir: 'ç''ç'.

char vs code point: kritik fark

Java'da char 16 bittir ve bir "kod birimini" (code unit) temsil eder:

  • BMP (U+0000–U+FFFF): Türkçe dahil çoğu karakter tek char'a sığar.
  • Astral (U+FFFF üstü): Emoji ve bazı semboller iki char ile temsil edilir (surrogate pair)!

Bunun çarpıcı sonucu:

"😀".length()                       // 2  (char/kod birimi sayısı — KARAKTER DEĞİL!)
"😀".codePointCount(0, 2)           // 1  (gerçek karakter sayısı)
"ab😀cd".length()                   // 6
"ab😀cd".codePoints().count()       // 5  (gerçek karakter)

Kural: String.length() karakter sayısı değil, char (kod birimi) sayısıdır. Gerçek karakter sayısı veya astral karakterlerle güvenli işlem için codePointCount / codePoints() kullan.

Örnek 1 (./Ornek1.java) bunu gösterir.

Karakter kodlama (charset): metin → byte

Karakterler bellekte kod noktasıdır; ama bir dosyaya yazılırken veya ağdan geçerken byte'lara kodlanmalıdır. Bu dönüşümü bir charset belirler:

  • UTF-8: Evrensel standart. ASCII ile uyumlu (ASCII karakterler 1 byte), Türkçe ~2 byte, emoji ~4 byte (değişken uzunluk). Web, dosya ve API'lerde varsayılan tercih.
  • ISO-8859-1 (Latin-1): Her karakter 1 byte ama yalnızca Batı Avrupa karakterlerini kapsar (Türkçe'nin bazı harflerini tam karşılamaz).
  • UTF-16: Java'nın char'ının iç temsili.
byte[] b = metin.getBytes(StandardCharsets.UTF_8);
String s = new String(b, StandardCharsets.UTF_8);   // aynı charset ile geri çöz

Mojibake: en sık yapılan hata

Bir metni bir kodlamayla yazıp başka bir kodlamayla okumak, "mojibake" denen bozuk karakterlere yol açar (ç, ı gibi):

byte[] utf8 = metin.getBytes(UTF_8);
new String(utf8, ISO_8859_1);   // YANLIŞ -> bozuk metin

Örnek 2 (./Ornek2.java) UTF-8/ISO-8859-1 farkını ve mojibake'yi canlı gösterir.

Altın kural: Kodlamayı her zaman açıkça belirt. getBytes(UTF_8), new String(bytes, UTF_8), Files.readString (varsayılan UTF-8). Platform varsayılan kodlamasına güvenme — Windows ve Linux'ta farklı olabilir, taşınabilirliği bozar. (Java 18'den beri varsayılan charset UTF-8 oldu, ama yine de açık belirtmek en güvenlisidir.)

Özet

Unicode'un her karaktere bir kod noktası atadığını; Java'da char'ın 16-bit kod birimi olduğunu ve astral karakterlerin (emoji) iki char ile temsil edildiğini — bu yüzden length()'in karakter sayısı olmadığını (Örnek 1); metni byte'lara çeviren charset'leri, UTF-8'in önemini ve mojibake hatasını (Örnek 2) öğrendik. "Kodlamayı her zaman açıkça belirt" kuralı, metin hatalarının çoğunu önler. Sırada, tarih ve zamanla çalışmak: Date & Time (java.time).

Kod Örnekleri(2)

Ornek1

çalıştırılabilir
Ornek1.java
1// Ornek1: Unicode — karakter kodlari, u-kacis dizileri ve "char" vs "code point" farki.
2// Çalıştırma: java Ornek1.java
3public class Ornek1 {
4
5    public static void main(String[] args) {
6        // char 16 bitlik bir Unicode kod birimidir. (uXXXX biçimli kaçışla kod yazılır.)
7        char a = 'A';
8        char turkceC = 'ç';  // 'ç' (U+00E7)
9        System.out.println("'A' kodu: " + (int) a + " (U+0041)");
10        System.out.println("\\u00E7 -> '" + turkceC + "' (ç)");
11        System.out.println("'\\u011F' -> '" + 'ğ' + "' (ğ)");
12
13        // String'de Türkçe karakterler tek char'a sığar (BMP içinde):
14        String s = "Çiğdem";
15        System.out.println("\n'" + s + "' -> length()=" + s.length() + " (her harf 1 char)");
16
17        // ASTRAL karakterler (emoji gibi, U+FFFF üstü) İKİ char ile temsil edilir (surrogate pair)!
18        String emoji = "😀";  // 😀 (U+1F600) — tek "kod noktası" ama 2 char
19        System.out.println("\nEmoji 😀:");
20        System.out.println("  length() (char sayısı)     : " + emoji.length() + " (surrogate pair -> 2!)");
21        System.out.println("  codePointCount (gerçek harf): " + emoji.codePointCount(0, emoji.length()));
22
23        // Kod noktalarını DOĞRU saymak/gezmek için codePoints() kullan (char değil):
24        String karisik = "ab😀cd"; // a b 😀 c d -> 5 kod noktası, 6 char
25        System.out.println("\n'ab😀cd':");
26        System.out.println("  length()       = " + karisik.length() + " (char)");
27        System.out.println("  kod noktası say = " + karisik.codePoints().count() + " (gerçek karakter)");
28
29        System.out.println("""
30
31                --- Unicode: char vs code point ---
32                Unicode her karaktere bir KOD NOKTASI (code point) atar: 'A'=U+0041, 'ç'=U+00E7, '😀'=U+1F600.
33                Java'da char 16 bittir; U+0000..U+FFFF (BMP) tek char'a sığar.
34                AMA U+FFFF ÜSTÜ (emoji, bazı semboller) İKİ char ile (surrogate pair) temsil edilir!
35                Bu yüzden string.length() KARAKTER sayısı DEĞİL, char (kod birimi) sayısıdır.
36                Gerçek karakter sayısı için codePointCount / codePoints() kullan. \\uXXXX ile kod yazılır.""");
37    }
38}
Çıktı yerel JDK 21 ile yakalandı — tarayıcıda JVM çalışmaz.

Ornek2

çalıştırılabilir
Ornek2.java
1// Ornek2: Karakter kodlama (charset) — UTF-8 vs diğerleri ve "mojibake" (bozuk metin).
2// Çalıştırma: java Ornek2.java
3import java.nio.charset.StandardCharsets;
4
5public class Ornek2 {
6
7    public static void main(String[] args) {
8        String metin = "Şçğ İÖ"; // Türkçe karakterler
9
10        // Bir String'i byte'lara çevirmek KODLAMA gerektirir; UTF-8 standarttır.
11        byte[] utf8 = metin.getBytes(StandardCharsets.UTF_8);
12        byte[] latin1 = metin.getBytes(StandardCharsets.ISO_8859_1); // Türkçe'yi tam karşılamaz
13
14        System.out.println("Metin: " + metin);
15        System.out.println("UTF-8 byte sayısı   : " + utf8.length + " (Türkçe harfler 2 byte)");
16        System.out.println("ISO-8859-1 byte sayısı: " + latin1.length + " (her harf 1 byte ama bilgi kaybı)");
17
18        // DOĞRU: aynı kodlamayla geri çöz -> orijinali al
19        String geriUtf8 = new String(utf8, StandardCharsets.UTF_8);
20        System.out.println("\nUTF-8 yaz + UTF-8 oku: '" + geriUtf8 + "' (doğru)");
21
22        // YANLIŞ: UTF-8 yazıp BAŞKA kodlamayla okumak -> MOJIBAKE (bozuk karakterler)
23        String bozuk = new String(utf8, StandardCharsets.ISO_8859_1);
24        System.out.println("UTF-8 yaz + ISO-8859-1 oku: '" + bozuk + "' (MOJIBAKE - bozuk!)");
25
26        // Sayısal kod (code point) -> karakter
27        System.out.println("\nU+20AC = '" + new String(Character.toChars(0x20AC)) + "' (€ avro işareti)");
28
29        System.out.println("""
30
31                --- Karakter kodlama (charset) ---
32                Karakterler bellekte kod noktasıdır; DOSYAYA/AĞA giderken byte'lara KODLANIR (charset).
33                UTF-8: evrensel standart. ASCII ile uyumlu; Türkçe/emoji dahil her şeyi değişken uzunlukta kodlar.
34                  (ASCII 1 byte, Türkçe ~2 byte, emoji ~4 byte.) Web ve dosyalarda VARSAYILAN tercih.
35                MOJIBAKE: bir kodlamayla yazıp BAŞKA kodlamayla okumak -> bozuk karakterler (ç gibi).
36                KURAL: her zaman kodlamayı AÇIKÇA belirt (getBytes(UTF_8), new String(bytes, UTF_8),
37                  Files.readString varsayılan UTF-8). Platform varsayılanına güvenme.""");
38    }
39}
Çıktı yerel JDK 21 ile yakalandı — tarayıcıda JVM çalışmaz.