540

I'm unable to find file.ReadLine function in Go.

How does one read a file line by line?

Amin Shojaei
  • 5,451
  • 2
  • 38
  • 46
g06lin
  • 5,725
  • 3
  • 18
  • 13

13 Answers13

921

In Go 1.1 and newer the most simple way to do this is with a bufio.Scanner. Here is a simple example that reads lines from a file:

package main

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

func main() {
    file, err := os.Open("/path/to/file.txt")
    if err != nil {
        log.Fatal(err)
    }
    defer file.Close()

    scanner := bufio.NewScanner(file)
    // optionally, resize scanner's capacity for lines over 64K, see next example
    for scanner.Scan() {
        fmt.Println(scanner.Text())
    }

    if err := scanner.Err(); err != nil {
        log.Fatal(err)
    }
}

This is the cleanest way to read from a Reader line by line.

There is one caveat: Scanner will error with lines longer than 65536 characters. If you know your line length is greater than 64K, use the Buffer() method to increase the scanner's capacity:

...
scanner := bufio.NewScanner(file)

const maxCapacity int = longLineLen  // your required line length
buf := make([]byte, maxCapacity)
scanner.Buffer(buf, maxCapacity)

for scanner.Scan() {
...
Zach Young
  • 10,137
  • 4
  • 32
  • 53
Stefan Arentz
  • 34,311
  • 8
  • 67
  • 88
  • 47
    And since the OP asked to scan over a file, it would be trivial to first `file, _ := os.Open("/path/to/file.csv")` and then scan over the file handle: `scanner := bufio.NewScanner(file)` – Evan Plumlee Aug 18 '13 at 13:28
  • 26
    Problem is Scanner.Scan() is limited in a 4096 []byte buffer size per line. You will get `bufio.ErrTooLong` error, which is `bufio.Scanner: token too long` if the line is too long. In which case, you'll have to use bufio.ReaderLine() or ReadString(). – eduncan911 Oct 21 '14 at 14:49
  • 12
    Just my $0.02 - this is the most correct answer on the page :) – sethvargo Nov 23 '14 at 20:45
  • 11
    from the source it's now limited to 64 KB instead of 4 KB, see: http://golang.org/src/bufio/scan.go?#L71 – Kokizzu Jan 21 '15 at 09:40
  • 5
    The problem with this is that CSV files can have newlines in their columns that do not indicate the end of a row. If you're reading CSV files, you should use [the go CSV package](https://golang.org/pkg/encoding/csv/) – Christopher Davies Aug 06 '15 at 01:09
  • 11
    You can configured Scanner to handle even longer lines using its Buffer() method: https://golang.org/pkg/bufio/#Scanner.Buffer – Alex Robinson Mar 23 '16 at 20:01
  • what is `longLineLen` in this example? There is not variable typing shown here – user5359531 Apr 06 '22 at 23:11
  • @user5359531, I've added a type, and you can check the documentation for the `Buffer()` method. – Zach Young Apr 06 '22 at 23:56
192

NOTE: The accepted answer was correct in early versions of Go. See the highest voted answer contains the more recent idiomatic way to achieve this.

There is function ReadLine in package bufio.

Please note that if the line does not fit into the read buffer, the function will return an incomplete line. If you want to always read a whole line in your program by a single call to a function, you will need to encapsulate the ReadLine function into your own function which calls ReadLine in a for-loop.

bufio.ReadString('\n') isn't fully equivalent to ReadLine because ReadString is unable to handle the case when the last line of a file does not end with the newline character.

Samuel Hawksby-Robinson
  • 2,652
  • 4
  • 24
  • 25
  • 47
    From the docs: "ReadLine is a low-level line-reading primitive. Most callers should use ReadBytes('\n') or ReadString('\n') instead or use a Scanner." – Michael Whatcott Mar 18 '14 at 23:20
  • 14
    @mdwhatcott why does it matter that its a " low-level line-reading primitive"? How does that reach to the conclusion that "Most callers should use ReadBytes('\n') or ReadString('\n') instead or use a Scanner."? – Charlie Parker Aug 17 '14 at 21:56
  • 16
    @CharlieParker - Not sure, just quoting the docs to add context. – Michael Whatcott Aug 18 '14 at 21:40
  • I was wondering, is it really true that it can return a string not up to "\n"? It makes sense, but I have not been able to "force" it to do so. I will try again... – Charlie Parker Aug 20 '14 at 03:07
  • 13
    From the same docs.. "If ReadString encounters an error before finding a delimiter, it returns the data read before the error and the error itself (often io.EOF)." So you can just check for io.EOF error and know your are done. – eduncan911 Oct 18 '14 at 22:44
  • I just ran into the issue stated by @mdwhatcott. My file had one line and no newline at the end so the file appeared to be empty by ReadBytes("\n"). ReadLine worked. – blockloop Dec 30 '15 at 04:55
  • 1
    Note that a read or write can fail due to an interrupted system call, which results in less than the expected number of bytes being read or written. – Justin Swanhart May 02 '19 at 14:21
  • 1
    (clarifying comments 5 years later) When something says it is low-level and most callers should use some other high-level thing it is because the high level thing is probably easier to use, implements the low level thing correctly, and provides an abstraction layer so that the impact of breaking changes to the low-level thing are contained to fixing the usage by the high-level thing, rather than all the user code that calls the low-level thing directly. – Davos Oct 28 '19 at 14:42
  • @CharlieParker "I was wondering, is it really true that it can return a string not up to ..." Yes, if reading from SDTIN, go seems to read up to 4096 bytes, instead of of the full line. – Ferrybig Jul 11 '22 at 21:13
64

EDIT: As of go1.1, the idiomatic solution is to use bufio.Scanner

I wrote up a way to easily read each line from a file. The Readln(*bufio.Reader) function returns a line (sans \n) from the underlying bufio.Reader struct.

// Readln returns a single line (without the ending \n)
// from the input buffered reader.
// An error is returned iff there is an error with the
// buffered reader.
func Readln(r *bufio.Reader) (string, error) {
  var (isPrefix bool = true
       err error = nil
       line, ln []byte
      )
  for isPrefix && err == nil {
      line, isPrefix, err = r.ReadLine()
      ln = append(ln, line...)
  }
  return string(ln),err
}

You can use Readln to read every line from a file. The following code reads every line in a file and outputs each line to stdout.

f, err := os.Open(fi)
if err != nil {
    fmt.Printf("error opening file: %v\n",err)
    os.Exit(1)
}
r := bufio.NewReader(f)
s, e := Readln(r)
for e == nil {
    fmt.Println(s)
    s,e = Readln(r)
}

Cheers!

Malcolm
  • 2,394
  • 1
  • 23
  • 26
  • 23
    I wrote this answer before Go 1.1 came out. Go 1.1 has a Scanner package in the stdlib. that provides the same functionality as my answer. I would recommend using Scanner instead of my answer since Scanner is in the stdlib. Happy hacking! :-) – Malcolm Jul 09 '13 at 15:57
45

There two common way to read file line by line.

  1. Use bufio.Scanner
  2. Use ReadString/ReadBytes/... in bufio.Reader

In my testcase, ~250MB, ~2,500,000 lines, bufio.Scanner(time used: 0.395491384s) is faster than bufio.Reader.ReadString(time_used: 0.446867622s).

Source code: https://github.com/xpzouying/go-practice/tree/master/read_file_line_by_line

Read file use bufio.Scanner,

func scanFile() {
    f, err := os.OpenFile(logfile, os.O_RDONLY, os.ModePerm)
    if err != nil {
        log.Fatalf("open file error: %v", err)
        return
    }
    defer f.Close()

    sc := bufio.NewScanner(f)
    for sc.Scan() {
        _ = sc.Text()  // GET the line string
    }
    if err := sc.Err(); err != nil {
        log.Fatalf("scan file error: %v", err)
        return
    }
}

Read file use bufio.Reader,

func readFileLines() {
    f, err := os.OpenFile(logfile, os.O_RDONLY, os.ModePerm)
    if err != nil {
        log.Fatalf("open file error: %v", err)
        return
    }
    defer f.Close()

    rd := bufio.NewReader(f)
    for {
        line, err := rd.ReadString('\n')
        if err != nil {
            if err == io.EOF {
                break
            }

            log.Fatalf("read file line error: %v", err)
            return
        }
        _ = line  // GET the line string
    }
}
zouying
  • 1,337
  • 10
  • 7
  • 2
    Be aware that this `bufio.Reader` example will not read the last line in a file if it does not end with a newline. `ReadString` will return both the last line and `io.EOF` in this case. – konrad Nov 26 '18 at 13:10
  • The code use bufio.Reader will lost the last line of the file. if err== io.EOF it cannot break directly, that time line has the last line of the file. – Justin Jul 08 '20 at 06:14
21

Example from this gist

func readLine(path string) {
  inFile, err := os.Open(path)
  if err != nil {
     fmt.Println(err.Error() + `: ` + path)
     return
  }
  defer inFile.Close()

  scanner := bufio.NewScanner(inFile)
  for scanner.Scan() {
    fmt.Println(scanner.Text()) // the line
  }
}

but this gives an error when there is a line that larger than Scanner's buffer.

When that happened, what I do is use reader := bufio.NewReader(inFile) create and concat my own buffer either using ch, err := reader.ReadByte() or len, err := reader.Read(myBuffer)

Another way that I use (replace os.Stdin with file like above), this one concats when lines are long (isPrefix) and ignores empty lines:


func readLines() []string {
  r := bufio.NewReader(os.Stdin)
  bytes := []byte{}
  lines := []string{}
  for {
    line, isPrefix, err := r.ReadLine()
    if err != nil {
      break
    }
    bytes = append(bytes, line...)
    if !isPrefix {
      str := strings.TrimSpace(string(bytes))
      if len(str) > 0 {
        lines = append(lines, str)
        bytes = []byte{}
      }
    }
  }
  if len(bytes) > 0 {
    lines = append(lines, string(bytes))
  }
  return lines
}
Kokizzu
  • 24,974
  • 37
  • 137
  • 233
13

You can also use ReadString with \n as a separator:

  f, err := os.Open(filename)
  if err != nil {
    fmt.Println("error opening file ", err)
    os.Exit(1)
  }
  defer f.Close()
  r := bufio.NewReader(f)
  for {
    path, err := r.ReadString(10) // 0x0A separator = newline
    if err == io.EOF {
      // do something here
      break
    } else if err != nil {
      return err // if you return error
    }
  }
lzap
  • 16,417
  • 12
  • 71
  • 108
7

Another method is to use the io/ioutil and strings libraries to read the entire file's bytes, convert them into a string and split them using a "\n" (newline) character as the delimiter, for example:

import (
    "io/ioutil"
    "strings"
)

func main() {
    bytesRead, _ := ioutil.ReadFile("something.txt")
    fileContent := string(bytesRead)
    lines := strings.Split(fileContent, "\n")
}

Technically you're not reading the file line-by-line, however you are able to parse each line using this technique. This method is applicable to smaller files. If you're attempting to parse a massive file use one of the techniques that reads line-by-line.

kubanczyk
  • 5,184
  • 1
  • 41
  • 52
pythoner
  • 97
  • 1
  • 3
6

bufio.Reader.ReadLine() works well. But if you want to read each line by a string, try to use ReadString('\n'). It doesn't need to reinvent the wheel.

kroisse
  • 515
  • 1
  • 4
  • 9
4
// strip '\n' or read until EOF, return error if read error  
func readline(reader io.Reader) (line []byte, err error) {   
    line = make([]byte, 0, 100)                              
    for {                                                    
        b := make([]byte, 1)                                 
        n, er := reader.Read(b)                              
        if n > 0 {                                           
            c := b[0]                                        
            if c == '\n' { // end of line                    
                break                                        
            }                                                
            line = append(line, c)                           
        }                                                    
        if er != nil {                                       
            err = er                                         
            return                                           
        }                                                    
    }                                                        
    return                                                   
}                                    
cyber
  • 59
  • 1
2

In the code bellow, I read the interests from the CLI until the user hits enter and I'm using Readline:

interests := make([]string, 1)
r := bufio.NewReader(os.Stdin)
for true {
    fmt.Print("Give me an interest:")
    t, _, _ := r.ReadLine()
    interests = append(interests, string(t))
    if len(t) == 0 {
        break;
    }
}
fmt.Println(interests)
zuzuleinen
  • 2,604
  • 5
  • 38
  • 49
1
import (
     "bufio"
     "os"
)

var (
    reader = bufio.NewReader(os.Stdin)
)

func ReadFromStdin() string{
    result, _ := reader.ReadString('\n')
    witl := result[:len(result)-1]
    return witl
}

Here is an example with function ReadFromStdin() it's like fmt.Scan(&name) but its takes all strings with blank spaces like: "Hello My Name Is ..."

var name string = ReadFromStdin()

println(name)
0DAYanc
  • 357
  • 2
  • 5
0

The Scan* functions are of great user here. Here is a slightly modified version of word scanner example from go-lang docs to scan lines from a file.

package main

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

func main() {
    // An artificial input source.
    const input = "Now is the winter of our discontent,\nMade glorious summer by this sun of York.\n"
    scanner := bufio.NewScanner(strings.NewReader(input))
    // Set the split function for the scanning operation.
    scanner.Split(bufio.ScanLines)
    // Count the lines.
    count := 0
    for scanner.Scan() {
        fmt.Println(scanner.Text())
        count++
    }
    if err := scanner.Err(); err != nil {
        fmt.Fprintln(os.Stderr, "reading input:", err)
    }
    fmt.Printf("%d\n", count)
}
-2

In the new version of Go 1.16 we can use package embed to read the file contents as shown below.

package main

import _"embed"


func main() {
    //go:embed "hello.txt"
    var s string
    print(s)

    //go:embed "hello.txt"
    var b []byte
    print(string(b))

    //go:embed hello.txt
    var f embed.FS
    data, _ := f.ReadFile("hello.txt")
    print(string(data))
}

For more details go through https://tip.golang.org/pkg/embed/ And https://golangtutorial.dev/tips/embed-files-in-go/

Arun
  • 205
  • 2
  • 4
  • 8
    This example is a great demonstration of the [`embed`](https://tip.golang.org/pkg/embed/) package, but I don't think your answer addresses the *core* of the question. OP wants to read in a file *line by line*. Even so, you've offered him a very great and idiomatic way for him to read in an entire file. – chahu418 Apr 20 '21 at 22:13