mirror of
https://github.com/golang/go
synced 2024-11-22 03:44:39 -07:00
fmt.Scan: fix handling of EOFs.
Fixes #876. R=rsc CC=golang-dev https://golang.org/cl/1675048
This commit is contained in:
parent
37a6bc838b
commit
56f3c70111
@ -125,12 +125,15 @@ type scanError struct {
|
|||||||
err os.Error
|
err os.Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const EOF = -1
|
||||||
|
|
||||||
// ss is the internal implementation of ScanState.
|
// ss is the internal implementation of ScanState.
|
||||||
type ss struct {
|
type ss struct {
|
||||||
rr readRuner // where to read input
|
rr readRuner // where to read input
|
||||||
buf bytes.Buffer // token accumulator
|
buf bytes.Buffer // token accumulator
|
||||||
nlIsSpace bool // whether newline counts as white space
|
nlIsSpace bool // whether newline counts as white space
|
||||||
peekRune int // one-rune lookahead
|
peekRune int // one-rune lookahead
|
||||||
|
atEOF bool // already read EOF
|
||||||
maxWid int // max width of field, in runes
|
maxWid int // max width of field, in runes
|
||||||
widPresent bool // width was specified
|
widPresent bool // width was specified
|
||||||
wid int // width consumed so far; used in accept()
|
wid int // width consumed so far; used in accept()
|
||||||
@ -150,11 +153,12 @@ func (s *ss) Width() (wid int, ok bool) {
|
|||||||
return s.maxWid, s.widPresent
|
return s.maxWid, s.widPresent
|
||||||
}
|
}
|
||||||
|
|
||||||
const EOF = -1
|
|
||||||
|
|
||||||
// The public method returns an error; this private one panics.
|
// The public method returns an error; this private one panics.
|
||||||
// If getRune reaches EOF, the return value is EOF (-1).
|
// If getRune reaches EOF, the return value is EOF (-1).
|
||||||
func (s *ss) getRune() (rune int) {
|
func (s *ss) getRune() (rune int) {
|
||||||
|
if s.atEOF {
|
||||||
|
return EOF
|
||||||
|
}
|
||||||
if s.peekRune >= 0 {
|
if s.peekRune >= 0 {
|
||||||
rune = s.peekRune
|
rune = s.peekRune
|
||||||
s.peekRune = -1
|
s.peekRune = -1
|
||||||
@ -163,6 +167,7 @@ func (s *ss) getRune() (rune int) {
|
|||||||
rune, _, err := s.rr.ReadRune()
|
rune, _, err := s.rr.ReadRune()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == os.EOF {
|
if err == os.EOF {
|
||||||
|
s.atEOF = true
|
||||||
return EOF
|
return EOF
|
||||||
}
|
}
|
||||||
s.error(err)
|
s.error(err)
|
||||||
@ -174,6 +179,9 @@ func (s *ss) getRune() (rune int) {
|
|||||||
// It is called in cases such as string scanning where an EOF is a
|
// It is called in cases such as string scanning where an EOF is a
|
||||||
// syntax error.
|
// syntax error.
|
||||||
func (s *ss) mustGetRune() (rune int) {
|
func (s *ss) mustGetRune() (rune int) {
|
||||||
|
if s.atEOF {
|
||||||
|
s.error(io.ErrUnexpectedEOF)
|
||||||
|
}
|
||||||
if s.peekRune >= 0 {
|
if s.peekRune >= 0 {
|
||||||
rune = s.peekRune
|
rune = s.peekRune
|
||||||
s.peekRune = -1
|
s.peekRune = -1
|
||||||
@ -291,6 +299,7 @@ func newScanState(r io.Reader, nlIsSpace bool) *ss {
|
|||||||
}
|
}
|
||||||
s.nlIsSpace = nlIsSpace
|
s.nlIsSpace = nlIsSpace
|
||||||
s.peekRune = -1
|
s.peekRune = -1
|
||||||
|
s.atEOF = false
|
||||||
s.maxWid = 0
|
s.maxWid = 0
|
||||||
s.widPresent = false
|
s.widPresent = false
|
||||||
return s
|
return s
|
||||||
|
@ -493,3 +493,45 @@ func TestScanlnWithMiddleNewline(t *testing.T) {
|
|||||||
t.Errorf("expected newline error scanning string with extra newline, got: %s", err)
|
t.Errorf("expected newline error scanning string with extra newline, got: %s", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Special Reader that counts reads at end of file.
|
||||||
|
type eofCounter struct {
|
||||||
|
reader *strings.Reader
|
||||||
|
eofCount int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ec *eofCounter) Read(b []byte) (n int, err os.Error) {
|
||||||
|
n, err = ec.reader.Read(b)
|
||||||
|
if n == 0 {
|
||||||
|
ec.eofCount++
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify that when we scan, we see at most EOF once per call to a Scan function,
|
||||||
|
// and then only when it's really an EOF
|
||||||
|
func TestEOF(t *testing.T) {
|
||||||
|
ec := &eofCounter{strings.NewReader("123\n"), 0}
|
||||||
|
var a int
|
||||||
|
n, err := Fscanln(ec, &a)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("unexpected error", err)
|
||||||
|
}
|
||||||
|
if n != 1 {
|
||||||
|
t.Error("expected to scan one item, got", n)
|
||||||
|
}
|
||||||
|
if ec.eofCount != 0 {
|
||||||
|
t.Error("expected zero EOFs", ec.eofCount)
|
||||||
|
ec.eofCount = 0 // reset for next test
|
||||||
|
}
|
||||||
|
n, err = Fscanln(ec, &a)
|
||||||
|
if err == nil {
|
||||||
|
t.Error("expected error scanning empty string")
|
||||||
|
}
|
||||||
|
if n != 0 {
|
||||||
|
t.Error("expected to scan zero items, got", n)
|
||||||
|
}
|
||||||
|
if ec.eofCount != 1 {
|
||||||
|
t.Error("expected one EOF, got", ec.eofCount)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user