Forum: D Programlama Dili RSS
string'i Unicode dizgisi olarak kullanabilmek
acehreli (Moderatör) #1
Kullanıcı başlığı: Ali Çehreli
Üye Haz 2009 tarihinden beri · 4527 mesaj
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
Konu adı: string'i Unicode dizgisi olarak kullanabilmek
char türünün UTF-8 kod birimi (code unit) olduğunu biliyorsunuz. Kod birimi, Unicode karakterleri oluşturan birim anlamına geliyor. Örneğin 'ğ' harfi UTF-8'de iki kod biriminden oluşur. (Kod noktası (code point) ise karakterin kendisini ifade eden koddur.)

Code point (kod noktası): Unicode'un her bir dünya karakterine atadığı kod. Örneğin ğ'nin kod noktası Unicode gösteriminde U+011E'dir. O değeri on altılı düzenden çevirirsek, 286'dır.

Code unit (kod birimi): Her bir karakterin, belirli bir Unicode kodlamasındaki bir birimdir. Örneğin ğ harfi UTF-8 kodlamasında iki adet 8-bit olarak temsil edilir: onaltılı olarak c4 ve 9f (onlu düzende 196 ve 159). İşte bunların her birisi kod birimidir.

string'in elemanlarına kendimiz teker teker erişirsek, kod birimlerini görürüz:

import std.stdio;
 
void main()
{
    writeln("\nUnicode kod birimi (code unit) olarak:");
    foreach (kodBirimi; "ağb"c) {
        writeln(kodBirimi);
    }
}

Çıktısı, ğ'yi iki kod birimi olarak gösterir:

Unicode kod birimi (code unit) olarak:
a
?
?
b

(Bu forumda yanlış bir şeyler görünmesin diye ben elle "?" yazdım.)

Oysa, string'i bir aralık olarak kullanırsak, kod noktalarına, yani Unicode karakterlerine erişiriz:

import std.stdio;
import std.range;
 
void main()
{
    writeln("\nUnicode karakteri (code point) olarak:");
    for (string dizgi = "ağb"c; !dizgi.empty; dizgi.popFront()) {
        writeln(dizgi.front);
    }
}

Çıktısı:

Unicode karakteri (code point) olarak:
a
ğ
b

Yaşasın aralıklaaar! :D

Ama daha foreach ile kullanmayı bilmiyorum. Örneğin ters sırada ilerlemek için retro kullanıldığında foreach ile güzelce çalışıyor:

import std.stdio;
import std.range;
 
void main()
{
    writeln("\nUnicode karakteri (code point) olarak ters sırada:");
    foreach (karakter; retro("ağb"c)) {
        writeln(karakter);
    }
}

UTF-8 kodlamasını akıllıca geriye doğru ilerliyor ve beklenen çıktıyı oluşturuyor:

Unicode karakteri (code point) olarak ters sırada:
b
ğ
a

foreach ile düz ilerlemeyi de öğrenmeliyiz... :)

Ali
acehreli (Moderatör) #2
Kullanıcı başlığı: Ali Çehreli
Üye Haz 2009 tarihinden beri · 4527 mesaj
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
acehreli:
foreach ile düz ilerlemeyi de öğrenmeliyiz

Çözüldü! :)

std.string içindeki ByCodeUnit yapı şablonundan esinlendim ve KarakterOlarak yapı şablonunu yazdım. Kolay kullanılabilmesi için de karakterOlarak işlev şablonunu yazdım.

Artık bütün dizgi türlerini foreach içinde gerçek Unicode karakterler olarak ilerleyebiliriz:

import std.stdio;
import std.range;
import std.string;
 
/*
 * Dizgileri Unicode karakteri olarak ilerlemek istediğimizde
 * kullanabileceğimiz yardımcı bir yapı.
 *
 * Bu tür yapılara adaptör (adapter) denir. Kendisi bir dizgi alır, ve o
 * dizgiyi UTF kodlamasına uygun adımlar atan bir aralık olarak tekrar
 * sunar.
 *
 * Burada yalnızca "ilerleyen" bir aralık sunuyoruz. Bunun ters sırada
 * ilerlemesi de aynı kolaylıkta sağlanabilir.
 */
struct KarakterOlarak(DizgiTürü)
{
    DizgiTürü dizgi;
    dchar baştaki;
 
public:
 
    this(DizgiTürü dizgi)
    {
        this.dizgi = dizgi;
 
        /*
         * Bu 'hevesli' bir yapı. Daha ihtiyaç olduğundan emin olunmadan
         * 'baştaki'ni hazırlıyor.
         */
        if (!dizgi.empty) {
            popFront();
        }
    }
 
    /*
     * İlerleme olanağı sunan aralıklar üç işlev tanımlamak zorundadırlar:
     *
     * - boş olup olmadığını bildiren empty
     * - baştakine eriştiren front
     * - baştan eksilten popFront
     */
 
    bool empty() const
    {
        return (baştaki == baştaki.init) && dizgi.empty;
    }
 
    dchar front() const
    in
    {
        assert(!empty);
    }
    body
    {
        return baştaki;
    }
 
    void popFront()
    in
    {
        assert(!empty);
    }
    body
    {
        if (dizgi.empty) {
            /* Dizgi tükenmiş; elimizde tuttuğumuz 'baştaki'ni silmek gerek */
            baştaki = baştaki.init;
 
        } else {
            /* std.utf.decode, UTF kodlamalarından anlayan ve belirli bir
             * indeksteki Unicode karakterini döndüren bir işlevdir. Ek
             * olarak, referans olarak aldığı indeks'i, kodlamanın
             * gerektirdiği kadar ilerletir.
             */
            size_t indeks = 0;
            baştaki = std.utf.decode(dizgi, indeks);
 
            /*
             * Bu karakterin kaç elemandan oluştuğunu decode'dan öğrendik;
             * dizgiyi başından eksilterek bir sonraki karaktere
             * hazırlanıyoruz.
             */
            dizgi = dizgi[indeks .. $];
        }
    }
}
 
/*
 * Bu, C++'da da çok kullanılan bir kolaylık işlevidir. Asıl işi yapan,
 * yukarıdaki KarakterOlarak yapı şablonudur.
 *
 * Yapı şablonlarının şablon parametreleri otomatik olarak çıkarsanamazlar,
 * ama işlev şablonlarının şablon parametreleri otomatik olarak
 * çıkarsanabilirler.
 *
 * Bu yöntem, kendisine verilen dizgi ile bir yapı oluşturan bir işlevdir.
 * Böylece şablon parametresi otomatik olarak çıkarsanabiliyor.
 *
 * Normalde örneğin
 *
 *     KarakterOlarak!string("abc")
 *
 * yazmamız gerekir (büyük K ile). Onun yerine
 *
 *     karakterOlarak("abc")
 *
 * yazarak bu işlevi çağırıyoruz; o, bizim yerimize
 * KarakterOlarak!string("abc") döndürüyor.
 */
KarakterOlarak!DizgiTürü karakterOlarak(DizgiTürü)(DizgiTürü dizgi)
{
    /*
     * Bunu yeni öğrendim. typeof(return), "dönüş türü" demekmiş. Daha uzun
     * olarak şunu da yazabilirdim:
     *
     *     return KarakterOlarak!DizgiTürü(dizgi);
     */
    return typeof(return)(dizgi);
}
 
unittest
{
    auto kc = karakterOlarak("ağ\U00000e95"c);
    kc.popFront();
    assert(kc.front == 'ğ');
    kc.popFront();
    assert(kc.front == '\U00000e95');
 
    auto kw = karakterOlarak("ağ\U00000e95"w);
    kw.popFront();
    assert(kw.front == 'ğ');
    kw.popFront();
    assert(kw.front == '\U00000e95');
 
    auto kd = karakterOlarak("ağ\U00000e95"d);
    kd.popFront();
    assert(kd.front == 'ğ');
    kd.popFront();
    assert(kd.front == '\U00000e95');
}
 
void main()
{
    writeln("\nUnicode karakteri (code point) olarak:");
    foreach (karakter; karakterOlarak("ağb"c)) {
        writeln(karakter);
    }
}

Çıktısı:

Unicode karakteri (code point) olarak:
a
ğ
b

Ali
acehreli (Moderatör) #3
Kullanıcı başlığı: Ali Çehreli
Üye Haz 2009 tarihinden beri · 4527 mesaj
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
acehreli:
* Bu tür yapılara adaptör (adapter) denir

O söylediğim doğru değil. Adaptör, bir arayüzü başka arayüze uydururlar ("adapt" İngilizce'de uyumlu hale getirmek demek.) Bu durumda ise string'in ve KarakterOlarak'ın arayüzleri aynı; ikisi de aralık işlevlerini sunuyorlar.

KarakterOlarak aslında bir 'proxy'. "Proxy" vekil demektir. KarakterOlarak, kendisine verilen string'in vekilliğini yapıyor. Yine ona eriştiriyor ama biraz değişik olarak. (Kod birimi olarak değil, kod noktası olarak eriştiriyor.)

Ali
Doğrulama Kodu: VeriCode Lütfen resimde gördüğünüz doğrulama kodunu girin:
İfadeler: :-) ;-) :-D :-p :blush: :cool: :rolleyes: :huh: :-/ <_< :-( :'( :#: :scared: 8-( :nuts: :-O
Özel Karakterler:
Bağlı değilsiniz. · Şifremi unuttum · ÜYELİK
This board is powered by the Unclassified NewsBoard software, 20100516-dev, © 2003-10 by Yves Goergen
Şu an: 2017-11-23, 23:13:35 (UTC -08:00)