DERS: PROGRAMLAMA DİLLERİ – 1 DÖNEM: 2002-2003 Güz yarı yılı
KONU 7: DOSYA İŞLEME (“File Processing”)
HEDEFLER:
1- Dosyaları yaratabilme, okuyabilme, yazabilme ve güncelleyebilme 2- Sıralı erişim dosyalarını tanıma
3- Rasgele (doğrudan) erişim dosyalarını tanıma 4- Bu dosyalar üzerinde G/Ç işlemleri yapma İÇERİK:
Veri hiyerarşisi
Sıralı erişim dosyası yaratma Sıralı erişim dosyasından okuma Sıralı erişim dosyalarını güncelleme Rasgele erişimli dosyalar
Sıralı erişim dosyalarını yaratma Sıralı erişim dosyalarına yazma Sıralı erişim dosyasından sıralı okuma
VERİ HİYERARŞİSİ
Bilgisayarlarda her sayı ya da alfabetik karakterin 0 ve 1’lerle temsil edildiğini biliyorsunuz.
Karakterlerin bitlerden oluşması gibi alanlar da karakter (veya baytlardan) oluşur. Alan bir anlamı olan bir grup karakterdir.
Bir kayıt ise (C++’da class , C’de struct ) birden fazla alandan meydana gelir. Örneğin bir muhasebe sisteminde bir çalışana ait bir kayıtta aşağıdaki alanlar bulunabilir.
1- Sicil no 2- Adı 3- Adresi 4- Ücreti 5- Vergi oranı
Dolayısıyla bir kayıt birbiriyle alakalı alanlardan meydana gelir.
100000 kaydın olduğu bir dosyadan bir kayda erişmek istediğimizi düşünelim. Genellikle bu erişimi kolaylaştırmak için kaydın bir alanı anahtar (“record key”) olarak belirlenir ve erişim esnasında kullanılır. Yukardaki örnekte sicil no kayıt anahtarı olarak kullanılabilir.
Şirketlerin birden fazla dosyaları vardır: muhasebe, alacak hesapları, stok, personel, vb gibi.
Birbiriyle ilgili dosyaların bir veri tabanı programı kullanılarak veri tabanında tutulması pek
çok kolaylık sağlar: veri bütünlüğü, veri tutarlılığı, bakım kolaylığı, mükerrer veri olmaması
gibi. Veri tabanlarını yaratmak ve erişimi sağlamak, bakımlarını yapmnak için Veri Tabanı
Yönetim Sistemi (DBMS) denen yazılımlar mevcuttur.
C++ bir dosyayı baytlar sırası (“sequence of bytes”) olarak görür. Her dosya, dosya sonunu belirten bir işaret ile biter.
C++’da dosya işlemleri yapabilmek için <iostream> ve <fstream> başlık dosyaları dahil edilmelidir.
<fstream> başlık dosyası ifstream (giriş işlemleri için) ve ofstream (çıkış işlemleri için) gerekli sınıf (“class”) tanımlarını içerir.
SIRALI DOSYA YARATMA
Örnek: Her müşteri için hesap no, müşter, adı, bakiyesi, vb. tutulacak. Program, kullanıcının kayıtları hesap no sırasında gireceğini varsaymaktadır
.
// fig. 14.4 s.761
// create a sequential file
#include <iostream>
using std::cout;
using std::cin;
using std::ios;
using std::cerr;
using std::endl;
#înclude <fstream>
using std::ofstream;
#include <cstdlib>
int main() {
ofstream MusteriDosyasi(“musteri.dat”, ios::out ); // musteri.dat çıkış // için açılır (open) if (!MusteriDosyasi) // dosya düzgün açılırsa 0 döndürür
{
cerr << “Dosya açılamadı” << endl;
exit(1); // sıfır harici bir değer programın bir hata sonucu bittiğini gösterir. Bu değer op.sys’e ...
}
cout << “Hesap no, Ad, Bakiye bilgilerini giriniz.\n”
<< “Girişi bitirmek için EOF giriniz.\n?”;
int hesap;
char ad[20];
double bakiye;
while (cin >> account >> ad >> bakiye ) //EOF olmadıkça bu koşul doğrudur {
MusteriDosyasi << hesap << „ „ << ad << „ „ << bakiye << „\n‟;
cout << “? “;
}
return 0; // EOF olunca main biter. Musteri.dat dosyası kapatılır.
}
Hesap no, Ad, Bakiye bilgilerini giriniz.
Girişi bitirmek için EOF giriniz.
? 100 Jones 24.98 ? 200 Doe 345.67 ? 300 White 0.00 ? 400 Stone –42.16 ? 500 Rich 224.62 ? ^Z
Dosya açma durumları (“file open modes”)
ios::app dosyanın sonuna ekle ios::in dosyayı giriş için aç ios::out dosyayı çıkış için aç ios::trunc dosyanın içeriğini at
ios::binary dosyayı ikili (metin olmayan) giriş çıkış için aç
SIRALI DOSYADAN OKUMA YAPMA (“reading from sequential access file”)
// fig. 14.7 s. 766-767 .
.
#include <fstream>
. .
void satiryazdir(int, const char * const, double);
int main() {
ifstream MusteriDosyasi(“musteri.dat”, ios::in ); // ios::in konulmasa da olur
if ( !MusteriDosyasi) {
cerr << “Dosya açılamadı\n”;
exit(1);
}
int hesap;
char ad[20];
double bakiye;
cout << setiosflags( ios::left ) setw( 10) << “HESAP NO”
<< setw(13) << “ADI”
<< BAKİYE\n” << setiosflags( ios::fixed | ios::showpoint );
while ( MusteriDosyasi >> hesap >> ad >> bakiye ) satiryazdir(hesap, ad, bakiye);
return 0;
}
void satiryazdir(int hes, const char * char ad, double bak) {
cout << setiosflags( ios::left ) setw( 10) << hes << setw(13) << ad << setw(7) << setprecision(2) << resetiosflags( ios::fixed ) << bal << „\n‟;
}
HESAP NO ADI BAKIYE 100 Jones 24.98 200 Doe 345.67 300 White 0.00 400 Stone -42.16
500 Rich 224.62
---
ss. 768-769-770-771 atlandıad is a char
pointer to a
const char
SIRALI ERİŞİM DOSYALARININ GÜNCELLENMESİ
Yukarıda yaratılmış olan
musteridosyasındaki bir veri değiştirilmek (güncellenmek) istendiğinde diğer verilerin bozulma riski vardır. Örneğin White ismini Worthington yapmak istersek, bu isim orijinal isim olan Whhite’dan 6 karakter daha uzun olduğundan kendisinden sonra gelen kaydı bozar. Bunu önlemek için 300 White 0.00’a kadar olan kayıtlar yeni bir dosyaya kopyalanıp 300 Worthington 0.00 yazılıp geri kan kayıtlar da yeni dosyaya aktarılabilir. Bir seferde çok sayıda kayıt güncellenecekse bu yöntem kabul edilebilir.
RASGELE ERİŞİMLİ DOSYALAR
Belirli bir kaydın hemen bulunmasu gerektiği durumlarda kullanılır. Bankada belirli bir müşteri hesabına (dosyada daha önce gelen müşteri kayıtları okunmadan ) erişilmesi, kayıt sırasında belirli bir öğrenci numarasına daha önceki tüm numaralar okunmadan erişilmesi gibi.
C++ dosyalar üzerinde bir yapı oluşturmadığı için bunu programcı yapmalıdır. Rasgele erişimli dosya yaratmak için pek çok teknik vardır. Bunların en kolayında, tüm kayıtların sabit uzunluklu olması tercih edilir / gerekir. Kayıtların sabit uzunluklu olması aranan bir kaydın dosyanın başına göre kaçıncı bayttan (“byte offset”) başladığını hesaplamamızı mümkün kılar.
RASGELE ERİŞİMLİ DOSYALARI YARATMAK
Bu tür dosyalara genellikle class veya struct ile tanımlanmış bir grup alanı bir anda yazdırırız, tek bir sayıyı değil.
Örnek: 100 müşteri için 100 sabit uzunluklu kayıt tutulacak. Her kayıtta hesap no (kayıt anahtarı), ad, soyad, bakiye alanları bulunacak. Program, bir hesabı güncelleyebilmeli, yeni hesap ekleyebilmeli, hesap silebilmeli ve tüm hesapları listeleyebilmeli.
Aşağıda rasgele erişimli bir dosyanın nasıl açıldığını, struct ile kayıt yapısının nasıl tanımlandığı (
musveri.hadlı başlık dosyasında) gösterilmektedir. Program öncelikle 100 kayda boş struct’lar yazarak kayıtların ik değer atamalarını yapmaktadır. Boş struct’da hesap no için 0, ad ve soyad için “null” dizgi, ve bakiye için 0.0 vardır.
// musveri.h
// struct musteri’nin tanımı
#ifndef MUSVERI_H
#define MUSVERI_H
Header file musveri.h
struct musteri
{ aşağıdaki tüm programlarda
int hesapno; bu başlık dosyası kullanılacak char soyad[15];
char ad[10];
double bakiye;
};
#endif
// fig.14.11 s.774
// creating random-access file sequentially
#include <iostream>
using std::cerr;
using std::endl;
using std::ios;
#include <fstream>
using std::ofstream;
#include <cstdlib>
#include “musveri.h”
int main() {
ofstream kredi(“kredi.dat”, ios::binary); // kredi.dat file is associated with // ofstream object kredi
if ( !kredi ) {
cerr << “kredi dosyası açılamadı” << endl;
exit(1);
}
musteri bosmusteri = { 0, “”, “”, 0.0 };
for ( int i = 0; i <100, i++ ) kredi.write(
reinterpret_cast<const char *>( &bosmusteri ), sizeof(musteri));
return 0;
}
kredi.write(
reinterpret_cast<const char *>( &bosmusteri ), sizeof(musteri));
cümlesinin anlamı:
kredi.dat ile ilişkilendirilmiş kredi nesnesine (“object”) musteri yapısı büyüklüğündeki bosmusteri yapısını yazar.
NOTE THAT the first argument of write function must be of type const char *.
However, the data type of &bosmusteri is musteri *.
To convert &bosmusteri to the appropriate pointer type, the expression
reinterpret_cast<const char *>( &bosmusteri )
uses the cast operator reinterpret_cast to convert the address of
bosmusterito a
const char *,
so the call to write compiles without issuing a syntax error.
RASGELE-ERİŞİMLİ DOSYAYA VERİLERİ RASGELE YAZMA
Aşağıdaki program kredi.dat dosyasına veri yazar. Dosyaya verileri tam yerlerine yazmak için ofstream fonksiyonları write ve seekp kullanır. Fonksiyon seekp, dosyaya yazıacak kaydın tam yerini ayarlar ve write verileri yazar.
// fig.14.12 s.775
// writing to a random access file
#include <iostream>
using std::cerr;
using std::endl;
using std::ios;
using std::cin;
using std::cout;
#include <fstream>
using std::ostream;
#include <cstdlib>
#include “musveri.h”
int main() {
ofstream kredi(“kredi.dat”, ios::binary); // kredi.dat file is associated with // ofstream object kredi
if ( !kredi ) {
cerr << “kredi dosyası açılamadı” << endl;
exit(1);
}
cout << “Hesap numarasını ( 1 ile 100 arasında )giriniz. “ << “Bitirmek için 0 giriniz.\n? “;
musteri mus;
cin >> mus.hesapno;
while ( mus.hesapno > 0 && mus.hesapno <= 100 ) {
cout << “Ad, soyad ve bakiye giriniz.\n? “;
cin >> mus.ad >> mus.soyad >> mus.bakiye;
// kredi dosyasında gerekli adrese konumlan
kredi.seekp( ( mus.hesapno – 1 ) * sizeof (musteri) );
kredi.write(
reinterpret_cast<const char *>(&mus), sizeof(musteri) );
cout << “Hesap numarasını giriniz:\n? “;
cin >> mus.hesapno;
}
return 0;
}
Hesap numarasını ( 1 ile 100 arasında )giriniz.Bitirmek için 0 giriniz.
? 37
Ad, soyad ve bakiyeyi giriniz:
? Bakır Deniz 0.00
Hesap numarasını giriniz:
? 29
Ad, soyad ve bakiyeyi giriniz:
? Baki Naci –24.54
Hesap numarasını giriniz:
? 96
Ad, soyad ve bakiyeyi giriniz:
? Sarı Sami 34.98
Hesap numarasını giriniz:
? 88
Ad, soyad ve bakiyeyi giriniz:
? Simit Davut 258.34 Hesap numarasını giriniz:
? 0
kredi.seekp( ( mus.hesapno – 1 ) * sizeof (musteri) );
Parantez içindeki aritmetik ifade, yazılacak yadın, dosya içine baştan kaçıncı bayttan başlacağını hesaplar. Musteri 50 bayt ise, hesapno 4 ise, hesapno 4 olan kayıt 200.bayttan itibaren yazılacak demektir. Dosya konumlandırma işaretçisi (“file position pointer”) bu adrese konumlanır.
RASGELE-ERİŞİMLİ BİR DOSYAYI SIRALI OKUMA s.777
Daha önce yarattığımız rasgele-erişimli dosyayı sıralı okuyacağız yani ilk kayıttan başlayarak sırayla her kaydı. Öncelikle birkaç önmeli konuya bakalım:
ifstream fonksiyonu read belirlenen bayt uzunluğundaki veriyi dosyadan okur. Örneğin
kredi.read(reinterpret_cast<char *>( ?mus ), sizeof(musteri) );
=
kredidosyasından
musteriuzunluğunda baytı okuyup
musyapısına koyar.
NOT:
readfonksiyonunun ilk argümanı
char *olmak durumundadır.
Aşağıdaki program
kredi.datdosyasındaki bütün kayıtları sırayla okur ve okunan her bir kaydın veri içerip içermediğine bakar. Döngü koşulunu inceleyecek olursak:
while ( kredi && !kredi.eof() )
dosya sonuna gelip gelinmediğini anlamak için
eoffonksiyonunu kullanır. Dosya sonuna gelindiyse döngüden çıkılır. İkinci bir koşul da
&&işaretinin sol tarafında yer alan
kredisözcüğüyle belirtilmiştir.
kredidosyası okunurken bir hata oluşursa bu noktaya
falsedöner ve yine döngüden çıkılır.
OutputLine
fonksiyonunun 2 argümanına dikkat edelim:
1- ostream objesi 2-musteri yapısı (structure).
Görüldüğü gibi,
coutgibi bir
ostreamobjesini argüman olarak vermek mümkündür.
Program: Rasgele erişimli dosyasını sıralı okunması
// fig. 14.14 reading a random access file sequentially
#include <iostream>
using std::cerr;
using std::endl;
using std::ios;
using std::cout;
#include <iomanip>
using std::setprecision;
using std::setiosflags;
using std::resetiosflags;
using std::setw;
#include <fstream>
using std::ifstream;
using std::ostream;
#include <cstdlib>
#include “musveri.h”;
void outputLine( ostream&, const clientData & );
int main() {
ifstream kredi(“kredi.dat”, ios::in); // kredi.dat file is associated with // ifstream object kredi if ( !kredi )
{
cerr << “kredi dosyası açılamadı” << endl;
exit(1);
}
cout << setioflags( ios:: left ) << setw (10) << “HESAP NO”
<< setw(16) << “SOYADI” << setw(11) << “ADI” << resetiosflags(ios::left) << setw(10) << “BAKİYE” << endl;
musteri mus;
kredi.read(reinterpret_cast<char *>( &mus ), sizeof( musteri ));
while ( kredi && !kredi.eof() ) {
if (mus.hesapno != 0 ) outputLine ( cout, mus);
kredi.read(reinterpret_cast<char *>( &mus ),
sizeof( musteri ));
}
return 0;
}
void outputLine( ostream &output, const musteri &c) {
output << setiosflags( ios::left ) << setw (10) << c.hesapno << setw(16) << c.soyad
<< setw(11) << c.ad << setw(10)
<< setprecision(2) << resetiosfalgs( ios::left) << setiosflags( ios::fixed | ios::showpoint) << c.bakiye << „\n‟;
}
HESAP NO SOYADI ADI BAKİYE 29 Baki Naci -24.54 33 Dakik Selin 314.33 37 Bakır Deniz 0.00 88 Simit Davut 258.34 96 Sarı Sami 34.98