Forum: Ders Arası RSS
string'in bir parçasını (sub-string) almak
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'in bir parçasını (sub-string) almak
Başka dillerde bulunan substr() gibi işlevler Phobos'ta bulunmuyor. D'de dilimlemek var ama tabii string'in UTF-8 dizisi olduğunu hatırlarsak işe yaramadığı açık.

Örneğin, şöyle bir kullanım iyi olurdu:
    // İkinci indeksten dördüncü indekse kadar (dört hariç)
    assert("abcçdef".subString(2, 4).equal("cç"));
İlk parametre başlangıç indeksi olabilir. İkinci indeks ise ya uzunluk olur ya da yukarıda olduğu gibi bitiş indeksi.

Bu soru şu sıralarda İngilizce D.learn haber grubunda da soruldu. (Oradan kopya çekmeyin! :-p) Orada eksi değerli indekslerin de kullanışlı olacağı hatırlatıldı. Başka dillerde öyle: -1 indeksi sonuncu karakter anlamına geliyor; -2 sondan bir önceki karakter, vs.

Siz olsanız nasıl yazardınız?

Ali
Avatar
Salih Dinçer #2
Üye Ock 2012 tarihinden beri · 1912 mesaj · Konum: İstanbul
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
Kötü bir kodlama oldu ama benim bulduğum çözüm şu:
import core.bitop : popcnt;
 
string subString(string str, size_t left, size_t right) {
  debug(0) {
    foreach(chr; str) chr.write(", ");  
    writeln;
  }
 
  int chrCount, bitPos = 8;
 
  size_t[] xSay;
  string result;    
 
  foreach(chr; str) {
    while(bitPos--) {
      auto bitTest = 8 - popcnt(chr >> bitPos);
      if(bitTest == bitPos) {
        chrCount++;
      } else {
        bitPos = 0; // break ?
      }
    }
 
    if(chrCount && !result.length) xSay ~= chrCount;  
 
    if(chrCount--) {
      if(result.length) {
        assert(popcnt(chr >> 6) == 1, "UTF failure");
      }
      result ~= chr;
    } else {
      debug(0) chr.write;
      xSay ~= 1;
    }
    if(chrCount < 1) {
      debug(0) result.write;
      bitPos = 8;
      result.length = 0;
      chrCount = 0;
    }
  }
 
  debug(0) xSay.writeln;
  
  if(right > xSay.length) {
    assert(0, "Range violationnnn"); // customizable
  }
  
  size_t a, b;
  foreach(sum; xSay[0..left]) a += sum; 
  foreach(sum; xSay[left..right]) b += sum;
 
  return str[a..a+b];
}
 
 
import std.stdio, std.algorithm : equal;
 
void main() {
 assert("abcçdef".subString(2, 4).equal("cç"));
 assert("abcÇçuüz".subString(3, 5).equal("Çç")); // chr.length == 8
 
 debug(1) "ağ".subString(0, 3).writeln; // range violation error
}
Bilgi paylaştıkça bir bakmışız; kar topu olmuş ve çığ gibi üzerimize geliyor...:)
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ı
Seninkinin iyi tarafı, döndürülen dilimin asıl karakterleri gösteriyor olması. (İstesek onları değiştirebiliriz diyeceğim ama UTF-8'de bunun her durumda mümkün olmadığını biliyoruz.)

Doğrusu, beklediğimden çok alt düzey bir çözüm olmuş. :) Phobos'ta o işlemlerin hiç olmazsa bazılarını karşılayan işlevler vardır. (Yeni std.uni çok kapsamlı olarak geliyor(muş).)

Benim ilk aklıma gelen yöntem çok kısa ama bir kötü tarafı, seninki gibi var olan bir dilim döndürmüyor. subRange karakterleri tembel olarak üretiyor. subString ise o aralığı hevesle string'e dönüştürüyor:
import std.range;
import std.algorithm;
 
auto subRange(R)(R r, size_t beg, size_t end)
{
    return r.dropExactly(beg).take(end - beg);
}
 
unittest
{
    assert("abcçdef".subRange(2, 4).equal("cç"));
}
 
import std.conv;
 
string subString(string s, size_t beg, size_t end)
{
    return s.subRange(beg, end).text;
}
 
unittest
{
    assert("abcçdef".subString(2, 4).equal("cç"));
}
 
void main()
{}
Ali
Avatar
Salih Dinçer #4
Üye Ock 2012 tarihinden beri · 1912 mesaj · Konum: İstanbul
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
Hocam seninki çok daha leziz ve inanılmaz kısa olmuş...:)

Bahsettiklerin dışında bir farkı da "range violation" hatası vermemesi. Çünkü aralıklar üzerinde çalışıyor. Tabi subString() içinde uzunluk denetimi yapabilmemiz mümkün ama "abcçdef".subRange(2, 40) diye bir parametre verdiğimizde hiç bir hata atmadan olduğu gibi sonuna kadar aralık döndürüyor.

Biraz zor bir örnek (UTF8+UTF16+UTF8) üzerinde test ettiğimde, her ikisinde de bir sorun yaşamadım.
(Editörünüz, aşağıdaki satırı sağa bitişik ve garip şekilde ";subRange(0, 2).writeln."ߡࠀߜ"" yazabilir!)
 "ߡࠀߜ".subRange(0, 2).writeln; //ࠀߜ
 
/* Benim çıktı:
�, �, �, �, �, �, �, 
ߡࠀߜ[2, 3, 2]
*/
Yazdıklarımızı biraz daha geliştirip (range ve low level birleştirilerek) bir sınıf haline getirmemiz mümkün. Belki o zaman çeşitli özellikler eklenebilir. Örneğin index'i verilen karakterin hafızada kaç byte yer kapladığı olabilir. Tıpkı yukarıdaki örnekte, ortadaki karakterin 3 byte yer kaplaması gibi.

Özetle kodunuz aralık isterse kısa kodun gönderdiğini, dizi isterse benim yazdığımın sonucunu gönderebilir. Bilmiyorum yurt dışında ne tür çözümler geldi ama açıkcası hiç bakmadım. Tabii ki her yiğidin yoğurt yiyişi farklı ama ben biraz bit'ler üzerinde cirit atmayı, Ali hocam da aralıklarda gezinmeyi sanırım çok seviyor...:)

Sevgiler, saygılar...
Bilgi paylaştıkça bir bakmışız; kar topu olmuş ve çığ gibi üzerimize geliyor...:)
Bu mesaj Salih Dinçer tarafından değiştirildi; zaman: 2013-10-28, 13:38.
Değişiklik nedeni: Önemsiz yazım hataları giderildi...
Avatar
Salih Dinçer #5
Üye Ock 2012 tarihinden beri · 1912 mesaj · Konum: İstanbul
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
Yaptığım kodlama içerisine açıklama yerleştirme fırsatım olmadı. Öylesine ayaküstü yazılmış bir şeydi. Aslında bitler içindeki düzeni ben de bilmiyordum. Internette bir çok kaynakta codepoint'den bahsediliyordu.
(-bknz. http://en.wikipedia.org/wiki/UTF-8#Description)

Gerçekten basit bir sistem olduğu için fazla açıklama yapmıyorum. Hatta yazdığımdan daha akıllıcası ve kısası mümkün de olabilir. Az önce koda bakarken abartılmış if()'ler gördüm. Sanırım bunları kaynaştırmak mümkün. Wikipedia maddesi ile döngü çekirdeğine bakarsanız ne yapmak istediğimi çıkarabilirsiniz. Aslında her şey bakkal hesabı gibi saymaktan ibaret...:)

Başarılar...
Bilgi paylaştıkça bir bakmışız; kar topu olmuş ve çığ gibi üzerimize geliyor...:)
Avatar
Salih Dinçer #6
Üye Ock 2012 tarihinden beri · 1912 mesaj · Konum: İstanbul
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
Yanıtlanan mesaj #2
import core.bitop : popcnt;
Son olarak popcnt()'nin ne yaptığı hakkında bilgi vereyim...

Bir keresinde şuradaki başlıkta, çekirdeğin bir modülünden yorumsuz bir şekilde bahsetmiştim. Aslında içeriğinde başka işlevler de var ama Wikipedia maddesini görünce aklıma hemen o işlev geldi. Ben de kodu onun üzerine bina ettim.

Belki kendi maskelememizi yapabilirdik ama bu, gelişmiş CPU'lar için çok hızlı sonuç üretecek şekil tasarlanmış bir modüldü ve ortalığı daha fazla karıştırmamayı yeğledim...:)

İşleve gelince; tek yaptığı kendisine gönderilen uint değerin kaç adet 1 bitine sahip olduğunu bizim için saymak. Zaten UTF'de ilk baytın solunda kaç tane bitişik 1 var ise bize bu karakterin kaç byte'dan oluştuğu bilgisini veriyor. Ama işlem biraz daha kontrollü olması için baytı sırayla (belki gereksizce) solda saymaya çalıştım.

Mutlaka daha basit yöntemi de vardır, ne dersiniz?

Dip Not: Şurada bazı C kodları gördüm. İşimize yarayabilirdi de...
Bilgi paylaştıkça bir bakmışız; kar topu olmuş ve çığ gibi üzerimize geliyor...:)
acehreli (Moderatör) #7
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ı
Yanıtlanan mesaj #4
Salih Dinçer:
inanılmaz kısa olmuş...:)

Çünkü D (ve Phobos) UTF-8 kodlamasını biliyor ve dropExactly dediğimde senin yaptığın işlemlerin benzerleri ile o kadar sayıda harf atlıyor.

Bahsettiklerin dışında bir farkı da "range violation" hatası vermemesi.

O benim hatam olmuş. take yerine takeExactly kullanılırsa o da denetlenir.

Tabi subString() içinde uzunluk denetimi yapabilmemiz mümkün

Hem de aralığın çeşidine göre... Örneğin, length niteliği varsa sonuna kadar ilerlemeden öğrenmek mümkün:
import std.range;
import std.algorithm;
import std.traits;
import std.exception;
import std.string;
 
auto subRange(R)(R r, size_t beg, size_t end)
{
    static if (hasLength!R || isArray!R) {
        enforce((beg < r.length) && (end <= r.length),
                format("%s elemanlı aralıkta [%s,%s) indeksleri yasal değil",
                       r.walkLength, beg, end));
    }
 
    return r.dropExactly(beg).takeExactly(end - beg);
}
 
import std.conv;
 
string subString(string s, size_t beg, size_t end)
{
    return s.subRange(beg, end).text;
}
 
void main()
{
    assert("abcçdef".subString(2, 40).equal("cç"));
}
Dizginin uzunluğu 7 yazılsın diye hata mesajında r.length değil, r.walkLength kullandım. Hata mesajı:

7 elemanlı aralıkta [2,40) indeksleri yasal değil

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:
Forum: Ders Arası RSS
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-21, 13:21:49 (UTC -08:00)