Writer interface
The same principle that is valid for reading also applies for writing – there's an interface in the io package that determines writing behaviors, as shown in the following code:
type Writer interface {
Write(p []byte) (n int, err error)
}
The io.Writer interface defines one method that, given a slice of bytes, returns how many of them have been written and/or if there's been any errors. A writer makes it possible to write data one chunk at a time without there being a requirement to have it all at once. The os.File struct also happens to be a writer, and can be used in such a fashion.
We can use a slice of bytes as a buffer to write information piece by piece. In the following example, we will try to combine reading from the previous section with writing, using the io.Seeker capabilities to reverse its content before writing it.
An example of reversing the contents of a file is shown in the following code:
// Let's omit argument check and file opening, we obtain src and dst
cur, err := src.Seek(0, os.SEEK_END) // Let's go to the end of the file
if err != nil {
fmt.Println("Error:", err)
return
}
b := make([]byte, 16)
After moving to the end of the file and defining a byte buffer, we enter a loop that goes a little backwards in the file, then reads a section of it, as shown in the following code:
for step, r, w := int64(16), 0, 0; cur != 0; {
if cur < step { // ensure cursor is 0 at max
b, step = b[:cur], cur
}
cur = cur - step
_, err = src.Seek(cur, os.SEEK_SET) // go backwards
if err != nil {
break
}
if r, err = src.Read(b); err != nil || r != len(b) {
if err == nil { // all buffer should be read
err = fmt.Errorf("read: expected %d bytes, got %d", len(b), r)
}
break
}
Then, we reverse the content and write it to the destination, as shown in the following code:
for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 {
switch { // Swap (\r\n) so they get back in place
case b[i] == '\r' && b[i+1] == '\n':
b[i], b[i+1] = b[i+1], b[i]
case j != len(b)-1 && b[j-1] == '\r' && b[j] == '\n':
b[j], b[j-1] = b[j-1], b[j]
}
b[i], b[j] = b[j], b[i] // swap bytes
}
if w, err = dst.Write(b); err != nil || w != len(b) {
if err != nil {
err = fmt.Errorf("write: expected %d bytes, got %d", len(b), w)
}
}
}
if err != nil && err != io.EOF { // we expect an EOF
fmt.Println("\n\nError:", err)
}