Reading Files in Swift

Here’s the translation of the Go code for reading files to Swift, along with explanations in Markdown format suitable for Hugo:

Reading and writing files are basic tasks needed for many Swift programs. First we’ll look at some examples of reading files.

import Foundation

// Reading files requires checking most calls for errors.
// This helper will streamline our error checks below.
func check(_ error: Error?) {
    if let error = error {
        fatalError(error.localizedDescription)
    }
}

// Perhaps the most basic file reading task is
// slurping a file's entire contents into memory.
let dat = try String(contentsOfFile: "/tmp/dat", encoding: .utf8)
print(dat)

// You'll often want more control over how and what
// parts of a file are read. For these tasks, start
// by opening a file to obtain a FileHandle.
guard let file = FileHandle(forReadingAtPath: "/tmp/dat") else {
    fatalError("Unable to open file")
}

// Read some bytes from the beginning of the file.
// Allow up to 5 to be read but also note how many
// actually were read.
let b1 = file.readData(ofLength: 5)
print("\(b1.count) bytes: \(String(data: b1, encoding: .utf8) ?? "")")

// You can also seek to a known location in the file
// and read from there.
file.seek(toFileOffset: 6)
let b2 = file.readData(ofLength: 2)
print("\(b2.count) bytes @ 6: \(String(data: b2, encoding: .utf8) ?? "")")

// Other methods of seeking are relative to the
// current cursor position,
file.seek(toFileOffset: UInt64(file.offsetInFile + 4))

// and relative to the end of the file.
file.seekToEndOfFile()
file.seek(toFileOffset: UInt64(file.offsetInFile - 10))

// The Foundation framework provides some functions that may
// be helpful for file reading. For example, reads
// like the ones above can be more robustly
// implemented with Data.
file.seek(toFileOffset: 6)
let b3 = file.readData(ofLength: 2)
print("\(b3.count) bytes @ 6: \(String(data: b3, encoding: .utf8) ?? "")")

// There is no built-in rewind, but
// seek(toFileOffset: 0) accomplishes this.
file.seek(toFileOffset: 0)

// The BufferedInputStream class implements a buffered
// reader that may be useful both for its efficiency
// with many small reads and because of the additional
// reading methods it provides.
let inputStream = InputStream(fileAtPath: "/tmp/dat")!
inputStream.open()
let bufferSize = 5
var buffer = [UInt8](repeating: 0, count: bufferSize)
let bytesRead = inputStream.read(&buffer, maxLength: bufferSize)
if bytesRead > 0 {
    let b4 = Data(buffer[0..<bytesRead])
    print("\(bytesRead) bytes: \(String(data: b4, encoding: .utf8) ?? "")")
}

// Close the file when you're done (usually this would
// be scheduled immediately after opening with
// defer).
file.closeFile()
inputStream.close()

To run this program:

$ echo "hello" > /tmp/dat
$ echo "swift" >> /tmp/dat
$ swift reading-files.swift
hello
swift
5 bytes: hello
2 bytes @ 6: sw
2 bytes @ 6: sw
5 bytes: hello

Next we’ll look at writing files.