Getchar and ungetc equivalents in zig?

Does zig have functions that are equivalent to C library’s getchar() and ungetc(ch1, stdin)?

readByte is the getchar() equivalent. You can get a std.io.Reader using the reader() method of the file or network stream.

There is no ungetc in the standard library. You can use a char: ?u8 (optional byte) to implement ungetc like:

var char: ?u8 = null;

fn ungetc(ch: u8) void {
    char = ch;
}

fn getchar(reader: Reader) !u8 {
    if (char) |ch| {
        char = null; 
        return ch;
    }
    return reader.readByte();
}

Welcome to ziggit :slight_smile:

1 Like

char = ch?..

Yes, thank you.

Hey, that’s actually a pretty cool solution. The global variable makes me slightly worried, but I’ve got some ideas how to work around it.

The only really limitation is that it would only allow a single character ungetc (but that would not be something that I would need right now at least).

1 Like

This “buffer” can be placed into some struct with getc/ungetc methods or so.

BTW, here is a snippet from man ungetc:

ungetc() pushes c back to stream, cast to unsigned char, where it is
available for subsequent read operations. Pushed-back characters will
be returned in reverse order; only one pushback is guaranteed.

So “one-byte push-back buffer” is quite a “legal” solution.

That’s interesting. I never tried to ungetc more than one character, but I somehow assumed that it would work just fine if I did push back more. It’s always a good idea to read the spec/man, etc.

const std = @import("std");

const CharReader = struct {
    char: ?u8,
    reader: std.io.Reader,

    fn init(reader: std.io.Reader) CharReader {
        return .{
            .char = null,
            .reader = reader,
        };
    }

    fn ungetc(self: CharReader, ch: u8) void {
        self.char = ch;
    }

    fn getchar(self: CharReader) !u8 {
        if (self.char) |ch| {
            self.char = null; 
            return ch;
        }
        return self.reader.readByte();
    }
};
1 Like

This is exactly what I was looking for! Thanks!

(I’m very new to zig and there are many “d’oh” moments when I crack my brain to figure out the correct way… and then I see the solution and it elegant and simple)

1 Like

It won’t, but you can use a stack (can be implemented using generic ArrayList from standard library, for ex.). If pop operation returns null, do actual reading.

Off-topic. I personally never used ungetc(). How can it be useful at all? Multiple input handlers?.. I searched a bit and found this.

I can’t find any useful examples of how this function could be necessary

Just out of curiosity, what are you using ungetc() for?

Maybe it is not the most elegant solution, but I sometimes use it for key detection.

  • ESC = 27
  • UP_ARROW = 27[A
  • A = A

So, if I want to detect both ESC, UP_ARROW and A, etc, I must “ignore” the first ESC and read the next character. If the next character is not [, then I can ungetc and continue the loop to capture it as another key that was pressed after normal ESC.