Bir Karakterin Dönüştürülmesi
Önceki Geridönüşümlü Çok Baytlı Dönüşüm Sonraki
Bir Karakterin Dönüştürülmesi
Çok temel dönüşüm işlevlerinin çoğu tek karakter ile çalışır. Lütfen aklınızdan çıkarmayın, tek karakter her zaman tek bayt anlamına gelmez. Ancak çoğunlukla çokbaytlı karakter kümeleri tek baytlık karakterler içerdiğinden, baytları dönüştürmeye yarayan işlevler vardır. Sıklıkla, ASCII çokbaytlı karakter kümesinin bir alt kümesidir. Böyle bir senaryoda, her ASCII karakter kendini temsil eder, tüm diğer karakterlerin en azından ilk baytı 0 ile 127 arasındaki karakterlerden biri olur.
wint_t btowc
(int c)
işlev
btowc ("byte to wide character" kısaltması) işlevi, ilk öteleme durumundaki tek baytlık geçerli bir c karakterini o an geçerli olan LC_CTYPE yerelindeki dönüşüm kurallarına uygun olarak geniş karakter eşdeğerine dönüştürür.
Eğer (unsigned char) c geçerli olmayan tek baytlık bir çokbaytlı karakter ise ya da c karakteri EOF ise işlev WEOF ile döner.
c karakterinin geçerliliğinin sadece ilk öteleme durumu için sınandığını lütfen unutmayın. Durum bilgisinin alınmasında kullanılacak bir mbstate_t nesnesi yoktur ve ayrıca işlev herhangi bir sabit durum kullanmaz.
btowc işlevi ISO C90 standardının 1. düzeltmesinde tanımlanmış ve wchar.h başlık dosyasına bildirilmiştir.
Tek baytlık değerlerin daima ilk durumuna göre yorumlanması sınırlamasına rağmen bu işlev aslında çoğu zaman oldukça kullanışlıdır. Karakterlerin çoğu ya tamamen tek baytlık karakter kümelerindendir ya da ASCII'ye göre bir genişletmedir. Bu bilgilerden sonra aşağıdaki gibi bir kod yazmak mümkündür (bu özel örnek çok kullanışlıdır):
wchar_t *
itow (unsigned long int val)
{
  static wchar_t buf[30];
  wchar_t *wcp = &buf[29];
  *wcp = L'\0';
  while (val != 0)
    {
      *--wcp = btowc ('0' + val % 10);
      val /= 10;
    }
  if (wcp == &buf[29])
    *--wcp = L'0';
  return wcp;
}
Böylesine karmaşık bir gerçeklemeyi kullanmak neden gerekli ve neden basitçe '0' + val % 10 bir geniş karaktere dönüştürülmüyor? Yanıtı, wchar_t türünde karakterler üzerinde bu çeşit aritmetik işlemler uygulandığında sonuç garanti değildir de ondan. Diğer durumlarda ise baytlar derleme zamanında sabit değildir, bu nedenle derleyici işlem yapamaz. Bu gibi durumlarda btowc kullanmak gereklidir.
Aksi yönde dönüşüm için de bir işlev vardır.
int wctob
(wint_t wc)
işlev
wctob ("wide character to byte" kısaltması) işlevi parametre olarak geçerli bir geniş karakter alır. Bu karakterin ilk durumu bir bayt uzunlukta ise işlevin dönüş değeri karakterin kendisi olacaktır. Aksi takdirde EOF döner.
wctob işlevi ISO C90 standardının 1. düzeltmesinde tanımlanmış ve wchar.h başlık dosyasına bildirilmiştir.
Karakterleri tek tek çokbaytlı gösterimden geniş karakterli gösterime ya da tersine dönüştürecek daha genel amaçlı işlevler de vardır. Bu işlevler çokbaytlı gösterimin uzunluğu ile ilgili bir sınırlamaya sahip değillerdir ve ayrıca ilk durumda olmayı gerektirmez.
size_t mbrtowc
(wchar_t *restrict    pwc,
 const char *restrict dizge,
 size_t               n,
 mbstate_t *restrict  ps)
işlev
mbrtowc ("multibyte restartable to wide character" kısaltması) işlevi, dizge ile gösterilen dizgedeki sonraki çokbaytlı karakteri geniş karaktere dönüştürür ve pwc ile gösterilen geniş karakterli dizge içinde saklar. Dönüşüm o an geçerli olan LC_CTYPE ile belirtilen yerele bağlı olarak gerçekleşir. Yerelde kullanılan karakter kümesi dönüşüm için bir durum bilgisi gerektiriyorsa, bu bilgi ps ile gösterilen nesne ile belirtilebilir. ps bir boş gösterici ise, işlev tarafından bir durağan dahili durum değişkeni kullanılır.
Sonraki çokbaytlı karakter bir boş geniş karakter ise, işlevin dönüş değeri sıfırdır ve durum nesnesi sonrasında ilk durumdadır. Eğer sonraki n veya daha az bayt doğru çokbaytlı karakter biçimindeyse, dönüş değeri, çokbaytlı karakter biçimindeki dizge’den başlayan baytların sayısıdır. Dönüşüm durumu dönüşümde tüketilen baytlara göre güncellenir. Her iki durumda da geniş karakter (ya L'\0' ya da dönüşümde bulunan) pwc bir boş gösterici değilse, pwc ile gösterilen dizgede saklanır.
Çok baytlı dizgenin ilk n baytının geçerli bir çokbaytlı karakter olduğu varsayılmış ama dönüşüm için n’den daha fazla bayt gerekiyorsa işlevin dönüş değeri (size_t) -2’dir ve hiçbir değer saklanmaz. Girdi gereğinden fazla öteleme durumu içerebildiğinden n, MB_CUR_MAX'dan büyük ya da ona eşit bir değer içerdiğinde bile bu durumun oluşabileceğini unutmayınız.
Çokbaytlı dizgenin ilk n baytının geçerli bir çokbaytlı karakter olduğu varsayılmamışsa, hiçbir değer saklanmaz, errno genel değişkenine EILSEQ değeri atanır ve işlev (size_t) -1 ile döner. Dönüşüm durumu bundan sonra tanımsızdır.
mbrtowc işlevi ISO C90 standardının 1. düzeltmesinde tanımlanmış ve wchar.h başlık dosyasına bildirilmiştir.
mbrtowc işlevinin kullanımı basittir. Bir çokbaytlı dizgeyi bir geniş karakterli dizgeye kopyalarken küçük harfleri büyük harfe çeviren bir işlev şöyle olur (bu tam bir uygulama değildir; sadece örnektir; hata denetimi yapılmaz ve bellek kaçağı olabilir):
wchar_t *
mbstouwcs (const char *s)
{
  size_t uzunluk = strlen (s);
  wchar_t *sonuc = malloc ((uzunluk + 1) * sizeof (wchar_t));
  wchar_t *wcp = sonuc;
  wchar_t tmp[1];
  mbstate_t durum;
  size_t bayt_sayisi;

  memset (&durum, '\0', sizeof (durum));
  while ((bayt_sayisi = mbrtowc (tmp, s, uzunluk, &durum)) > 0)
    {
      if (bayt_sayisi >= (size_t) -2)
        /* Geçersiz girdi dizgesi.  */
        return NULL;
      *wcp++ = towupper (tmp[0]);
      uzunluk -= bayt_sayisi;
      s += bayt_sayisi;
    }
  return souc;
}
mbrtowc kullanımı temizdir. Tek bir geniş karakter tmp[0] içinde saklanır ve tüketilen baytların sayısı bayt_sayisi değişkeninde saklanır. Eğer dönüşüm başarılı olursa, geniş karakterin büyük harf karşılığı sonuc dizisinde saklanır ve girdi dizgesinin göstericisi ile kullanılabilir baytların sayısı ayarlanır.
mbrtowc hakkında belirsiz kalan tek şey sonuç için belleği ayırmakta kullanılan yöntem olabilir. Yukarıdaki kod, çokbaytlı girdi dizgesindeki baytların dönüşüm sonrası elde edilen geniş karakterli dizgenin bayt sayısından büyük ya da eşit olduğu varsayımına dayandırılmıştır. Bu yöntem sonucun uzunluğu hakkında iyimserdir ve çok sayıda geniş karakterli dizge ya da çok uzun bir dizge bu yöntemle oluşturulmaya çalışılırsa ek bellek ayrılması gerekebilecektir. Ayrılan bellek bloğu döndürülmeden önce doğru boyuta ayarlanabilir, fakat en doğrusu sonucun gerektirdiği kadar belleği baştan ayırmaktır. Umulanın aksine, elde edilecek geniş karakterli dizgenin boyunu çokbaytlı dizgenin boyutlarına bakarak elde edebilecek bir işlev yoktur. Yine de, işlemin bir parçası olabilecek bir işlev vardır.
size_t mbrlen
(const char *restrict dizge,
 size_t               n,
 mbstate_t           *ps)
işlev
mbrlen ("multibyte restartable length" kısaltması) işlevi, dizge ile başlayan en fazla n baytlık sonraki geçerli ve çokbaytlı tam karakterin bayt sayısını hesaplar.
Sonraki çokbaytlı karakter boş geniş karaktere karşılıksa, dönüş değeri sıfırdır. Sonraki n bayt, geçerli bir çokbaytlı karakter biçimindeyse, bu çokbaytlı karakteri oluşturan baytların sayısı ile döner.
Eğer ilk n geçerli bir çokbaytlı karakter için yetersizse, dönüş değeri (size_t) -2’dir. Aksi halde, çokbaytlı karakter dizilimi geçersizdir ve dönüş değeri (size_t) -1’dir.
Çokbaytlı dizilim ps ile gösterilen nesne ile belirtilen duruma göre yorumlanır. ps bir boş gösterici ise mbrlen'e özgü bir dahili durum nesnesi kullanılır.
mbrlen işlevi ISO C90 standardının 1. düzeltmesinde tanımlanmış ve wchar.h başlık dosyasına bildirilmiştir.
Dikkatli okuyucular mbrlen işlevinin şöyle gerçeklenebileceğini farkedecektir:
mbrtowc (NULL, s, n, ps != NULL ? ps : &dahili)
Bu doğrudur ve aslında resmi belirtimde bahsedilendir. Şimdi, bir çokbaytlı karakter dizisinden bir geniş karakterli dizgenin uzunluğunun nasıl saptanabileceğine bakalım. İşlev doğrudan kullanılabilir değildir, fakat mbslen isimli bir işlevi onu kullanarak tanımlayabiliriz:
size_t
mbslen (const char *s)
{
  mbstate_t durum;
  size_t sonuc = 0;
  size_t bayt_sayisi;
  memset (&durum, '\0', sizeof (durum));
  while ((bayt_sayisi = mbrlen (s, MB_LEN_MAX, &durum)) > 0)
    {
      if (bayt_sayisi >= (size_t) -2)
        /* Birşeyler yanlış.  */
        return (size_t) -1;
      s += bayt_sayisi;
      ++sonuc;
    }
  return sonuc;
}
Bu işlev, dizgedeki her çokbaytlı karakter için basitçe mbrlen çağrısı yapar ve işlev çağrılarını sayar. Burada MB_LEN_MAXmbrlen'in boyut argümanı olarak kullandığımıza dikkat edin. Bu kabul edilebilir, çünkü;
  1. Bu değer en uzun çokbaytlı karakter diziliminden büyüktür.
  2. dizge dizgesinin boş bayt ile bittiğini biliyoruz. Bu sonlandırıcı bayt başka bir çokbaytlı karakter diziliminin parçası olamaz ama bir geniş boş karakteri temsil edebilir.
Diğer taraftan, mbrlen işlevi geçersiz belleği asla okumaz.
Şimdi bu işlev kullanılabilir (daha temiz olarak, bu işlev GNU C kütüphanesinin bir parçası değildir). Çok baytlı karakterli dizge dizgesinden dönüştürülerek elde edilecek geniş karakterli dizgenin saklanacağı alanın genişliğini hesaplayabiliriz:
wcs_bytes = (mbslen (s) + 1) * sizeof (wchar_t);
mbslen işlevinin verimsiz olduğunu unutmayın. mbstouwcs'nin mbslen ile gerçeklenmesi çokbaytlı karakterli girdi dizgesine iki defa dönüşüm uyguladığından bu dönüşüm masraflıdır. Bu durumda, kullanımı daha kolay ama işlemi iki defa yapmayan bir yöntem düşünmek gerekir.
size_t wcrtomb
(char *restrict      dizge,
 wchar_t             wc,
 mbstate_t *restrict ps)
işlev
wcrtomb ("wide character restartable to multibyte" kısaltması) işlevi tek bir geniş karakteri, bunun karşılığı olan çokbaytlı karakter dizgesine dönüştürür.
dizge bir boş gösterici ise, işlev, ps ile gösterilen nesnedeki durum bilgisini (ya da işlevin dahili durum bilgisini) başlangıç durumuna sıfırlar. dizge boş bir gösterici olduğundan, bu aşağıdaki gibi bir çağrı ile aşılabilir:
wcrtombs (temp_buf, L'\0', ps)
wcrtomb yeterince büyük olduğu garanti edilen bir dahili tampona yazabiliyorsa bunu yapar.
Eğer wc bir boş geniş karakter ise, wcrtomb bunu yoksayar, eğer gerekliyse, ps durumunu ilk duruma getirecek bir öteleme diziliminin ardından tek bir boş karakter gelir ve dizge dizgesinde saklanır.
Aksi takdirde, bir bayt dizilimi (öteleme dizilimlerini de içerebilir) dizge dizgesine yazılır. Bu sadece wc geçerli bir geniş karakterse mümkündür (örneğin, yereli LC_CTYPE kategorisine göre seçilen karakter kümesindeki bir çokbaytlı gösterim). wc geçerli bir geniş karakter değilse, dizge dizgeside hiçbir şey saklanmaz, (size_t) -1 döner.
Bir hata oluşmazsa, işlev dizge dizgesinde saklanan baytların sayısı ile döner. Bu, öteleme durumlarını gösteren bütün baytları içerir.
İşlevin arayüzünden biraz bahsetmek gerekirse: dizge dizgesinin uzunluğunu belirten bir parametre yoktur. Bunun yerine, işlev en azından MB_CUR_MAX baytın varlığını kabul eder. Çünkü tek bir karakterin ifade edilebileceği azami bayt sayısı budur. Bu durumda, çağrıcı yeterli yerin mevcut olduğuna emindir, aksi takdirde tampon taşması oluşabilirdi.
wcrtomb işlevi ISO C90 standardının 1. düzeltmesinde tanımlanmış ve wchar.h başlık dosyasına bildirilmiştir.
wcrtomb işlevinin kullanımı mbrtowc işlevinin kullanımına göre daha kolaydır. Aşağıdaki örnekte, bir geniş karakterli dizge bir çokbaytlı dizgeye eklenmektedir. Tekrar belirtelim; kod kullanılabilir (veya doğru) değildir, bazı sorunlara ve kullanıma bir örnektir.
char *
mbscatwcs (char *s, size_t len, const wchar_t *ws)
{
  mbstate_t state;
  /* Mevcut dizgenin uzunluğunu bulalım.  */
  char *wp = strchr (s, '\0');
  len -= wp - s;
  memset (&state, '\0', sizeof (state));
  do
    {
      size_t nbytes;
      if (len < MB_CUR_LEN)
        {
          /* Sonraki karakterin tampona sığacağını garanti etmiyoruz.
             Bu durumda bir hata dönebilir.  */
          errno = E2BIG;
          return NULL;
        }
      nbytes = wcrtomb (wp, *ws, &state);
      if (nbytes == (size_t) -1)
        /* Dönüşümde hata.  */
        return NULL;
      len -= nbytes;
      wp += nbytes;
    }
  while (*ws++ != L'\0');
  return s;
}
İlk işlevde s dizisi içindeki dizgenin sonu bulunmaktadır. strchr işlevi bunu çok iyi yapar, çünkü çokbaytlı karakter gösterimlerinde bir gereklilik olarak boş bayt kendisini temsil etmesi (bu bağlamda dizgenin sonu) dışında bir amaçla asla kullanılmaz.
Durum nesnesini ilklendirip döngüye girilince ilk iş olarak s dizisinde yeterince yer olup olmadığına bakıyoruz. MB_CUR_LEN bayttan daha az yer varsa işlevden çıkıyoruz. Bu daima en iyi seçim olmasa da bizim başka bir şansımız yok. MB_CUR_LEN bayttan daha az yer olabilir ve sonraki çokbaytlı karakter sadece bir bayt uzunlukta olabilirdi. Bu durumda tamponda yeterince yerin varlığına karar verecek ek kod nedeniyle işlev çok geç dönerdi. Bu çözüm pek kullanışlı değildir, daha doğru ama çok yavaş bir çözüm olurdu.
...
if (len < MB_CUR_LEN)
{
  mbstate_t temp_state;
  memcpy (&temp_state, &state, sizeof (state));
  if (wcrtomb (NULL, *ws, &temp_state) > len)
  {
    /* Sonraki karakterin tampona sığacağını garanti etmiyoruz.
             Bu durumda bir hata dönebilir.  */
    errno = E2BIG;
    return NULL;
  }
}
...
Burada tamponun taşabileceği bir dönüşüm uyguluyoruz, yani tamponun boyutu hakkındaki kararı işlemi yaptıktan sonra veriyoruz. wcrtomb çağrısındaki hedef tampon için NULL argümanına dikkat edin; bu noktada dönüştürülen metinle ilgilenmediğimizden, bu, bu sorunu aşmak için iyi bir yöntemdir. Bu kod parçasındaki en lüzumsuz şey dönüşüm durum nesnesinin yinelenmesidir; ancak eğer sonraki çokbaytlı karakteri yoksayacak bir değişiklik gerekliyse gerçek dönüşümde aynı öteleme durum değişikliğinin uygulanmasını isteriz. Bunun yanında, ilk öteleme durum bilgisini korumak zorundayız.
Bu soruna çok sayıda ve çok daha iyi çözümler vardır. Bu örnek sadece öğrenim amacıyla hazırlanmıştır.
Önceki Üst Ana Başlık Sonraki
Durumun saklanması Başlangıç Dizge Dönüşümleri
Bir Linux Kitaplığı Sayfası