Cél és előfeltétel

A fájl neve és a fájl tartalma két külön dolog. Egy útvonal összeállítható úgy is, hogy semmi nem található rajta, és egy létező fájl olvasása is meghiúsulhat. Ebben a leckében külön kezeled az útvonal szerkezetét, a beolvasást és a kapott szöveget. A cél egy olyan kis műveletsor, amelyben a hibák eredete nem mosódik össze.

Előfeltétel a standard bemenet és kimenet, illetve az argumentumok feldolgozása. A megoldások teljes edition 2024 programok. Az első kettő csak memóriabeli adatot vizsgál, a harmadik saját, újonnan létrehozott ideiglenes fájlt használ. Nem kell a gépeden meglévő dokumentumot módosítanod a lecke elvégzéséhez.

Útvonalat útvonalként építs

A Path kölcsönzött útvonalnézet, a PathBuf saját, módosítható útvonal. Hasonló kapcsolatot láttál már a szövegnézet és a saját String között. A join egy új útvonalat épít, így nem kell kézzel perjeleket szúrnod a részek közé. Az elválasztó és néhány útvonalviselkedés platformfüggő, ezért a szemantikát útvonalobjektumokkal ellenőrizzük, nem egyetlen operációs rendszer megjelenített karakterláncával.

A join nem biztonsági szűrő, és nem ellenőrzi a létezést. Egy abszolút útvonalrész az alapot is lecserélheti, a szülőre mutató elemek pedig külön jelentést hordoznak. Most rögzített, ismert relatív részekkel dolgozunk. Ha később felhasználói fájlnevet kell egy engedélyezett könyvtár alá zárnod, ahhoz további szerződés és ellenőrzés szükséges; a puszta összefűzés ezt nem bizonyítja.

Megjelenítés nem visszaalakítás

Egy útvonal nem minden környezetben érvényes UTF-8 szöveg. A megjelenítéshez használt veszteséges alak helyettesítő karaktereket is tartalmazhat, ezért azt nem szabad megbízható eredeti útvonalként visszaforgatni a fájlműveletbe. A fájlt közvetlenül Path referenciával nyisd meg. Az embernek szóló kijelzés és az operációs rendszernek átadott eredeti adat szerepe külön maradjon.

A fájlkiterjesztés is csak az útvonal szerkezeti része. Egy txt kiterjesztés nem garantál érvényes szövegkódolást, és nem bizonyítja a tartalom jelentését. A második feladat ezért bájtokból olvas UTF-8 szöveget, és hibás kódolást is kipróbál. A nevétől függetlenül csak az olvasási eredményből derül ki, hogy String készíthető-e belőle.

Olvasás eredményként kezelt hibával

A read_to_string teljes tartalmat olvas Stringbe, ezért a bemenetnek érvényes UTF-8 szövegnek kell lennie. Fájl esetén a megnyitás, a hozzáférés vagy az olvasás is hibázhat. A Result ezt nem keveri össze az üres, de sikeresen olvasott szöveggel. A kérdőjel a hibát továbbadja a hívónak, amely eldöntheti, hogyan jelenítse meg és milyen kilépési állapottal jelezze.

A teljes fájl beolvasása kis bemenetekhez egyszerű, nagy adatoknál viszont a memóriaigényt is figyelembe kell venni. A lecke pár bájtos saját példát használ, így itt a fájlméret nem bizonytalan külső tényező. A soronként feldolgozó eszköz a következő miniprojektben már más olvasási megoldást választhat. Az olvasási stratégia a várható adatmennyiség része, nem pusztán ízléskérdés.

1. feladat: útvonal és fájlnév részei

Építs adatok nevű relatív könyvtár alatt napi.txt útvonalat. Ellenőrizd a fájlnevet, a kiterjesztést és a szülőt. A program ne nyissa meg ezt az útvonalat: most csak a szerkezetet vizsgáljuk. Figyeld meg, hogy az ellenőrzések akkor is lefutnak, ha az adatok könyvtár a gépeden nem létezik.

Megoldás
use std::ffi::OsStr;
use std::path::{Path, PathBuf};
fn main() {
    let alap = PathBuf::from("adatok");
    let fajl = alap.join("napi.txt");
    assert_eq!(fajl.file_name(), Some(OsStr::new("napi.txt")));
    assert_eq!(fajl.extension(), Some(OsStr::new("txt")));
    assert_eq!(fajl.parent(), Some(Path::new("adatok")));
    assert_eq!(alap, PathBuf::from("adatok"));
}

A join új értéket ad, az alapot nem írja át. A kiterjesztés eredménye operációs rendszer szerinti szövegnézet, nem kényszerített UTF-8 String.

2. feladat: szövegolvasás memóriabeli forrásból

Készíts olvasó függvényt, amely Read képességű forrást fogad, és String eredményt ad. Ellenőrizd az érvényes rövid szöveget, az üres adatot és egy hibás UTF-8 bájtot. A hibás kódolás ne váljon csendben más tartalommá. A feladat a szövegolvasási szabályt vizsgálja, ezért nincs szükség valódi fájlra.

Megoldás
use std::io::{self, Cursor, Read};
fn szoveg(mut forras: impl Read) -> io::Result<String> {
    let mut eredmeny = String::new();
    forras.read_to_string(&mut eredmeny)?;
    Ok(eredmeny)
}
fn main() -> io::Result<()> {
    assert_eq!(szoveg(Cursor::new(b"alma\n"))?, "alma\n");
    assert_eq!(szoveg(Cursor::new(b""))?, "");
    let hiba = szoveg(Cursor::new([0xff])).unwrap_err();
    assert_eq!(hiba.kind(), io::ErrorKind::InvalidData);
    Ok(())
}

Az üres String sikeres eredmény, az érvénytelen kódolás hiba. Ez a kettő külön marad akkor is, ha a hívó végül mindkettőhöz rövid üzenetet szeretne rendelni.

3. feladat: saját ideiglenes fájl visszaolvasása

Hozz létre egy új ideiglenes fájlt, írj bele ismert adatot, majd olvasd vissza teljes szövegként. A létrehozás ne írjon felül már meglévő fájlt. Csak a saját sikeresen létrehozott fájlt töröld a próba végén. Az olvasási eredményt a takarítás után hasonlítsd össze, így a szokásos állításhiba sem hagyja ott a fájlt.

Megoldás
use std::fs::{self, OpenOptions};
use std::io::{self, Write};
use std::time::{SystemTime, UNIX_EPOCH};
fn main() -> io::Result<()> {
    let pillanat = SystemTime::now().duration_since(UNIX_EPOCH).expect("óra").as_nanos();
    let nev = format!("rusthu-kezdok-{}-{pillanat}.txt", std::process::id());
    let utvonal = std::env::temp_dir().join(nev);
    let mut fajl = OpenOptions::new().write(true).create_new(true).open(&utvonal)?;
    let iras = fajl.write_all("saját próba\n".as_bytes());
    drop(fajl);
    let olvasott = if iras.is_ok() { fs::read_to_string(&utvonal) } else { Err(iras.unwrap_err()) };
    fs::remove_file(&utvonal)?;
    assert_eq!(olvasott?, "saját próba\n");
    assert!(!utvonal.exists());
    Ok(())
}

A create_new névütközéskor hibázik, nem ír felül idegen tartalmat. A példa takarítása a szokásos siker- és visszaadott hibautat kezeli; a folyamat kényszerű megszakítása után maradhat ideiglenes fájl.

Elfogadási feltételek

  • Az útvonalépítés a megfelelő fájlnevet, kiterjesztést és szülőt adja az alap módosítása nélkül.
  • Az üres olvasás sikeres, az érvénytelen UTF-8 adat InvalidData hibával tér vissza.
  • A próba csak új saját fájlt hoz létre, az ismert tartalmat pontosan visszaolvassa, és azt a fájlt eltávolítja.

Továbblépés

Az adatforrás és az adatfeldolgozás különválasztása után a program szerkezetét is rendezheted. A modulok és láthatóság leckében szűk, jól használható felület mögé helyezed a belső segédfüggvényeket.