mirror of
https://github.com/golang/go
synced 2024-11-20 07:54:39 -07:00
fmt: stop giving characters to the Scan method of Scanner
when we hit a newline during *scanln routines. Fixes #1490. R=rsc CC=golang-dev https://golang.org/cl/4209042
This commit is contained in:
parent
75fb2f6c82
commit
87fe958a49
@ -120,7 +120,7 @@
|
|||||||
An analogous set of functions scans formatted text to yield
|
An analogous set of functions scans formatted text to yield
|
||||||
values. Scan, Scanf and Scanln read from os.Stdin; Fscan,
|
values. Scan, Scanf and Scanln read from os.Stdin; Fscan,
|
||||||
Fscanf and Fscanln read from a specified os.Reader; Sscan,
|
Fscanf and Fscanln read from a specified os.Reader; Sscan,
|
||||||
Sscanf and Sscanln read from an argument string. Sscanln,
|
Sscanf and Sscanln read from an argument string. Scanln,
|
||||||
Fscanln and Sscanln stop scanning at a newline and require that
|
Fscanln and Sscanln stop scanning at a newline and require that
|
||||||
the items be followed by one; Sscanf, Fscanf and Sscanf require
|
the items be followed by one; Sscanf, Fscanf and Sscanf require
|
||||||
newlines in the input to match newlines in the format; the other
|
newlines in the input to match newlines in the format; the other
|
||||||
|
@ -29,6 +29,8 @@ type runeUnreader interface {
|
|||||||
// to discover the next space-delimited token.
|
// to discover the next space-delimited token.
|
||||||
type ScanState interface {
|
type ScanState interface {
|
||||||
// GetRune reads the next rune (Unicode code point) from the input.
|
// GetRune reads the next rune (Unicode code point) from the input.
|
||||||
|
// If invoked during Scanln, Fscanln, or Sscanln, GetRune() will
|
||||||
|
// return EOF after returning the first '\n'.
|
||||||
GetRune() (rune int, err os.Error)
|
GetRune() (rune int, err os.Error)
|
||||||
// UngetRune causes the next call to GetRune to return the same rune.
|
// UngetRune causes the next call to GetRune to return the same rune.
|
||||||
UngetRune()
|
UngetRune()
|
||||||
@ -44,7 +46,7 @@ type ScanState interface {
|
|||||||
// Scanner is implemented by any value that has a Scan method, which scans
|
// Scanner is implemented by any value that has a Scan method, which scans
|
||||||
// the input for the representation of a value and stores the result in the
|
// the input for the representation of a value and stores the result in the
|
||||||
// receiver, which must be a pointer to be useful. The Scan method is called
|
// receiver, which must be a pointer to be useful. The Scan method is called
|
||||||
// for any argument to Scan or Scanln that implements it.
|
// for any argument to Scan, Scanf, or Scanln that implements it.
|
||||||
type Scanner interface {
|
type Scanner interface {
|
||||||
Scan(state ScanState, verb int) os.Error
|
Scan(state ScanState, verb int) os.Error
|
||||||
}
|
}
|
||||||
@ -96,7 +98,7 @@ func Sscanf(str string, format string, a ...interface{}) (n int, err os.Error) {
|
|||||||
// returns the number of items successfully scanned. If that is less
|
// returns the number of items successfully scanned. If that is less
|
||||||
// than the number of arguments, err will report why.
|
// than the number of arguments, err will report why.
|
||||||
func Fscan(r io.Reader, a ...interface{}) (n int, err os.Error) {
|
func Fscan(r io.Reader, a ...interface{}) (n int, err os.Error) {
|
||||||
s := newScanState(r, true)
|
s := newScanState(r, true, false)
|
||||||
n, err = s.doScan(a)
|
n, err = s.doScan(a)
|
||||||
s.free()
|
s.free()
|
||||||
return
|
return
|
||||||
@ -105,7 +107,7 @@ func Fscan(r io.Reader, a ...interface{}) (n int, err os.Error) {
|
|||||||
// Fscanln is similar to Fscan, but stops scanning at a newline and
|
// Fscanln is similar to Fscan, but stops scanning at a newline and
|
||||||
// after the final item there must be a newline or EOF.
|
// after the final item there must be a newline or EOF.
|
||||||
func Fscanln(r io.Reader, a ...interface{}) (n int, err os.Error) {
|
func Fscanln(r io.Reader, a ...interface{}) (n int, err os.Error) {
|
||||||
s := newScanState(r, false)
|
s := newScanState(r, false, true)
|
||||||
n, err = s.doScan(a)
|
n, err = s.doScan(a)
|
||||||
s.free()
|
s.free()
|
||||||
return
|
return
|
||||||
@ -115,7 +117,7 @@ func Fscanln(r io.Reader, a ...interface{}) (n int, err os.Error) {
|
|||||||
// values into successive arguments as determined by the format. It
|
// values into successive arguments as determined by the format. It
|
||||||
// returns the number of items successfully parsed.
|
// returns the number of items successfully parsed.
|
||||||
func Fscanf(r io.Reader, format string, a ...interface{}) (n int, err os.Error) {
|
func Fscanf(r io.Reader, format string, a ...interface{}) (n int, err os.Error) {
|
||||||
s := newScanState(r, false)
|
s := newScanState(r, false, false)
|
||||||
n, err = s.doScanf(format, a)
|
n, err = s.doScanf(format, a)
|
||||||
s.free()
|
s.free()
|
||||||
return
|
return
|
||||||
@ -134,6 +136,7 @@ type ss struct {
|
|||||||
rr io.RuneReader // where to read input
|
rr io.RuneReader // 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
|
||||||
|
nlIsEnd bool // whether newline terminates scan
|
||||||
peekRune int // one-rune lookahead
|
peekRune int // one-rune lookahead
|
||||||
prevRune int // last rune returned by GetRune
|
prevRune int // last rune returned by GetRune
|
||||||
atEOF bool // already read EOF
|
atEOF bool // already read EOF
|
||||||
@ -149,6 +152,11 @@ func (s *ss) GetRune() (rune int, err os.Error) {
|
|||||||
s.peekRune = -1
|
s.peekRune = -1
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if s.nlIsEnd && s.prevRune == '\n' {
|
||||||
|
rune = EOF
|
||||||
|
err = os.EOF
|
||||||
|
return
|
||||||
|
}
|
||||||
rune, _, err = s.rr.ReadRune()
|
rune, _, err = s.rr.ReadRune()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
s.prevRune = rune
|
s.prevRune = rune
|
||||||
@ -300,7 +308,7 @@ func (r *readRune) ReadRune() (rune int, size int, err os.Error) {
|
|||||||
var ssFree = newCache(func() interface{} { return new(ss) })
|
var ssFree = newCache(func() interface{} { return new(ss) })
|
||||||
|
|
||||||
// Allocate a new ss struct or grab a cached one.
|
// Allocate a new ss struct or grab a cached one.
|
||||||
func newScanState(r io.Reader, nlIsSpace bool) *ss {
|
func newScanState(r io.Reader, nlIsSpace, nlIsEnd bool) *ss {
|
||||||
s := ssFree.get().(*ss)
|
s := ssFree.get().(*ss)
|
||||||
if rr, ok := r.(io.RuneReader); ok {
|
if rr, ok := r.(io.RuneReader); ok {
|
||||||
s.rr = rr
|
s.rr = rr
|
||||||
@ -308,6 +316,8 @@ func newScanState(r io.Reader, nlIsSpace bool) *ss {
|
|||||||
s.rr = &readRune{reader: r}
|
s.rr = &readRune{reader: r}
|
||||||
}
|
}
|
||||||
s.nlIsSpace = nlIsSpace
|
s.nlIsSpace = nlIsSpace
|
||||||
|
s.nlIsEnd = nlIsEnd
|
||||||
|
s.prevRune = -1
|
||||||
s.peekRune = -1
|
s.peekRune = -1
|
||||||
s.atEOF = false
|
s.atEOF = false
|
||||||
s.maxWid = 0
|
s.maxWid = 0
|
||||||
@ -804,6 +814,9 @@ func (s *ss) scanOne(verb int, field interface{}) {
|
|||||||
if v, ok := field.(Scanner); ok {
|
if v, ok := field.(Scanner); ok {
|
||||||
err = v.Scan(s, verb)
|
err = v.Scan(s, verb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if err == os.EOF {
|
||||||
|
err = io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
s.error(err)
|
s.error(err)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
@ -673,3 +673,62 @@ func TestUnreadRuneWithBufio(t *testing.T) {
|
|||||||
t.Errorf("expected αb; got %q", a)
|
t.Errorf("expected αb; got %q", a)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TwoLines string
|
||||||
|
|
||||||
|
// Attempt to read two lines into the object. Scanln should prevent this
|
||||||
|
// because it stops at newline; Scan and Scanf should be fine.
|
||||||
|
func (t *TwoLines) Scan(state ScanState, verb int) os.Error {
|
||||||
|
chars := make([]int, 0, 100)
|
||||||
|
for nlCount := 0; nlCount < 2; {
|
||||||
|
c, err := state.GetRune()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
chars = append(chars, c)
|
||||||
|
if c == '\n' {
|
||||||
|
nlCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*t = TwoLines(string(chars))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMultiLine(t *testing.T) {
|
||||||
|
input := "abc\ndef\n"
|
||||||
|
// Sscan should work
|
||||||
|
var tscan TwoLines
|
||||||
|
n, err := Sscan(input, &tscan)
|
||||||
|
if n != 1 {
|
||||||
|
t.Errorf("Sscan: expected 1 item; got %d", n)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Sscan: expected no error; got %s", err)
|
||||||
|
}
|
||||||
|
if string(tscan) != input {
|
||||||
|
t.Errorf("Sscan: expected %q; got %q", input, tscan)
|
||||||
|
}
|
||||||
|
// Sscanf should work
|
||||||
|
var tscanf TwoLines
|
||||||
|
n, err = Sscanf(input, "%s", &tscanf)
|
||||||
|
if n != 1 {
|
||||||
|
t.Errorf("Sscanf: expected 1 item; got %d", n)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Sscanf: expected no error; got %s", err)
|
||||||
|
}
|
||||||
|
if string(tscanf) != input {
|
||||||
|
t.Errorf("Sscanf: expected %q; got %q", input, tscanf)
|
||||||
|
}
|
||||||
|
// Sscanln should not work
|
||||||
|
var tscanln TwoLines
|
||||||
|
n, err = Sscanln(input, &tscanln)
|
||||||
|
if n != 0 {
|
||||||
|
t.Errorf("Sscanln: expected 0 items; got %d: %q", n, tscanln)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
t.Error("Sscanln: expected error; got none")
|
||||||
|
} else if err != io.ErrUnexpectedEOF {
|
||||||
|
t.Errorf("Sscanln: expected io.ErrUnexpectedEOF (ha!); got %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user