Go: Copy data from io.Reader to io.Writer implementations with sleep timeout, empty Writes

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

Go: Copy data from io.Reader to io.Writer implementations with sleep timeout, empty Writes

问题

这是一个关于在io.Reader实现和io.Writer实现之间复制数据的代码示例,每次迭代之间有一个延迟(time.Sleep)。代码中尝试了4种不同的方法,但都无法正确写入数据,只写入了空字符串。你可能遗漏了一些基本的东西,需要进一步解释和帮助。

以下是代码的翻译:

package main

import (
	"bufio"
	"fmt"
	"io"
	"log"
	"math/rand"
	"time"
)

// 读取
type MyReader struct{}

func (r *MyReader) Read(p []byte) (read int, err error) {

	someString := fmt.Sprintf("testing-%d", rand.Int())

	p = []byte(someString)
	read = len(p)

	log.Printf("读取了 %d 字节: %s\n", read, string(p))

	return read, io.EOF
}

// 写入
type MyWriter struct{}

func (w *MyWriter) Write(p []byte) (wrote int, err error) {

	wrote = len(p)
	log.Printf("写入了 %d 字节: %s\n", wrote, string(p))

	return
}

// 主函数
func main() {

	// 以下包含了对从自定义的io.Reader实现到io.Writer实现进行数据复制和日志记录的4种不同方法的注释和函数。
	// 您可以在下面的函数中取消/注释以观察结果。

	// AutoIoCopy - 方法1)使用io.Copy
	//
	// 预期结果是读取并记录正确的值(在方法中生成),
	// 然后写入该值(输出另一个日志)
	//
	// 实际结果是调用了bufio.Write方法和io.Writer实现的MyWriter.Write方法,
	// 但MyWriter.Write记录的输出为空字符串,而不是MyReader.Read日志中预期的字符串。
	//
	AutoIoCopy()

	// ReadBytesApproach - 方法2)使用ReadBytes('\n')
	//
	// 预期结果是读取并记录正确的值(在方法中生成),
	// 然后写入该值(输出另一个日志)
	//
	// 实际结果是调用了bufio.Write方法并报告了写入的字节数,
	// 但MyWriter的Write方法(io.Writer实现)从未执行,它被跳过并进行了另一次读取。
	//
	//ReadBytesApproach()

	// ReadLineApproach - 方法3)使用ReadLine()
	//
	// 预期结果是读取并记录正确的值(在方法中生成),
	// 然后写入该值(输出另一个日志)
	//
	// 实际结果是调用了bufio.Write方法并报告了写入的字节数,
	// 但MyWriter的Write方法(io.Writer实现)从未执行,它被跳过并进行了另一次读取。
	//
	//ReadLineApproach()

	// WriteToApproach - 方法4)使用WriteTo()
	//
	// 预期结果是读取并记录正确的值(在方法中生成),
	// 然后写入该值(输出另一个日志)
	//
	// 实际结果是bufio.Write和MyWriter的Write方法(io.Writer实现)都未执行,
	// 它们被跳过并进行了另一次读取。
	//
	//WriteToApproach()

}

// 方法:

// AutoIoCopy - 方法1)使用io.Copy
//
// 预期结果是读取并记录正确的值(在方法中生成),
// 然后写入该值(输出另一个日志)
//
// 实际结果是调用了bufio.Write方法和io.Writer实现的MyWriter.Write方法,
// 但MyWriter.Write记录的输出为空字符串,而不是MyReader.Read日志中预期的字符串。
//
func AutoIoCopy() {

	reader := MyReader{}
	writer := MyWriter{}

	for {
		_, _ = io.Copy(&writer, &reader)
		time.Sleep(1000 * time.Millisecond)
	}
}

// ReadBytesApproach - 方法2)使用ReadBytes('\n')
//
// 预期结果是读取并记录正确的值(在方法中生成),
// 然后写入该值(输出另一个日志)
//
// 实际结果是调用了bufio.Write方法并报告了写入的字节数,
// 但MyWriter的Write方法(io.Writer实现)从未执行,它被跳过并进行了另一次读取。
//
func ReadBytesApproach() {

	reader := MyReader{}
	writer := MyWriter{}

	bufRead := bufio.NewReader(&reader)
	bufWrite := bufio.NewWriter(&writer)

	for {

		// 使用ReadBytes('\n')
		readBytes, err := bufRead.ReadBytes('\n')

		if err != nil {

			switch err {
			case io.EOF:

				log.Printf("检测到io.EOF\n")
				wrote, err := bufWrite.Write(readBytes)
				if err != nil {
					log.Printf("写入错误:%s\n", err)
				}
				convertedValue := string(readBytes)
				log.Printf("bufio写入了 %d 字节:%s\n", wrote, convertedValue)
				break

			default:
				log.Printf("bufio读取错误:%s\n", err.Error())
				break
			}

		} else {
			log.Printf("没有错误,继续读取\n")
		}

		time.Sleep(1000 * time.Millisecond)
	}
}

// ReadLineApproach - 方法3)使用ReadLine()
//
// 预期结果是读取并记录正确的值(在方法中生成),
// 然后写入该值(输出另一个日志)
//
// 实际结果是调用了bufio.Write方法并报告了写入的字节数,
// 但MyWriter的Write方法(io.Writer实现)从未执行,它被跳过并进行了另一次读取。
//
func ReadLineApproach() {

	reader := MyReader{}
	writer := MyWriter{}

	bufRead := bufio.NewReader(&reader)
	bufWrite := bufio.NewWriter(&writer)

	for {

		// 使用ReadLine()
		readBytes, _, err := bufRead.ReadLine()

		if err != nil {

			switch err {
			case io.EOF:

				log.Printf("检测到io.EOF\n")
				wrote, err := bufWrite.Write(readBytes)
				if err != nil {
					log.Printf("写入错误:%s\n", err)
				}
				convertedValue := string(readBytes)
				log.Printf("bufio写入了 %d 字节:%s\n", wrote, convertedValue)
				break

			default:
				log.Printf("bufio读取错误:%s\n", err.Error())
				break
			}

		} else {
			log.Printf("没有错误,继续读取\n")
		}

		time.Sleep(1000 * time.Millisecond)
	}

}

// WriteToApproach - 方法4)使用WriteTo()
//
// 预期结果是读取并记录正确的值(在方法中生成),
// 然后写入该值(输出另一个日志)
//
// 实际结果是bufio.Write和MyWriter的Write方法(io.Writer实现)都未执行,
// 它们被跳过并进行了另一次读取。
//
func WriteToApproach() {

	reader := MyReader{}
	writer := MyWriter{}

	bufRead := bufio.NewReader(&reader)
	bufWrite := bufio.NewWriter(&writer)

	for {
		wrote, _ := bufRead.WriteTo(bufWrite)
		log.Printf("bufio写入了 %d 字节\n", wrote)
		time.Sleep(1000 * time.Millisecond)
	}
}

希望对你有帮助!如果有任何进一步的问题,请随时提问。

英文:

-edit-

This was not caused by typos and it is reproducible, contrary to the designation given by stackoverflow. The code provided was written specifically to be self-contained examples of this issue (the issue is right there in the code, all you have to do is run it to reproduce the issues...)

Furthermore, this is the exact question I had and the exact solution I needed that I did not find on StackOverflow prior to asking the question. The answer was derived from a conversation on IRC and I have forwarded the solution here for others like myself who may experience this same problem. This is very much a relevant Q/A and certainly can help future readers.

Thank you

-edit-

I am trying to copy data from an io.Reader implementation to an io.Writer implementation with a delay (time.Sleep) before the next iteration. Ideally I want to control the process (i.e. io.Copy is not ideal because I may want to perform some action between the Read and Write).

In any case, there are 4 approaches attempted in the code below, it should run in go.dev/play. I was unable to get any of the approaches to Write anything but an empty string, though all of the approaches to Write did report the correct number of written bytes (the same as reported by Read). I am probably missing something fundamental, but any help/explanation is greatly appreciated as I am quite confused.

The following code contains comments and functions for 4 different approaches to copying and logging data from custom implementations of io.Reader to an io.Writer with a specified delay (time.Sleep) before the next iteration. You may comment/uncomment the desired function below to observe the results.

package main
import (
"bufio"
"fmt"
"io"
"log"
"math/rand"
"time"
)
// READ
type MyReader struct{}
func (r *MyReader) Read(p []byte) (read int, err error) {
someString := fmt.Sprintf("testing-%d", rand.Int())
p = []byte(someString)
read = len(p)
log.Printf("io read %d bytes: %s\n", read, string(p))
return read, io.EOF
}
// WRITE
type MyWriter struct{}
func (w *MyWriter) Write(p []byte) (wrote int, err error) {
wrote = len(p)
log.Printf("io wrote %d bytes: %s\n", wrote, string(p))
return
}
// MAIN
func main() {
// The following contains comments and functions for 4 different approaches to copying and logging data from
// custom implementations of io.Reader to an io.Writer with a specified delay (time.Sleep) before the next
// iteration. You may comment/uncomment the desired function below to observe the results.
// AutoIoCopy - Approach 1) io.Copy
//
// Expected is to read and log the correct value (generated in the method)
// then write the value (output another log)
//
// Actual is that the bufio.Write method is called and the MyWriter.Write method of the io.Writer implementation
// is executed, but the output logged by MyWriter.Write is empty instead of the expected string reported by the
// MyReader.Read log.
//
AutoIoCopy()
// ReadBytesApproach - Approach 2) Using ReadBytes('\n')
//
// Expected is to read and log the correct value (generated in the method)
// then write the value (output another log)
//
// Actual is that the bufio.Write method is called and reports written bytes, but the Write method of MyWriter
// io.Writer implementation is never executed, it is skipped and another Read occurs
//
//ReadBytesApproach()
// ReadLineApproach - Approach 3) Using ReadLine()
//
// Expected is to read and log the correct value (generated in the method)
// then write the value (output another log)
//
// Actual is that the bufio.Write method is called and reports written bytes, but the Write method of MyWriter
// io.Writer implementation is never executed, it is skipped and another Read occurs
//
//ReadLineApproach()
// WriteToApproach - Approach 4) Using WriteTo()
//
// Expected is to read and log the correct value (generated in the method)
// then write the value (output another log)
//
// Actual is that the bufio.Write method is called and reports written bytes, but the Write method of MyWriter
// io.Writer implementation is never executed, it is skipped and another Read occurs
//
//WriteToApproach()
}
// Approaches:
// AutoIoCopy - Approach 1) io.Copy
//
// Expected is to read and log the correct value (generated in the method)
// then write the value (output another log)
//
// Actual is that the bufio.Write method is called and the MyWriter.Write method of the io.Writer implementation
// is executed, but the output logged by MyWriter.Write is empty instead of the expected string reported by the
// MyReader.Read log.
//
func AutoIoCopy() {
reader := MyReader{}
writer := MyWriter{}
for {
_, _ = io.Copy(&writer, &reader)
time.Sleep(1000 * time.Millisecond)
}
}
// ReadBytesApproach - Approach 2) Using ReadBytes('\n')
//
// Expected is to read and log the correct value (generated in the method)
// then write the value (output another log)
//
// Actual is that the bufio.Write method is called but the Write method of MyWriter io.Writer implementation
// is never executed, it is skipped and another Read occurs
//
func ReadBytesApproach() {
reader := MyReader{}
writer := MyWriter{}
bufRead := bufio.NewReader(&reader)
bufWrite := bufio.NewWriter(&writer)
for {
// Using ReadBytes('\n')
readBytes, err := bufRead.ReadBytes('\n')
if err != nil {
switch err {
case io.EOF:
log.Printf("io.EOF detected\n")
wrote, err := bufWrite.Write(readBytes)
if err != nil {
log.Printf("error writing: %s\n", err)
}
convertedValue := string(readBytes)
log.Printf("bufio wrote %d bytes: %s\n", wrote, convertedValue)
break
default:
log.Printf("bufio error reading: %s\n", err.Error())
break
}
} else {
log.Printf("no error, continue to read\n")
}
time.Sleep(1000 * time.Millisecond)
}
}
// ReadLineApproach - Approach 3) Using ReadLine()
//
// Expected is to read and log the correct value (generated in the method)
// then write the value (output another log)
//
// Actual is that the bufio.Write method is called but the Write method of MyWriter io.Writer implementation
// is never executed, it is skipped and another Read occurs
//
func ReadLineApproach() {
reader := MyReader{}
writer := MyWriter{}
bufRead := bufio.NewReader(&reader)
bufWrite := bufio.NewWriter(&writer)
for {
// Using ReadLine()
readBytes, _, err := bufRead.ReadLine()
if err != nil {
switch err {
case io.EOF:
log.Printf("io.EOF detected\n")
wrote, err := bufWrite.Write(readBytes)
if err != nil {
log.Printf("error writing: %s\n", err)
}
convertedValue := string(readBytes)
log.Printf("bufio wrote %d bytes: %s\n", wrote, convertedValue)
break
default:
log.Printf("bufio error reading: %s\n", err.Error())
break
}
} else {
log.Printf("no error, continue to read\n")
}
time.Sleep(1000 * time.Millisecond)
}
}
// WriteToApproach - Approach 4) Using WriteTo()
//
// Expected is to read and log the correct value (generated in the method)
// then write the value (output another log)
//
// Actual is that neither the bufio.Write or the Write method of MyWriter io.Writer implementation is executed,
// it is skipped and another Read occurs
//
func WriteToApproach() {
reader := MyReader{}
writer := MyWriter{}
bufRead := bufio.NewReader(&reader)
bufWrite := bufio.NewWriter(&writer)
for {
wrote, _ := bufRead.WriteTo(bufWrite)
log.Printf("bufio wrote %d bytes\n", wrote)
time.Sleep(1000 * time.Millisecond)
}
}

答案1

得分: 0

问题出在MyReader.Read方法中。

这行代码:

p = []byte(someString)

应该改为:

read = copy(p, someString)
英文:

The issue was in the MyReader.Read method

The line

p = []byte(someString)

Should be

read = copy(p, someString)

huangapple
  • 本文由 发表于 2021年12月5日 15:25:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/70232249.html
匿名

发表评论

匿名网友

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

确定