Created
April 24, 2018 11:03
-
-
Save keegancsmith/54d2325e7a3c6eb78276c884c4208aa6 to your computer and use it in GitHub Desktop.
Using bufio.Scanner to get the last available line
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"bufio" | |
"bytes" | |
"fmt" | |
"log" | |
"os" | |
) | |
func scanLastNonEmptyLine(data []byte, atEOF bool) (advance int, token []byte, err error) { | |
// Set advance to after our last line | |
if atEOF { | |
advance = len(data) | |
} else { | |
// data[advance:] now contains a possibly incomplete line | |
advance = bytes.LastIndexAny(data, "\n\r") + 1 | |
} | |
data = data[:advance] | |
// Remove empty lines (strip EOL chars) | |
data = bytes.TrimRight(data, "\n\r") | |
// We have no non-empty lines, so advance but do not return a token. | |
if len(data) == 0 { | |
return advance, nil, nil | |
} | |
token = data[bytes.LastIndexAny(data, "\n\r")+1:] | |
return advance, token, nil | |
} | |
func main() { | |
scanner := bufio.NewScanner(os.Stdin) | |
scanner.Split(scanLastNonEmptyLine) | |
for scanner.Scan() { | |
fmt.Printf("%q\n", scanner.Text()) | |
} | |
if err := scanner.Err(); err != nil { | |
log.Fatalf("Invalid input: %s", err) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"bytes" | |
"testing" | |
) | |
func TestScanLastNonEmptyLine(t *testing.T) { | |
tests := []struct { | |
Name string | |
Data []byte | |
AtEOF bool | |
Advance int | |
Token []byte | |
}{ | |
{ | |
Name: "request-more", | |
Data: []byte("test"), | |
AtEOF: false, | |
Advance: 0, | |
Token: nil, | |
}, | |
{ | |
Name: "only-eol", | |
Data: []byte("\n"), | |
AtEOF: false, | |
Advance: 1, | |
Token: nil, | |
}, | |
{ | |
Name: "only-eol-multi", | |
Data: []byte("\n\r\r\r"), | |
AtEOF: false, | |
Advance: 4, | |
Token: nil, | |
}, | |
{ | |
Name: "only-eol-not-eof", | |
Data: []byte("\n\rworld"), | |
AtEOF: false, | |
Advance: 2, // at the w in world | |
Token: nil, | |
}, | |
{ | |
Name: "eof-simple", | |
Data: []byte("hello"), | |
AtEOF: true, | |
Advance: 5, | |
Token: []byte("hello"), | |
}, | |
{ | |
Name: "eof-trailing-eol-1", | |
Data: []byte("hello\n"), | |
AtEOF: true, | |
Advance: 6, | |
Token: []byte("hello"), | |
}, | |
{ | |
Name: "eof-trailing-eol-2", | |
Data: []byte("hello\r\n"), | |
AtEOF: true, | |
Advance: 7, | |
Token: []byte("hello"), | |
}, | |
{ | |
Name: "eof-trailing-eol-4", | |
Data: []byte("hello\r\n\r\n"), | |
AtEOF: true, | |
Advance: 9, | |
Token: []byte("hello"), | |
}, | |
{ | |
Name: "one-line", | |
Data: []byte("hello\nworld"), | |
AtEOF: false, | |
Advance: 6, // at the w in world | |
Token: []byte("hello"), | |
}, | |
{ | |
Name: "many-lines", | |
Data: []byte("one\ntwotwo\nthreethreethree\rfourfourfourfour"), | |
AtEOF: false, | |
Advance: 27, // at first f | |
Token: []byte("threethreethree"), | |
}, | |
{ | |
Name: "many-lines-eof", | |
Data: []byte("one\ntwotwo\nthreethreethree\rfourfourfourfour"), | |
AtEOF: true, | |
Advance: 43, // at end of data | |
Token: []byte("fourfourfourfour"), | |
}, | |
{ | |
Name: "many-lines-eol", | |
Data: []byte("one\ntwotwo\nthreethreethree\rfourfourfourfour\n"), | |
AtEOF: false, | |
Advance: 44, // at end of data | |
Token: []byte("fourfourfourfour"), | |
}, | |
{ | |
Name: "many-lines-multi-eol", | |
Data: []byte("one\ntwotwo\nthreethreethree\rfourfourfourfour\r\n"), | |
AtEOF: false, | |
Advance: 45, // at end of data | |
Token: []byte("fourfourfourfour"), | |
}, | |
} | |
for _, tt := range tests { | |
t.Run(tt.Name, func(t *testing.T) { | |
advance, token, err := scanLastNonEmptyLine(tt.Data, tt.AtEOF) | |
if err != nil { | |
t.Fatal(err) | |
} | |
if advance != tt.Advance { | |
t.Errorf("got advance %d != %d", advance, tt.Advance) | |
} | |
if !bytes.Equal(token, tt.Token) { | |
t.Errorf("got token %q != %q", token, tt.Token) | |
} | |
}) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment