Right way to read user input from stdin in zig 0.15.2

i am sorry if this is a duplicate post, but i couldn’t find an answer to this.

const std = @import("std");

pub fn main() !void {
    var stdin_buffer: [1024]u8 = undefined;
    var stdin_reader = std.fs.File.stdin().reader(&stdin_buffer);

    menu: while (true) {
        try std.fs.File.stdout().writeAll("\t1 -> Greet\n\tq -> Quit\n");
        const user_input = try stdin_reader.interface.takeDelimiterExclusive('\n');
        switch (user_input[0]) {
            '1' => try std.fs.File.stdout().writeAll("Hi, Welcome.\n"),
            'q' => break :menu,
            else => try std.fs.File.stdout().writeAll("Invalid Input\n"),
        }
    }
}

this worked but after first iteration, throws index out of bound error even before taking input from the user. the slice access user_input[0] occurs after taking input from user but it’s throwing error before reading from stdin.
then i speculated that maybe i must flush the buffer before using it again but couldn’t find a flush method
the .interface feels wrong and i feel like this is not the right way to do it.

so, please help me.

it leaves the \n in the buffer, so the next attempt imediately finds it and returns a zero length slice (as it doesnt include the delimiter).

This is stated in the first sentence of its docs, though it’s easy to overlook

/// Returns a slice of the next bytes of buffered data from the stream until
/// `delimiter` is found, advancing the seek position up to (but not past)
/// the delimiter.

you’d need to either toss(1) or use takeDelimiter (not Exclusive nor Inclusive), which will consume the delimiter from the buffer, but not include it in the returned slice.

flush is only on the writer, as its important to ensure the data in the buffer gets written to wherever its going.

the equivalent on a a reader is tossBuffered. you wouldnt want to call that though, as that will toss out the entire buffer when you just want to toss the delimiter which after the takeDelimiterExclusive is garunteed to be the next byte. So you’d want to do toss(1) instead.

3 Likes

thanks for the answer
are there any other ways to do the same?
many LLMs suggested me to use std.Io.Reader along with takeDelimiterExclusive
and also some other LLMs suggested std.Io.getStdIn() but these didn’t work.
I noticed that this is because of Zig not being stable(v1) and changing many things drastically in releases. I often stumble upon resources that are outdated so as a beginner how do you recommend me to go about learning Zig?
I tried learning from the Docs but sometimes it’s hard to connect the dots.

Using a std.Io.Reader is recommended, the exact functions to use depend on what you want.

lang ref | ziglings | project based book
As well as the std source, it comes with your zig install.
You can run zig std to run doc website locally.

The 0.15 standard lib docs on the official website are out of sync with the actual 0.15 std that you get, so I didn’t link them.

Whenever those are not enough, just ask here or the discord.

I do recommend looking at talks/blogs/articles, especial those pertaining to zigs next release(s) so that you are not blind sided when releases come out. The change notes are good, but often lack detail on things.

I suggest avoiding LLMs with zig, its training data is out of date, but also because LLMs are not a source of truth, they do perpetuate and fabricate misinformation.

3 Likes

oh okay, now i understand why i was having problems

thank you very much