1

I have a text file containing some text data I would like to read in. Unfortunately I can't find the way to do it.

Here is an example

5 4
1 2 - Yogurt
2 0 X Chicken soup
3 1 X Cheese 
4 3 X Ham 
2
3
4
0

The file is made of three parts. The first part is the header (first line), the second part is a list of records, and the last part is a list of unit64 values.

The header contains only two values, a uint64 followed by a unit16. The second value is the number of records and also the number of values in the third part since these numbers are the same.

A record is a unit64 value, followed by a uint16 value, followed by a single char that can only be X or -, followed by a utf-8 encoded string up to the end of line. The data has be written into the file by using fmt.Fprintf().

The third part contains uint64 values.

I've spending some hours now trying to find out how to read this data out of the text file and can't find a way.

I can only use strconv.ParseUint(str, 0, 64) or uint16(strconv.ParseUint(str, 0, 16)) if str contains only the digits belonging to the number. I looked into bufio to use the Reader but I can get at most lines. I should probably use the bufio.Scanner but I can't determine how to use it from the documentation.

chmike
  • 20,922
  • 21
  • 83
  • 106
  • maybe http://stackoverflow.com/questions/5884154/golang-read-text-file-into-string-array-and-write could help –  Jun 15 '13 at 17:03
  • I found it but it doesn't help. I ended up using fmt.Scanf(...). To read the end of line string I had to trim the string because ReadString('\n') returns me a string with a space in front and a \n at the end. – chmike Jun 15 '13 at 17:25

1 Answers1

4

The Rubber duck debugging effect worked again. After asking the question, I found out the answer by my self. Here it is in case other people share the same problem I had.

In the following example I'll simply print out the data read

import (
    "bufio"
    "fmt"
    "log"
    "os"
    "strings"
)


func loadFile( fileName string ) {
    // Open file and instantiate a reader
    file, err := os.Open(fileName)
    if err != nil {
        log.Fatal(err)
    }
    reader := bufio.NewReader(file)

    var {
        value0 uint64,
        nbrRows uint16
    }

    // Read header values
    if _,err := fmt.Fscanf(reader, "%d %d\n", &value0, &nbrRows); err != nil {
        log.Fatal(err)
    }

    // Iterate on the rows 
    for i := uint16(0); i < nbrRows; i++ {
        var {
            value1 uint64,
            value2 uint16,
            value3 string,
            value4 string
        }

        // Read first three row values
        if _,err := fmt.Fscanf(reader, "%d %d %s\n", &value1, &value2, &value3); err != nil {
            log.Fatal(err)
        }
        // Read remain of line
        if value4,err := reader.ReadString('\n'); err != nil {
            log.Fatal(err)
        }
        value4 = strings.Trim(value4," \n")

        // Display the parsed data
        fmt.Printf("%d %d %s '%s'\n", value1, value2, value3, value4)
    }

    // Iterate on the rows containing a single integer value
    for i := uint16(0); i < nbrRows; i++ {
        var value5 uint64

        // Read the value
        if _,err := fmt.Fscanf(reader, "%d\n", &value5); err != nil {
            log.Fatal(err)
        }

        // Display the parsed data
        fmt.Printf("%d\n", value5)
    }
}

This code assume that the value4 doesn't start and end with spaces or newlines, which is my case. This is because the Trim() call will remove them.

chmike
  • 20,922
  • 21
  • 83
  • 106
  • I love that technique, unfortunately I tend to use whoever is closest at the time as my designated duck. They're usually confused, but occasionally offer surprisingly good advice! – Intermernet Jun 16 '13 at 11:04