The "Zig way" to read a file

Hi all -
Apologies if this seems too silly a question but here goes…

I’m trying to find the “Zig way” of reading a file.
What I’m after is the Zig equivalent of C’s int main ( int argc, char* argv[ ] ) where Zig counts the arguments and prints an error if there aren’t enough.

I’ve used Google but none of the several bits of code I found worked for me (maybe because they’re from previous Zig versions).
I also tried looking at the “File” code in Zig’s standard library but that didn’t help. So, I’m hoping someone can give the idiomatic code for reading a file.

Many thanks in advance -
mooseman

If I understand you correctly you are primarily looking for how to get the command line arguments and not how to read a file.
For commandline arguments you can use:
std.process.argsAlloc(..)
and don’t forget to do
std.process.argsFree(..)

Hi IntegratedQuantum -

Close! I’m actually after code that does both.
Here’s a C++ bit of code that is similar to what I want -


#include <fstream> // for file-access
#include <string>
#include <iostream>
using namespace std; 
int main(int argc, char* argv[])
{ 
    if (argc > 1) {
        cout << "argv[1] = " << argv[1] << endl; 
    } else {
        cout << "No file name entered. Exiting...";
        return -1;
    }
    ifstream infile(argv[1]); //open the file
    
    if (infile.is_open() && infile.good()) {
        while (getline(infile, line)){

    //  read the file          

        }
        
    } else {
        cout << "Failed to open file..";
    }
    return 0;
}

Hope that helps - thanks for your reply!
I’m off to bed soon but will reply to any other replies tomorrow - thanks all!

  • mooseman
1 Like

You can read a file like this:

const file = std.fs.cwd().openFile(path, .{}) catch |err| {
    std.log.err("Failed to open file: {s}", .{@errorName(err)});
    return;
};
defer file.close();

while(file.reader().readUntilDelimiterOrEofAlloc(allocator, '\n', std.math.maxInt(usize)) catch |err| {
    std.log.err("Failed to read line: {s}", .{@errorName(err)});
    return;
}) |line| {
    defer allocator.free(line);
    _ = line; // Do something with the line
}

As for the allocator, you can just use a general purpose allocator. It’s not the most efficient, but it should be good enough, and it can detect some memory bugs, like memory leaks(in debug mode it even gives stacktraces for where the memory was allocated):

var gpa = std.heap.GeneralPurposeAllocator(.{.thread_safe=true}){};
const allocator = gpa.allocator();
defer gpa.deinit() == .leak) {
    std.log.err("Memory leak", .{});
};
2 Likes

Worth knowing that there’s an open issue for adding an optimal allocator, which the GPA will switch to using in Fast/Small release modes. So we shouldn’t expect its current relative inefficiency in those modes to last.

Good reasons not to use the GPA:

  • Some other allocator makes more sense (arena etc)
  • You have real inefficiency problems stemming from the GPA, meaning:
    • It’s losing you money
    • Users are going to complain (your game is stuttering)

Bad reasons not to use the GPA:

  • You read on a forum that it isn’t very fast
  • You want to shave some nanoseconds off a benchmark right now, damn the torpedos

Catching memory bugs during development is very important. You don’t want to forgo that to save a few cycles in production, which you’ll get back on a later release anyway.

4 Likes

Hi IntegratedQuantum and mnemnion!

Thanks very much for that - that code looks great!
I’ll give it a go and that should “see me right” (as we say here in NZ… :slight_smile: )

This will be a big help with my learning Zig. I just need to be patient and realise that it’s still a young language so things will occasionally change here and there.
It’s just a matter of using it and using it until the syntax becomes almost “muscle memory” . I do very much like the feel of it already though!

Cheers, thanks again - bye for now -
mooseman

2 Likes