@@ -210,8 +210,185 @@ Buraya kadar ele aldığımız stdin ve stdout kullanımlarına ilişkin şunlar
210210- ** Unix/Linux** ortamlarında ` | ` , ` > ` ve ` >> ` operatörleri standart giriş-çıkış işlemlerinde, verilerin akışını
211211 yönetmek için kullanılır.
212212
213+ ---
214+
213215## Temel Dosya I/O İşlemleri
214216
215217Aşağıdaki örneklerde dosya okuma ve yazma işlemleri farklı şekillerde ele alınmaktadır.
216218
217- _ NotYetImplemented();_
219+ ### Dosya Oluşturma ve İçerik Yazma _ (create)_
220+
221+ Aşağıdaki fonksiyon parametre olarak Game türünden bir dizi alır. Bu dizinin her bir elemanı için satır satır akan bir
222+ String içerik üretilir. Söz konusu içerik contents isimli değişkende toplanır. f değişkeni games.dat isimli bir dosyayı
223+ temsil eder. Dosya create metodu ile oluşturulur. Create metodu dosya yoksa yeni bir tane oluşturur ama varsa truncate
224+ işlemini icra eder, bir başka deyişle içeriğini sıfırlar.
225+
226+ ``` rust
227+ pub fn write_games_to_file (games : & [Game ]) -> io :: Result <()> {
228+ let mut contents = String :: new ();
229+ for g in games {
230+ contents . push_str (& g . to_string ());
231+ contents . push_str (" \ n" );
232+ }
233+ let mut f = File :: create (" games.dat" )? ;
234+ f . write_all (contents . as_bytes ())? ;
235+ Ok (())
236+ }
237+ ```
238+
239+ ### Dosya İçeriğini Okuma
240+
241+ Aşağıdaki örnek kod ** games.dat** isimli dosyanın içeriğini satır satır okuyarak ** String** türünden bir vector'de
242+ toplar. Burada satır bazında okuma işlemi yapmak için ** BufReader** nesnesi kullanılmıştır. ** BufReader** esasında bu
243+ örnek için oldukça maliyetlidir. Genel olarak ** TCP Stream** 'lerin okunması gibi işlemlerde ** BufReader** kullanmak daha
244+ mantıklıdır.
245+
246+ ``` rust
247+ pub fn read_games_from_file () -> io :: Result <Vec <String >> {
248+ let mut games = Vec :: new ();
249+ let f = File :: open (" games.dat" )? ;
250+ for line in BufReader :: new (f ). lines () {
251+ games . push (line ? );
252+ }
253+ Ok (games )
254+ }
255+ ```
256+
257+ Yukarıdaki fonksiyondan yararlanılarak dosya içerisinde yer alan oyun bilgilerinin ** |** işaretine göre ayrıştırılıp
258+ ** Game** türünden bir ** vector** halinde ele alınması da mümkündür. Bunun için aşağıdaki gibi bir fonksiyondan
259+ yararlanılabilir.
260+
261+ ``` rust
262+ pub fn read_games_to_vec () -> io :: Result <Vec <Game >> {
263+ let mut games = Vec :: new ();
264+
265+ for line in read_games_from_file ()? {
266+ let cols : Vec <& str > = line . split ('|' ). collect ();
267+ if cols . len () != 3 {
268+ return Err (io :: Error :: new (
269+ io :: ErrorKind :: InvalidData ,
270+ format! (" Beklenmeyen sütun sayısı: `{}`" , line ),
271+ ));
272+ }
273+
274+ let title = cols [0 ]. to_string ();
275+ let year = cols [1 ]
276+ . parse :: <u16 >()
277+ . map_err (| e | io :: Error :: new (io :: ErrorKind :: InvalidData , e ))? ;
278+ let popularity = cols [2 ]
279+ . parse :: <f32 >()
280+ . map_err (| e | io :: Error :: new (io :: ErrorKind :: InvalidData , e ))? ;
281+
282+ games . push (Game {
283+ title ,
284+ year ,
285+ popularity ,
286+ });
287+ }
288+
289+ Ok (games )
290+ }
291+ ```
292+
293+ Yukarıdaki iki operasyon tek bir metot haline de getirilebilir. İlk olarak path parametresi üzerinden gelen dosya
294+ açılmaya çalışılır. Operasyon sonrasında hata olması durumu söz konusudur ve bu ** ?** operatörü ile ele alınarak ** error
295+ propagation** ile çağıran yere doğru gönderilir. Dosya içeriğini satır bazından okumak için ** BufReader** nesnesi
296+ kullanılır. Bu nesne oluşturulurken bir file nesnesi aldığına dikkat edilmelidir. ** BufReader** üzerinden ulaşılan lines
297+ metodu satır bazında okuma yapılmasını sağlar. Döngünün her iterasyonunda dosyadan bir satır okunur. Bu işlem okunabilir
298+ satır kalmayıncaya kadar devam eder. İlgili kontrol is_empty çağrısı ile gerçekleştirilmektedir. Game nesnesnin dosya
299+ içerisindeki tutuluş biçimine göre ** |** işaretleri ile ayrılmış 3 kolon olması gerekmektedir. Bu durum kontrol edilir
300+ ve hatalı kolon olması halinde geriye bir Error döndürülür. Buradaki akış tamamen stratejiye bağlıdır. Hatalı kolonların
301+ olduğu satırları atlayarak devam etmek de bir seçenektir.
302+
303+ Kolonlar elde edilikten sonra bazı dönüştürme işlemleri de icra edilir ve bunlarda da ** error propagation** tekniği
304+ kullanılır.
305+
306+ ``` rust
307+ pub fn read_games_buffered_into_vec (path : & str ) -> io :: Result <Vec <Game >> {
308+ let file = File :: open (path )? ;
309+ let reader = BufReader :: new (file );
310+ let mut games = Vec :: new ();
311+ for line in reader . lines () {
312+ let line = line ? ;
313+ if ! line . is_empty () {
314+ let cols : Vec <& str > = line . split ('|' ). collect ();
315+ if cols . len () != 3 {
316+ return Err (io :: Error :: new (
317+ io :: ErrorKind :: InvalidData ,
318+ format! (" Beklenmeyen sütun sayısı: `{}`" , line ),
319+ ));
320+ }
321+ let title = cols [0 ]. to_string ();
322+ let year = cols [1 ]
323+ . parse :: <u16 >()
324+ . map_err (| e | io :: Error :: new (io :: ErrorKind :: InvalidData , e ))? ;
325+ let popularity = cols [2 ]
326+ . parse :: <f32 >()
327+ . map_err (| e | io :: Error :: new (io :: ErrorKind :: InvalidData , e ))? ;
328+
329+ games . push (Game {
330+ title ,
331+ year ,
332+ popularity ,
333+ });
334+ }
335+ }
336+ Ok (games )
337+ }
338+ ```
339+
340+ ### Dosyaya Veri Yazma
341+
342+ Bir dosyaya veri yazma işlemi aslında içeriğin bir byte array olarak aktarılmasından ibarettir. Aşağıdaki örnek
343+ fonksiyonu ele alalım.
344+
345+ ``` rust
346+ pub fn write_games_to_file (games : & [Game ]) -> io :: Result <()> {
347+ let mut contents = String :: new ();
348+ for g in games {
349+ contents . push_str (& g . to_string ());
350+ contents . push_str (" \ n" );
351+ }
352+ let mut f = File :: create (" games.dat" )? ;
353+ f . write_all (contents . as_bytes ())? ;
354+ Ok (())
355+ }
356+ ```
357+
358+ Bu fonksiyon Game nesnelerinden oluşan bir diziyi parametre olarak alır. Her bir oyun değişkeni için içeriği | ile
359+ ayıran bir string üretilir ve bunlar contents isimli String değişkende toplanır. Satır bazında ayrıştırılarak tutulan
360+ içerik as_bytes metodu ile byte array'a çevrilip tek seferde games.dat isimli dosyaya yazdırılır.
361+
362+ Yazma işlemi BufWriter enstrümanını ile de gerçekleştirilebilir. Aşağıdaki kod parçasında bu durum ele alınmaktadır. Çok
363+ büyük blokların tek seferde yazılmasından ziyade in-memory olarak tutulan içeriklerin küçük bloklar halinde yazılması
364+ adına daha verimlidir. Yazma operasyonu aynı kaynağa doğru ele alınır. Bir dosya veya network'e yazma en çok kullanılan
365+ senaryolardandır. Yazma işlemi tamamlandığında bellekte kalmış olabilecek veri kalıntılarının da tamamen aktarıldığından
366+ emin olmak gerekir. Bunun için flush komutu kullanılır.
367+
368+ ``` rust
369+ pub fn write_games_buffered (path : & str , games : & [Game ]) -> io :: Result <()> {
370+ let file = File :: create (path )? ;
371+ let mut writer = BufWriter :: new (file );
372+ for game in games {
373+ writeln! (writer , " {}" , game )? ;
374+ }
375+ writer . flush ()? ;
376+ Ok (())
377+ }
378+ ```
379+
380+ ### Var Olan Dosya İçeriklerine Ek Yapmak
381+
382+ Çoğu zaman var olan dosya içeriklerine ilaveler yapılır. Söz gelimi log biriktiren dosyalar veya oyunun son durumunu
383+ tutan dosyalar bunlara örnek olarak verilebilir. Bu gibi senaryolarda ** OpenOptions** türünü kullanarak dosyanın hangi
384+ modda oluşturulacağı belirtilebilir. Aşağıdaki örnekte path değişkeni üzerinden gelen dosyanın ** append** modda
385+ açılacağı belirtilir. Buna göre dosyanın sonuna ekleme yapılacağı söylenir. Yukarıda gerçekleştirilen birçok operasyonda
386+ doğrudan File nesnesine erişmek yerine ** OpenOptions** enstrümanı ile de ilerlenebilir.
387+
388+ ``` rust
389+ pub fn append_game_to_file (path : & str , game : & Game ) -> io :: Result <()> {
390+ let mut file = OpenOptions :: new (). append (true ). create (true ). open (path )? ;
391+ writeln! (file , " {}" , game )? ;
392+ Ok (())
393+ }
394+ ```
0 commit comments