Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fmt: diagnose when Scanf consumes a newline not matched by the format #8944

Closed
gopherbot opened this issue Oct 16, 2014 · 5 comments
Closed

fmt: diagnose when Scanf consumes a newline not matched by the format #8944

gopherbot opened this issue Oct 16, 2014 · 5 comments
Assignees
Milestone

Comments

@gopherbot
Copy link

@gopherbot gopherbot commented Oct 16, 2014

by amirtaghavi2020:

The code : 
package main

import (
    "fmt"
)

var(
    i int
    first [2]int
    second [2]int
    result [2]int
)

func main(){
    fmt.Println("Enter first array:\r\n")
    for i=0;i<2;i++{
        _,err:=fmt.Scanf("%d",&first[i])
        if err!=nil{
            fmt.Println(err)
        }
    }
    fmt.Println("First[0]=",first[0],"First[1]=",first[1])
        
    
    fmt.Println("Enter second array:")
    for i=0;i<2;i++{
        _,err:=fmt.Scanf("%d",&second[i])
                if err!=nil{
                    fmt.Println(err)
                }
    }
    fmt.Println("Second[0]=",second[0],"Second[1]=",second[1]) //this is problem
    
    for i=0;i<2;i++{
        result[i] = first[i] + second[i]
    }   
}

when run it 
it asked Enter first array: next i type 10SPACE20NEWLINE next 
it asked Enter second array: then i type 20SPACE30NEWLINE

Enter first array:

10 20 
First[0]=10 First[1]=20
Enter second array:
30 40
Second[0]=0 Second[1]=30 // this is problem - i type 30 but second[0] give 0 value

but on linux , the same code result is correct;

my operating system is windows

my go version is :  go1.3 windows/amd64
@ianlancetaylor
Copy link
Contributor

@ianlancetaylor ianlancetaylor commented Oct 16, 2014

Comment 1:

I don't know if there is anything to change here.  I think the difference between
GNU/Linux and Windows is that Windows generates \r\n.  Your formats don't use any
spaces, and you are reading one element at a time.  Reading one element at a time means
that the scanner starts fresh each time.  Not using any spaces means that spaces are not
skipped.  Reading the second number on the first line picks up the first character after
the 20.  On Windows this is \r, on GNU/Linux this is \n.  That stops the %d, so the
scanner stops.  Your next call has forgotten the previous state, so it reads the next
character.  On Windows this is \n, on GNU/Linux it is the first character of the next
line.
We could probably make the behaviour more consistent across operating systems by rigidly
looking for \n following \r, and treating them as a single character.  I'll leave this
issue open in case we think that is a good choice.
For your particular case, though, you should probably write your code differently.  If
you want to read a line at a time, then read a line at a time and call Sscanf on that
line.

Labels changed: added repo-main, release-go1.5, os-windows.

Status changed to Accepted.

@rsc
Copy link
Contributor

@rsc rsc commented Jun 29, 2015

Portable demonstration / test case.
http://play.golang.org/p/ULvAgEwejo

package main

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

var (
    i      int
    first  [2]int
    second [2]int
    result [2]int
)

func main() {
    for _, sep := range []string{"\n", "\r\n"} {
        r, w, err := os.Pipe()
        if err != nil {
            log.Fatal(err)
        }
        os.Stdin = r

        input := "10 20" + sep + "30 40" + sep
        fmt.Printf("input = %q\n", input)
        w.Write([]byte(input))
        for i := 0; i < 4; i++ {
            var x int
            _, err := fmt.Scanf("%d", &x)
            if err != nil {
                log.Fatal(err)
            }
            fmt.Printf("#%d: %v\n", i, x)
        }
    }
}
@rsc
Copy link
Contributor

@rsc rsc commented Jun 29, 2015

My memory is that Rob was already working on this.

@robpike robpike modified the milestones: Go1.6, Go1.5 Jun 30, 2015
@robpike
Copy link
Contributor

@robpike robpike commented Jun 30, 2015

The test program is erroneous. The issue is that the input contains newlines but the pattern ("%d") does not. The documentation clearly states:
Newlines in the input must match newlines in the format.
Unfortunately, there is a catch that the program tickles. The second number, the one before the newline in the input, is terminated one of two ways in the program: \n or \r\n. The \r is a red herring; a plain space will trigger the same behavior, which is: if there is only a newline, the newline must be read to terminate the scanning of the number. Scanf then returns, and the newline has been consumed silently. As the documentation says, it scanning may read one byte past the input desired, which in this case consumes the newline.

If the number is instead followed by \r or ' ', that is consumed, the newline remains in the input stream, and the next call to Scanf fails because there is an unexpected newline, as diagnosed.

If you use Scan instead of Scanf, the program works just fine regardless. Scanf is fussy about newlines, on purpose (you have Scan if you don't care about them), but is sometimes thwarted by the clean separation between the fmt package and I/O, and the consequences of reading too far.

I admit this is unsatisfactory but it is working as documented, if not exactly as intended.

One possibility is for Scanf to notice this case and complain on the previous call that it is consuming a newline that cannot be matched by the pattern, but I am reluctant to do that this late in the cycle.

Deferring to 1.6. I will also change the title of the bug.

@robpike robpike changed the title fmt: consistently treat \r\n as \n fmt: diagnose when Scanf consumes a newline not matched by the format Jun 30, 2015
@rsc rsc modified the milestones: Unplanned, Go1.6 Nov 25, 2015
@robpike
Copy link
Contributor

@robpike robpike commented Oct 26, 2016

I believe this is fixed in 1.8. Please reopen if that is not the case.

@robpike robpike closed this Oct 26, 2016
@golang golang locked and limited conversation to collaborators Oct 26, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
5 participants
You can’t perform that action at this time.