Bekerja dengan berkas pada Zig 0.17 berarti bekerja dengan std.Io.Dir, yaitu tipe pegangan untuk satu direktori beserta seluruh operasi berkas di bawahnya. Topik ini membahas pembuatan pegangan, penulisan dan pembacaan berkas, serta penelusuran isi direktori dengan iterator. Setelah membacanya, program dapat menyimpan catatan ke berkas, membacanya kembali, dan mendaftar isi sebuah direktori beserta jenis setiap entrinya.
Konsep#
Dir sebagai pegangan direktori#
std.Io.Dir adalah tipe yang mewakili satu direktori terbuka dan menjadi titik masuk seluruh operasi berkas pada jalurnya. Pegangan direktori kerja diperoleh dari std.Io.Dir.cwd(), sedangkan subdirektori dibuka dengan openDir(io, sub_path, options) yang menerima instance io dan struct opsi. Opsi .iterate = true diperlukan apabila isi direktori hendak ditelusuri, karena tanpanya panggilan iterate merupakan illegal behavior. Setiap pegangan ditutup dengan close(io), dan pola defer membuat penutupan tetap terjadi meskipun fungsi keluar lebih awal karena error.
Seluruh operasi pada sebuah Dir menafsirkan path relatif terhadap direktori yang dipegangnya, bukan terhadap direktori kerja proses. Panggilan arsip.createFile(io, "baca.txt", .{}) karena itu menulis ke dalam direktori arsip, meskipun program dijalankan dari tempat lain. Pola ini menghilangkan penyusunan path secara manual, karena pegangan induk sudah membawa posisi awalnya, dan jalur panjang cukup dibaca sebagai rangkaian pegangan yang berlapis.
// tidak-dijalankan
var d = try std.Io.Dir.cwd().openDir(io, ".", .{ .iterate = true });
defer d.close(io);Direktori baru dibuat dengan createDirPath(io, sub_path), yang sekaligus membuat seluruh induknya bila belum ada. Fungsi ini mengembalikan sukses pula ketika path yang diminta sudah berupa direktori, sehingga program dapat dijalankan berulang tanpa perlu memeriksa keberadaannya lebih dahulu. Untuk pembuatan satu direktori dengan pengaturan izin tertentu, tersedia createDir yang menerima parameter Permissions, sedangkan createDirPath memakai izin bawaan.
Menulis dan membaca berkas#
Berkas dibuat melalui createFile(io, sub_path, flags). Opsi truncate bernilai true secara bawaan, sehingga berkas lama dikosongkan setiap kali dibuat ulang, sedangkan opsi read menambahkan hak baca pada pegangan yang dihasilkan. Pembukaan berkas yang sudah ada tanpa pembuatan ulang memakai openFile, yang secara bawaan membuka dalam mode baca-saja, dengan opsi mode berupa .read_write atau .write_only untuk mengubahnya. Sama seperti Writer untuk stdout, penulisan berkas memakai buffer pemanggil, yaitu berkas.writer(io, &wbuf) menghasilkan pembungkus, dan flush wajib dipanggil sebelum berkas ditutup.
Program berikut menyimpan dua catatan suhu ke berkas catatan.txt, kemudian membacanya kembali dan menampilkan isinya ke stdout.
const std = @import("std");
pub fn main(init: std.process.Init) !void {
const io = init.io;
const arena = init.arena.allocator();
var out_buf: [512]u8 = undefined;
var stdout_writer = std.Io.File.stdout().writer(io, &out_buf);
const out = &stdout_writer.interface;
const dir = std.Io.Dir.cwd();
var berkas = try dir.createFile(io, "catatan.txt", .{ .truncate = true });
defer berkas.close(io);
var wbuf: [128]u8 = undefined;
var berkas_writer = berkas.writer(io, &wbuf);
const w = &berkas_writer.interface;
try w.writeAll("07.00 suhu 27.5\n");
try w.writeAll("13.00 suhu 29.1\n");
try w.flush();
const isi = try dir.readFileAlloc(io, "catatan.txt", arena, .unlimited);
try out.print("{d} byte terbaca kembali:\n", .{isi.len});
try out.writeAll(isi);
try out.flush();
}32 byte terbaca kembali:
07.00 suhu 27.5
13.00 suhu 29.1Panggilan createFile menghasilkan berkas baru dengan isi kosong karena opsi truncate, lalu dua baris catatan diisi ke buffer dan dikirim dengan satu panggilan flush. Metode readFileAlloc(io, sub_path, allocator, limit) membaca seluruh isi berkas ke memori yang dialokasikan, dan slice yang dikembalikan dimiliki pemanggil, sehingga arena dari init.arena menutup kasus pembersihannya. Alternatifnya adalah membuka pegangan berkas dengan openFile lalu memakai reader(io, &rbuf).interface dengan pola Reader yang sama pada penulisan.
Jalankan program dari direktori tempat catatan-harian.zig berada, dan berkas catatan.txt akan tercipta di direktori tersebut.
# jalankan program dari direktori topik
zig run catatan-harian.zig32 byte terbaca kembali:
07.00 suhu 27.5
13.00 suhu 29.1Contoh Lengkap#
Menelusuri isi direktori#
Pendaftaran isi direktori dimulai dari iterate() yang menghasilkan iterator, kemudian next(io) dipanggil berulang sampai mengembalikan null. Setiap entri bertipe std.Io.Dir.Entry yang memuat name, kind, dan inode. Medan kind bertipe enum std.Io.File.Kind dengan tag seperti .file, .directory, dan .sym_link, sehingga jenis entri dapat dibedakan tanpa membuka entrinya. Nama tag dicetak dengan @tagName.
Urutan entri yang dikirim iterator mengikuti urutan internal sistem berkas dan tidak dijamin alfabetis. Program berikut menyusun ulang daftarnya dengan std.mem.sort sebelum dicetak agar keluarannya tetap sama di setiap sistem. Isi direktori arsip dibuat oleh program itu sendiri, sehingga daftar yang dihasilkan sepenuhnya deterministik.
const std = @import("std");
const Entri = struct {
nama: []u8,
kind: std.Io.File.Kind,
};
fn urutNama(_: void, a: Entri, b: Entri) bool {
return std.mem.order(u8, a.nama, b.nama) == .lt;
}
pub fn main(init: std.process.Init) !void {
const io = init.io;
const arena = init.arena.allocator();
var out_buf: [512]u8 = undefined;
var stdout_writer = std.Io.File.stdout().writer(io, &out_buf);
const out = &stdout_writer.interface;
const dir = std.Io.Dir.cwd();
try dir.createDirPath(io, "arsip");
var arsip = try dir.openDir(io, "arsip", .{ .iterate = true });
defer arsip.close(io);
var berkas = try arsip.createFile(io, "baca.txt", .{ .truncate = true });
defer berkas.close(io);
var wbuf: [64]u8 = undefined;
var berkas_writer = berkas.writer(io, &wbuf);
try berkas_writer.interface.writeAll("catatan pertama\n");
try berkas_writer.interface.flush();
var lain = try arsip.createFile(io, "tulis.txt", .{ .truncate = true });
defer lain.close(io);
try arsip.createDirPath(io, "lampiran");
var it = arsip.iterate();
var daftar: std.ArrayList(Entri) = .empty;
while (try it.next(io)) |entri| {
try daftar.append(arena, .{
.nama = try arena.dupe(u8, entri.name),
.kind = entri.kind,
});
}
std.mem.sort(Entri, daftar.items, {}, urutNama);
for (daftar.items) |entri| {
try out.print("{s} ({s})\n", .{ entri.nama, @tagName(entri.kind) });
}
try out.flush();
}baca.txt (file)
lampiran (directory)
tulis.txt (file)Fungsi createDirPath menyiapkan direktori arsip, dua berkas dibuat di dalamnya, dan satu subdirektori lampiran ditambahkan agar tag directory ikut terlihat. Iterator mengumpulkan seluruh entri ke ArrayList, dan setiap nama diduplikasi dengan arena.dupe karena nama entri merujuk buffer internal iterator yang ditimpa pada panggilan next berikutnya. Setelah daftar terurut, @tagName menampilkan jenis setiap entri secara ringkas.
Jalankan program dari direktori tempat daftar-arsip.zig berada, dan direktori arsip beserta isinya akan tercipta di sana.
# jalankan program dari direktori topik
zig run daftar-arsip.zigbaca.txt (file)
lampiran (directory)
tulis.txt (file)Kesalahan umum#
Kesalahan pertama adalah menutup berkas sebelum buffer disiram. close tidak memeriksa isi buffer milik antarmuka, sehingga data yang belum dikirim hilang meskipun berkasnya berhasil dibuat. Program yang menulis dalam banyak langkah sebaiknya memanggil flush segera setelah seluruh tulisan selesai dan sebelum defer menjalankan close.
Kesalahan kedua adalah menganggap urutan entri iterator sudah teratur. Program yang mencetak entri begitu terbaca dapat tampak benar pada satu komputer dan tampil acak pada komputer lain, karena urutannya ditentukan sistem berkas. Kumpulkan seluruh entri, urutkan dengan kriteria yang tetap, lalu cetak, sebagaimana dilakukan program pada topik ini.
Kesalahan ketiga adalah membuka direktori tanpa opsi .iterate, lalu memanggil iterate() atas pegangannya. Kompilator tidak menolak panggilan tersebut, sedangkan pelanggarannya baru terlihat saat program berjalan dalam bentuk penghentian program. Opsi ini karena itu dituliskan sejak pembukaan direktori, sebagaimana pada seluruh contoh topik ini.
Ringkasan#
std.Io.Diradalah pegangan direktori yang menjadi titik masuk seluruh operasi berkas, dan pegangan daricwd()tidak boleh diiterate.createFilemembuat berkas dengan opsitruncatedanread, penulisannya memakai buffer pemanggil, danflushwajib sebelumclose.readFileAllocmembaca seluruh isi berkas ke slice milik pemanggil dengan batasstd.Io.Limit.iteratedannext(io)menghasilkanEntrydengannamedankind, dan urutannya harus diurutkan ulang bila keluaran yang stabil diperlukan.
Referensi#
- std.Io.Dir — Dokumentasi pustaka standar Zig — Operasi direktori beserta opsi iterasinya.
- std.Io.File — Dokumentasi pustaka standar Zig — Pembuatan, penulisan, dan pembacaan berkas pada model instance.