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
charile 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çincodePointCount/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ılabilir1// 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}Ornek2
çalıştırılabilir1// 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}