How to use the function: takeDelimiterExclusive

I am using the following code snippet to try to read an input text file from advent of code, doing a line by line read. for this I am using the function: takeDelimiterExclusive.

The problem: it seems only the first line is being printed, the seek position stays before the delimiter character meaning each subsequent function call will too do the same and returns an empty string.

The question: am I using this function wrong ? should i use takeDelimiterInclusive (which works) ? or is there another preferred method to achieve what i am trying to do ? Thanks in advance

// I am using Zig version 0.16.0-dev.2223+4f16e80ce
const std = @import("std");

pub fn main(init: std.process.Init) !void {
    const io = init.io;
    const absolute_path = "/home/user/Desktop/Projects/AdventOfCode/2025/01/input.txt";
    const file = try std.Io.Dir.openFileAbsolute(io, absolute_path, .{});
    defer file.close(io);

    var buffer: [4096]u8 = undefined;
    var reader = file.reader(io, &buffer);
    while (true) {
        const line = reader.interface.takeDelimiterExclusive('\n') catch |e| {
            if (e == error.EndOfStream) break;
            return e;
        };
        std.debug.print("{s}\n", .{line});
    }
}

As the name suggests, it doesn’t take (consume) the delimiter. So, to make your code work, you need to consume the delimiter to make progress. Since you know that the delimiter is 1 byte, you can call take(1) toss(1) after you take the line.

2 Likes

.toss(1) is probably preferred.

2 Likes

Just plain takeDelimiter is the best choice.

If you want to figure out why there are three options, your best bet is to look at the tests in the documentation.

2 Likes

Indeed, using toss(1) lets me advance the seek position, even the simpler takeDelimiter solves my problem. Thank to all of you for your help !

1 Like

Why is it the best?

It isn’t globally the best. But it does what most people want when looking for a function like this: in \n terms, it gives you a line, then it gives you another line, and when there are none left it returns null.

For the other 10% of cases, there’s the other two.

2 Likes

ah right - takeDelimiter consumes the delimiter but doesn’t include it in the return slice. I don’t love the name, but I’m sure there’s reasoning why it isn’t “takeDelimited”.

Also handles end of stream that doesn’t end with the delimiter, so really is the idiomatic “read by lines”. In other bloaty languages, you’d have a “read_line” that assumes ‘\n’ delimited.

You’re completely right. My suggestion of .toss comes from me being too stuck dealing with network protocols recently where I want to distinguish between EOF and a delimiter (CRLF usually).

2 Likes