15

How can I read a file in zig, and run over it line by line?

I did found os.File.openRead, but it seems old cause it says that container 'std.os' has no member called 'File'.

clankill3r
  • 9,146
  • 20
  • 70
  • 126

4 Answers4

20

std.io.reader.readUntilDelimiterOrEof lets your read any std.io.reader line by line. You usually get the reader of something like a file by calling it’s reader() method. So for example:

var file = try std.fs.cwd().openFile("foo.txt", .{});
defer file.close();

var buf_reader = std.io.bufferedReader(file.reader());
var in_stream = buf_reader.reader();

var buf: [1024]u8 = undefined;
while (try in_stream.readUntilDelimiterOrEof(&buf, '\n')) |line| {
    // do something with line...
}

The std.io.bufferedReader isn’t mandatory but recommended for better performance.

Himel
  • 165
  • 1
  • 1
  • 6
slier
  • 6,511
  • 6
  • 36
  • 55
  • I understand zig is a quite low level. None the less I am surprised it needs that may lines of code to do something quite common like reading a file into a string. Do I have to admire the low levelness each time, I want to read a file? – Blcknx Feb 03 '23 at 15:18
5

I muddled through this by looking at the Zig library source/docs, so this might not be the most idiomatic way:

const std = @import("std");

pub fn main() anyerror!void {
    //  Get an allocator
    var gp = std.heap.GeneralPurposeAllocator(.{ .safety = true }){};
    defer _ = gp.deinit();
    const allocator = &gp.allocator;

    // Get the path
    var path_buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined;
    const path = try std.fs.realpath("./src/main.zig", &path_buffer);

    // Open the file
    const file = try std.fs.openFileAbsolute(path, .{ .read = true });
    defer file.close();

    // Read the contents
    const buffer_size = 2000;
    const file_buffer = try file.readToEndAlloc(allocator, buffer_size);
    defer allocator.free(file_buffer);

    // Split by "\n" and iterate through the resulting slices of "const []u8"
    var iter = std.mem.split(file_buffer, "\n");

    var count: usize = 0;
    while (iter.next()) |line| : (count += 1) {
        std.log.info("{d:>2}: {s}", .{ count, line });
    }
}

The above is a little demo program that you should be able to drop into the default project created from zig init-exe, it'll just print out it's own contents, with a line number.

You can also do this without allocators, provided you supply the required buffers.

I'd also recommend checking out this great resource: https://ziglearn.org/chapter-2/#readers-and-writers

Note: I'm currently running a development version of Zig from master (reporting 0.9.0), but I think this has been working for the last few official releases.

Chris
  • 8,268
  • 3
  • 33
  • 46
1

To open a file and get a file descriptor back std.os.open

https://ziglang.org/documentation/0.6.0/std/#std;os.open

To read from the file std.os.read

https://ziglang.org/documentation/0.6.0/std/#std;os.read

I can't find a .readlines() style function in the zig standard library. You'll have to write your own loop to find the \n characters.

nlta
  • 1,716
  • 1
  • 7
  • 17
1

Below is a test case that shows how to create a file, write to it then open the same file and read its content.

const std = @import("std");
const testing = std.testing;
const expect = testing.expect;

test "create a file and then open and read it" {
    var tmp_dir = testing.tmpDir(.{}); // This creates a directory under ./zig-cache/tmp/{hash}/test_file
    // defer tmp_dir.cleanup(); // commented out this line so, you can see the file after execution finished.

    var file1 = try tmp_dir.dir.createFile("test_file", .{ .read = true });
    defer file1.close();

    const write_buf: []const u8 = "Hello Zig!";
    try file1.writeAll(write_buf);

    var file2 = try tmp_dir.dir.openFile("test_file", .{});
    defer file2.close();

    const read_buf = try file2.readToEndAlloc(testing.allocator, 1024);
    defer testing.allocator.free(read_buf);

    try testing.expect(std.mem.eql(u8, write_buf, read_buf));
}

Check out fs package tests on Github or on your local machine under <zig-install-dir>/lib/fs/test.zig.

Also note that test allocator only works for tests. In your actual source code you need to choose an appropriate allocator.

artronics
  • 1,399
  • 2
  • 19
  • 28