3

I really want to understand whether there are conflicts regarding Go's ReadString and Scanf functions when reading from a redirected standard input. FYI, developement is done on Ubuntu.

I currently have a recursive function that runs recursively, where in each iteration it asks the user to input the number of integers that will be tested, for then to have a testing session be done within a newly created 'testcase' object. So here's the following code for the function:

func (T *Test) Testing() {

if I == T.N {
    return
} else {
    //ask for number of integers per testcase
    var input string
    x:=0

    _,err = fmt.Scanf("%v\n", &x)


    //check for correct user input first
    if err != nil {
        fmt.Println("Error: ", err, "LOL")
        ProgramExit()
    }


    //receive string as input (to accomodate the itegers that are going to be put in a line, with space-seperated integers), terminate at newline
    input,err = in.ReadString('\n')

    input = strings.Trim(input, "\n")

    //check for correct user input first
    if err != nil {
        fmt.Println("Error: ", err, "Errorororo")
        ProgramExit()
    }

    //split any spaces in the input
    input_array := strings.Split(input, " ") 

    if l := len(input_array); l!=x {
        fmt.Printf("inconsistent length of input! Inputted number of integers is %v, being ", l)
        fmt.Println(input_array)
        os.Exit(1)
    } 

    //make new TestCase object, with its sum and i zeroed first
    T.Tst[I] = &TestCase{x,make([]int,x),0,0} 

    //fill TestCase object
    T.Tst[I].Fill(input_array) 

    //increase I
    I++ 

//this is where println could be added to check for iterations

    //iterate by doing recursion back to method, instead of doing a for loop
    T.Testing() 
}

For clarification, ProgramExit() is just a function to exit the entire program when error occurs. While Fill() is a function to fill up T object using input_array's data.

Now my problem is that when I run the program with the mentioned loop, it can successfully read the inputs that I manually typed into the command line below:

(format of input is:

num of testcases

num of integers for testcase 1

num of input integers for testcase 1

num of integers for testcase 2

num of input integers for testcase 2

...

)

2
4
3 -1 1 14
5
9 6 -53 32 16

However when I put the same inputs into a .txt file and redirect that as the input for my program, it gets an EOF error, that seems to happen during the 2nd iteration of the function (i.e. when it tries to read in for 2nd testcase object). Here is the output that I got in the CLI:

$ main < data.txt
0
Error:  EOF LOL
Incorrect input! needs to be an integer input!

And this is the output when I put a println function for every successful iteration to print out value of I (I'th iteration)

$ main < data.txt
1
0
Error:  EOF LOL
Incorrect input! needs to be an integer input!

So it does seem that my function is iterating properly however unfortunately after reading the an entire line with ReadString, Scanf would read the next line as a newline (or EOF), instead of reading the next line properly. (Which does not happen when I inputted the integers manually).

Could anyone please help point out whether there is a problem with my function that does this, or is there an alternative way to do this properly? (please note that I am developing this for a challenge, and in this challenge, for loop is not allowed lol)

Thanks in advance!

Nicholas Sadjoli
  • 375
  • 1
  • 3
  • 11

2 Answers2

2

My guess is that Scanf is treating the \n you leave at the end after 1st iteration as the EOF.

It's hard to guess where you are getting that error, looking at your code. But maybe try;

_,err = fmt.Scanf("%v%c", &x)

instead of

_,err = fmt.Scanf("%v\n", &x)

And make sure your text file is formatted (beware of the newlines) as expected. Or use a more forgiving scanner(see below),

Scanf scans text read from standard input, storing successive space-separated values into successive arguments as determined by the format. It returns the number of items successfully scanned. If that is less than the number of arguments, err will report why. Newlines in the input must match newlines in the format. The one exception: the verb %c always scans the next rune in the input, even if it is a space (or tab etc.) or newline.

See ReadString doc;

ReadString reads until the first occurrence of delim in the input, returning a string containing the data up to and including the delimiter. If ReadString encounters an error before finding a delimiter, it returns the data read before the error and the error itself (often io.EOF). ReadString returns err != nil if and only if the returned data does not end in delim. For simple uses, a Scanner may be more convenient.

You may be getting the EOF from ReadString as well, again, hard to say looking at your output as it has neither of your debug logs (LOL etc.)

Could be helpful if you print what it actually reads. And i'd consider using Scanner instead.

Kerem
  • 2,867
  • 24
  • 35
  • Hmm, noted on the debug logs. Actually it ended on the one with LOL. So it is terminating right when 2nd iteration tries to read further inputs. Will edit the content asap. Thanks :) – Nicholas Sadjoli Feb 18 '17 at 17:28
  • Also, if ReadString could prove to be problematic like this, could you suggest to me which other function that could be used to read a line of integers like that? I've tried using Scanf("%s") previously, however scanf separates the spaces and hence the entire line cannot be read as a string :( – Nicholas Sadjoli Feb 18 '17 at 17:44
0

Thanks for those who tried to help! I found the more proper way to input in a line of integers after googling around again. Apparently there is a method already discussed here: Read a set of integers separated by space in Golang.

I basically replaced my strings input method and instead do an iteration of fmt.Scan() in each iteration of the testcase values to input the integers properly. Once again many thanks for those who tried to help, and apologies that I didn't find the google answer sooner hehe!

Community
  • 1
  • 1
Nicholas Sadjoli
  • 375
  • 1
  • 3
  • 11
  • It's a better practice for SO to edit your question and add your resolution to the end of it (what worked for you etc.) instead of an answer for those who will visit your question with the same problem later on. I'm glad it's resolved for you. – Kerem Feb 18 '17 at 22:48