Hare by Example: Reading Files

Let's look at ways to read files in Hare using the `os` and `io` modules.

use bufio;
use io;
use fmt;
use os;
use strings;

export fn main() void = {
	// We can try to read an entire files contents into a variable.
	// We'll open a file first and then read from the file handle
	// into a variable.
	const handle = os::open("./reading-files.txt")!;
	defer io::close(handle)!;

	// We define a buffer to hold the contents of the file using the
	// provided OS buffer size.
	let overview: [os::BUFSZ]u8 = [0...];

	// Next we'll read all the data from the handle and provide the
	// slice in which to store the file contents. The number of
	// bytes read is returned which we will use later.
	const osz = match(io::read(handle, overview[..])) {

	// Since we overallocated our buffer we'll read all of the file
	// contents and get returned how many bytes we read so we'll
	// match on that case and yield that size back up.
	case let sz: size =>
		yield sz;

	// There could potentially be an end of file error while we
	// catch here.
	case io::EOF =>
		fmt::fatal("Unexpected end of file");

	// Any other io error will be caught here and we'll convert that
	// to a string to print it.
	case let err: io::error =>
		fmt::fatal(io::strerror(err));
	};

	// Now we can print the buffer, but we only care about the
	// number of bytes read in since we overallocated the buffer.
	// We chop it down from index 0 to the number of bytes read in.
	fmt::println(strings::fromutf8_unsafe(overview[..osz]))!;

	// We can even seek to certain positions of a reader if needed.
	// Since we read all of the contents of the file above we need
	// to reset the read position back to the beginneing before we
	// read from the file again or we'll get an EOF error.
	//
	// Seek position 0 exactly
	io::seek(handle, 0, io::whence::SET)!;

	// Reading the entire contents is fine for small files, but if
	// we have a very large file it is better to buffer the read
	// calls. For that we can use a buffered reader.

	// Set up read and write buffers, but since we're only reading
	// our write buffer is just an empty slice.
	let rbuf: [os::BUFSZ]u8 = [0...];
	let wbuf: []u8 = [];
	const buf = bufio::init(handle, rbuf, wbuf);

	// Start looping forever so we can read the file buffer
	let wordcount = 0z;
	for (true) {
		// Let's read a whole word at a time. We can use this
		// function and specify a SPACE character as the
		// delimeter,
		match (bufio::read_tok(&buf, ' ')) {

		// If we get a slice of bytes back then we print the
		// word and a newline.
		case let tok: []u8 =>
			wordcount += 1z;
			fmt::println(strings::fromutf8_unsafe(tok))!;

		// If we're done reading the whole file we print a
		// message, but we also need to break from the infinite
		// for loop.
		case io::EOF =>
			fmt::println("Done reading the whole file.")!;
			break;

		// Any other error
		case let err: io::error =>
			fmt::fatal(io::strerror(err));
		};
	};
	
	fmt::printf("Total words: {}", wordcount)!;
};
$ hare run reading-files.ha
4/4 tasks completed (100%)
Let's look at ways to read files in Hare using the `os` and `io`
modules.

Let's
look
at
ways
to
read
files
in
Hare
using
the
`os`
and
`io`
modules.

Done reading the whole file.
Total words: 14