Sabit uzunluklu dizgilerin düşündürdükleri
Blog gibi bir yazı oldu. :) Çok basit bir konu gibi görünüyor ama bir sürü başka konuyla ilgili.
Şu konudaki ID3v1Pakedi yapısının üyeleri sabit uzunluklu dizgiler olarak tasarlanmıştı:
http://ddili.org/forum/thread/705
mp3 dosyalarının sonundaki ID3v1 pakedine bire bir uyduğu için kolayca okuyup yazabildiğimiz için iyi bir fikir olduğu düşünülebilir. O açıdan doğrudur. Ancak, program içinde kullanım açısından sorunları var. O konuda da görüldüğü gibi, basit bir atama işlemini bile gerçekleştiremiyoruz.
O yapıyı bir kenara bırakalım ve basitçe şu değişkene bakalım:
char[30] d;
d = "merhaba"; // <-- çalışma zamanı hatası!
Sol tarafla sağ tarafın uzunlukları tutmadığı için derlenemiyor. Bir çözüm olarak şunu bulmuştuk:
d = leftJustify("merhaba", d.length, '\0');
Bu konuyu İngilizce forumlarda şurada açtım:
http://dfeed.kimsufi.thecybershadow.net/discussion/thread/jgcfs5$2uss$1@digitalmars.com
Oradan çok daha basit olan yöntemi öğrendim (veya hatırladım :)):
auto değer = "merhaba";
d[0 .. değer.length] = değer; // baş tarafına ata
d[değer.length .. $] = '\0'; // sonunu '\0' doldur
(Not: Tabii orada 'değer''in hedefe sığacağını varsayıyoruz.)
O yöntem leftJustify'ın içinde de görülüyor:
retval[0 .. s.length] = s[];
retval[s.length .. $] = cast(C)fillChar;
Neyse... Tekrar baştaki soruya dönersek, char[N] bir dizgi midir? Rahatça bir dizgi gibi kullanamadığımıza bakarak bence onun dizgi olmadığını kabul etmek gerekiyor.
Daha iyi bir tasarım, mp3 dosyasının sonundaki pakedi açmak ve kullanılabilir bir yapı oluşturmaktır. Program içinde o yapı kullanılır ve dizgilerle rahatça oynanır. En sonunda tekrar dosyaya yazılacağı zaman da nesneye "kendini bir ID3v1 pakedi olarak ifade et" denir. Ve işte o paket dosyaya yazılır.
Dosya sonundaki ID3v1 pakedi aslında şu kavramı temsil ediyor: Şarkı hakkında bilgi. Pakedin sabit uzunluklu dizgilerden oluşan parçaları aslında şarkı bilgisinin yalnızca bir gösterimi.
Bütün bunların ışığı altında ben şöyle bir tasarım önereceğim:
import std.stdio;
import std.string;
import std.exception;
import std.conv;
// bkz. http://www.multimediasoft.com/amp3dj/help/index.html?amp3dj_00003e.htm
enum Tarz { blues /* ... vs. ... */ }
/*
* Bu, program içinde serbestçe kullanacağımız şarkı bilgisini taşıyor. Bu
* sınıf rahat üyelerden oluştuğu için programda çok durumda kullanabiliriz.
*
* Bunun bir üstünlüğü, bu bilgilerin farklı ID3 standartlarını da
* destekleyebilmesidir. Örneğin ID3v1 Unicode'u desteklemediği halde burada
* Unicode harfler kullanabiliriz. Ancak ID3v1 pakedi oluşturulduğunda Türkçe
* harflerin ASCII karşılıklarına dönüştürülmeleri gerekir. O işlem bu sınıfı
* bağlamamalıdır.
*
* Şarkının bilgisi, dosya içindeki gösteriminden farklı bir kavramdır. Bu
* sınıf o yüzden var.
*/
class ŞarkıBilgisi
{
string başlık;
string sanatçı;
string albüm;
size_t yıl;
string mesaj;
Tarz tarz;
/*
* Verilen paketten ŞarkıBilgisi oluşturan kurucu işlev
*/
this(ID3v1Pakedi paket)
{
this.başlık = to!string(paket.başlık);
this.sanatçı = to!string(paket.sanatçı);
this.albüm = to!string(paket.albüm);
this.yıl = to!int(paket.yıl);
this.mesaj = to!string(paket.mesaj);
this.tarz = cast(Tarz)paket.tarz[0];
}
/*
* Yapılacak bir iş: Burada bir kurucu daha iyi olur. Bu kurucu da başlık,
* vs. bilgileri normal D türleri olarak alır ve onlardan bir ŞarkıBilgisi
* oluşturur. Bu programda henüz gerekmediği için yazmadım:
*
* this (string başlık, string sanatçı, vs.) { ... }
*/
/*
* Bu dönüşüm işleci ŞarkıBilgisi nesnesinden ID3v1Pakedi
* oluşturur. Görüldüğü gibi asıl işi ID3v1Pakedi türünün kurucusuna
* devrediyor.
*/
ID3v1Pakedi opCast(T : ID3v1Pakedi)() const
{
return ID3v1Pakedi(başlık, sanatçı, albüm, yıl, mesaj, tarz);
}
}
struct ID3v1Pakedi
{
char[3] TAG; // "TAG" belirteci
char[30] başlık; // title
char[30] sanatçı; // artist
char[30] albüm; // album
char[4] yıl; // year
char[30] mesaj; // comment
char[1] tarz; // genre Bunu tek char da yapabilirdik
/*
* Bu kurucunun 'tag' bilgisi almadığına dikkat edin. O bilgi bu türün
* kendisi ile ilgili bir konudur; dışarıdan verilmesi beklenen bir şey
* değildir.
*/
this(string başlık,
string sanatçı,
string albüm,
size_t yıl,
string mesaj,
Tarz tarz)
{
/*
* Yalnızca bu kurucu tarafından kullanıldığı için bu işlevi böyle
* içeride tanımladım; başka bir yerde de olabilirdi ve gerekirse
* taşınabilir.
*
* Verilen dizginin sonu '\0' karakterleriyle dolu olmak üzere
* 'genişlik' kadar uzun olanını döndürür. Dizgi uzunsa kırpar.
*
* Daha da yapılması gereken işlem: ID3v1 paketleri yalnızca ASCII ile
* işliyorlar. 'değer' içindeki Türkçe karakterleri aksansızlarıyla
* değiştirmek de gerekir.
*/
string sabitUzunluklu(string değer, size_t uzunluk)
{
// Gerekirse kırp
if (değer.length > uzunluk) {
değer.length = uzunluk;
}
// Sonunun da '\0' ile doldurulmasını sağla
return leftJustify(değer, uzunluk, '\0');
}
this.TAG = "TAG";
this.başlık = sabitUzunluklu(başlık, 30);
this.sanatçı = sabitUzunluklu(sanatçı, 30);
this.albüm = sabitUzunluklu(albüm, 30);
this.yıl = sabitUzunluklu(to!string(yıl), 4);
this.mesaj = sabitUzunluklu(mesaj, 30);
this.tarz = [ cast(char)tarz ]; // Bu üyeyi tek char da yapabilirdik
}
static ID3v1Pakedi oku(File dosya)
{
immutable dosyaYeri = dosya.tell();
scope (exit) dosya.seek(dosyaYeri, SEEK_SET);
dosya.seek(-cast(long)ID3v1Pakedi.sizeof, SEEK_END);
ID3v1Pakedi[1] paketler;
dosya.rawRead(paketler);
ID3v1Pakedi paket = paketler[0];
enforce (paket.TAG == "TAG",
format("'%s', bir ID3v1 dosyası değil!", dosya.name()));
return paket;
}
void yaz(File dosya) const
{
dosya.seek(-cast(long)ID3v1Pakedi.sizeof, SEEK_END);
dosya.rawWrite([ this ]);
}
}
int main(string[] args)
{
if (args.length < 2) {
stderr.writefln("Kullanım: %s dosya1.mp3 [dosya2.mp3 [...]]", args[0]);
return 1;
}
foreach (dosyaİsmi; args[1..$]) {
auto dosya = File(dosyaİsmi, "r+");
auto paket = ID3v1Pakedi.oku(dosya);
auto bilgi = new ŞarkıBilgisi(paket);
writef("%s dosyası için mesaj giriniz: ", dosyaİsmi);
string mesaj = strip(readln());
bilgi.mesaj = mesaj;
/*
* Buradaki dönüşüm perde arkasında ve otomatik olarak
* ŞarkıBilgisi.opCast!ID3v1Pakedi() işlevini çağırır
*/
paket = to!ID3v1Pakedi(bilgi);
paket.yaz(dosya);
}
return 0;
}
Ali
--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]