Skip to content

Commit ac94d0e

Browse files
committed
Updated document
1 parent 41307ca commit ac94d0e

File tree

3 files changed

+181
-4
lines changed

3 files changed

+181
-4
lines changed

fops/src/io_ops.rs renamed to fops/src/file_io_ops.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ pub fn write_games_to_file(games: &[Game]) -> io::Result<()> {
4646
pub fn read_games_from_file() -> io::Result<Vec<String>> {
4747
let mut games = Vec::new();
4848
let f = File::open("games.dat")?;
49-
for line in io::BufReader::new(f).lines() {
49+
for line in BufReader::new(f).lines() {
5050
games.push(line?);
5151
}
5252
Ok(games)

fops/src/main.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
use crate::data::load_games;
2+
use crate::file_io_ops::*;
23
use crate::game::Game;
3-
use crate::io_ops::*;
44

55
mod data;
6+
mod file_io_ops;
67
mod game;
7-
mod io_ops;
88
mod std_ops;
99

1010
fn main() -> std::io::Result<()> {

io.md

Lines changed: 178 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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

215217
Aş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

Comments
 (0)