Go – Scanf and ReadString having conflicts with newline when taking input from redirected .txt file

huangapple go评论86阅读模式
英文:

Go - Scanf and ReadString having conflicts with newline when taking input from redirected .txt file

问题

我真的想了解一下在从重定向的标准输入读取时,Go语言的ReadString和Scanf函数是否存在冲突。开发环境是Ubuntu。

我目前有一个递归函数,每次迭代中它都会要求用户输入要测试的整数数量,然后在新创建的“testcase”对象中进行测试。以下是该函数的代码:

func (T *Test) Testing() {
    if I == T.N {
        return
    } else {
        // 询问每个测试用例中整数的数量
        var input string
        x := 0

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

        // 首先检查用户输入是否正确
        if err != nil {
            fmt.Println("Error: ", err, "LOL")
            ProgramExit()
        }

        // 以字符串形式接收输入(以适应将整数放在一行中,以空格分隔的整数),在换行符处终止
        input, err = in.ReadString('\n')

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

        // 首先检查用户输入是否正确
        if err != nil {
            fmt.Println("Error: ", err, "Errorororo")
            ProgramExit()
        }

        // 拆分输入中的空格
        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)
        }

        // 创建新的TestCase对象,并将其sum和i归零
        T.Tst[I] = &TestCase{x, make([]int, x), 0, 0}

        // 填充TestCase对象
        T.Tst[I].Fill(input_array)

        // 增加I
        I++

        // 迭代,通过递归返回方法,而不是使用for循环
        T.Testing()
    }
}

为了澄清,ProgramExit()只是一个在发生错误时退出整个程序的函数。而Fill()是一个使用input_array的数据填充T对象的函数。

现在我的问题是,当我使用上述循环运行程序时,它可以成功读取我在命令行中手动输入的输入:

(输入的格式为:

测试用例的数量

测试用例1的整数数量

测试用例1的输入整数

测试用例2的整数数量

测试用例2的输入整数

...)

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

然而,当我将相同的输入放入一个.txt文件中,并将其重定向为程序的输入时,它会出现EOF错误,似乎发生在函数的第二次迭代时(即尝试读取第二个测试用例对象时)。以下是我在CLI中得到的输出:

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

当我为每次成功迭代添加一个println函数以打印I的值(第I次迭代)时,输出如下:

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

因此,似乎我的函数在迭代后使用ReadString读取整行后,Scanf会将下一行读取为换行符(或EOF),而不是正确读取下一行。(当我手动输入整数时,这种情况不会发生)。

请问是否有人能够指出我的函数是否存在问题,或者是否有其他正确的方法来解决这个问题?(请注意,我正在为一个挑战开发这个程序,在这个挑战中,不允许使用for循环lol)

提前感谢!

英文:

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(&quot;%v\n&quot;, &amp;x)


	//check for correct user input first
	if err != nil {
		fmt.Println(&quot;Error: &quot;, err, &quot;LOL&quot;)
		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(&#39;\n&#39;)

	input = strings.Trim(input, &quot;\n&quot;)

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

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

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

	//make new TestCase object, with its sum and i zeroed first
	T.Tst[I] = &amp;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 &lt; 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 &lt; 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!

答案1

得分: 2

我猜测是因为在第一次迭代后,Scanf 将你在末尾留下的 \n 视为 EOF。

看着你的代码很难猜测你出现了什么错误。但是也许可以尝试一下:

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

而不是

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

并确保你的文本文件格式正确(注意换行符)。或者使用一个更宽容的扫描器(参见下面的内容):

> Scanf 从标准输入中扫描文本,根据格式将连续的以空格分隔的值存储到连续的参数中。它返回成功扫描的项目数。如果成功扫描的项目数少于参数数目,err 将报告原因。输入中的换行符必须与格式中的换行符匹配。唯一的例外是:%c 动词总是扫描输入中的下一个符文,即使它是空格(或制表符等)或换行符。

参见 ReadString 文档:

> ReadString 从输入中读取,直到第一次出现分隔符为止,返回包含数据和分隔符在内的字符串。如果在找到分隔符之前遇到错误,它将返回错误之前读取的数据和错误本身(通常是 io.EOF)。只有当返回的数据不以分隔符结尾时,ReadString 才会返回 err != nil。对于简单的用法,Scanner 可能更方便。

你可能也会从 ReadString 中获得 EOF,同样地,很难根据你的输出判断,因为它没有你的调试日志(LOL 等)。

如果你打印出它实际读取的内容可能会有帮助。我建议考虑使用 Scanner

英文:

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(&quot;%v%c&quot;, &amp;x)

instead of

_,err = fmt.Scanf(&quot;%v\n&quot;, &amp;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.

答案2

得分: 0

感谢那些试图帮助我的人!在再次搜索后,我找到了更合适的方法来输入一行整数。显然,这里已经讨论过一种方法:https://stackoverflow.com/questions/39565055/read-a-set-of-integers-separated-by-space-in-golang。

我基本上替换了我的字符串输入方法,而是在每个测试用例值的迭代中使用fmt.Scan()进行迭代,以正确输入整数。再次感谢那些试图帮助我的人,并对我没有更早找到谷歌答案表示歉意,呵呵!

英文:

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: https://stackoverflow.com/questions/39565055/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!

huangapple
  • 本文由 发表于 2017年2月18日 18:53:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/42314190.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定