从 Golang 中的流中读取第一个字节

问题描述 投票:0回答:2

我在Golang中有以下功能:


func ReadFromStdinIfAvailable(cmd *cobra.Command, args []string) ([]byte, error) {
    if len(args) == 0 {
        r := bufio.NewReader(os.Stdin)
        firstByte, err := r.Peek(1)
        if err != nil {
            return nil, err
        }

        if len(firstByte) == 0 {
            cmd.Println(userstrings.NoFilenameProvidedErrorString)
            return nil, errors.New(userstrings.NoFilenameProvidedErrorString)
        }

        var bytesResult []byte
        scanner := bufio.NewScanner(r)
        for scanner.Scan() {
            // Read from stdin - allocate a buffer to read into
            bytesResult = append(bytesResult, scanner.Bytes()...)
        }
        return bytesResult, nil
    }
    return nil, errors.New(userstrings.NoStdInProvidedErrorString)
}

数据量非常小(<10 MB to be sure). However, the data should be there from the first read - if it's not, there's an error.

但是,当我这样做时,它会挂在

scanner.Scan()
上,这是一个流。

在开始阅读之前,如何查看

os.Stdin
流中是否有任何数据?

go
2个回答
0
投票

答案是从流中读取前

n
字节,然后创建一个新的
io.Reader
,其中包含您已读取的相同数据,然后是流中的剩余数据。

这里是显示此示例的游乐场链接

reader := strings.NewReader("i am the very model of a modern major general")

buf := make([]byte, 10) // Read first 10 bytes
_, _ = io.ReadAtLeast(reader, buf, 10)

fmt.Println(string(buf))

// Make a new stream that concats the data you read with what is remaining
newReader := io.MultiReader(bytes.NewReader(buf), reader)
data, _ := io.ReadAll(newReader)

fmt.Println(string(data))

-1
投票

这就是我最终所做的 - 如果这对任何人有帮助(希望有人做得更好):


func ReadFromStdinIfAvailable(cmd *cobra.Command, args []string) ([]byte, error) {
    if len(args) == 0 {
        r := bufio.NewReader(RootCmd.InOrStdin())
        var bytesResult []byte
        scanner := bufio.NewScanner(r)

        // buffered channel of dataStream
        dataStream := make(chan []byte, 1)

        // Run scanner.Bytes() function in it's own goroutine and pass back it's
        // response into dataStream channel.
        go func() {
            for scanner.Scan() {
                dataStream <- scanner.Bytes()
            }
            close(dataStream)
        }()

        // Listen on dataStream channel AND a timeout channel - which ever happens first.
        timedOut := false
        select {
        case res := <-dataStream:
            bytesResult = append(bytesResult, res...)
        case <-time.After(time.Duration(10) * time.Millisecond): //nolint:gomnd // 10ms timeout
            timedOut = true
        }

        if timedOut {
            cmd.Println(userstrings.NoFilenameProvidedErrorString)
            return nil, errors.New(userstrings.NoFilenameProvidedErrorString)
        }
        for scanner.Scan() {
            bytesResult = append(bytesResult, scanner.Bytes()...)
        }

        return bytesResult, nil
    }
    return nil, fmt.Errorf("should not be possible, args should be empty")
}
© www.soinside.com 2019 - 2024. All rights reserved.