为了账号安全,请及时绑定邮箱和手机立即绑定

Read 函数完成后,Go rot13Reader 缓冲区未更新

Read 函数完成后,Go rot13Reader 缓冲区未更新

Go
哆啦的时光机 2023-07-10 09:03:03
这是我使用 strings.Map 实现的练习(rot13 函数直接来自 golang 的文档)。问题是在 Read 函数返回后缓冲区似乎没有被修改。这是代码:package mainimport (    "io"    "os"    "strings"    "fmt")type rot13Reader struct {    r io.Reader}func (reader *rot13Reader) Read(b []byte) (int, error) {    rot13 := func(r rune) rune {        switch {        case r >= 'A' && r <= 'Z':            return 'A' + (r-'A'+13)%26        case r >= 'a' && r <= 'z':            return 'a' + (r-'a'+13)%26        }        return r    }    n, err := reader.r.Read(b)    result := []byte(strings.Map(rot13, string(b)))    b = []byte(result)    fmt.Println(string(b))    return n, err}func main() {    s := strings.NewReader("Lbh penpxrq gur pbqr!")    r := rot13Reader{s}    io.Copy(os.Stdout, &r)}和输出:You cracked the code!Lbh penpxrq gur pbqr!You cracked the code!显然Read函数中缓冲区已经被修改了,但是返回后似乎不是这样。如果我注释掉fmt.Println(string(b)),输出将是:Lbh penpxrq gur pbqr!关于读者和作家有什么奇怪的事情我应该知道吗?
查看完整描述

2 回答

?
沧海一幻觉

TA贡献1824条经验 获得超5个赞

在 Go 中,所有参数都按值传递,就像通过分配给参数或接收者(浅拷贝)一样。


在 Go 中,切片的实现为


type slice struct {

    array unsafe.Pointer

    len   int

    cap   int

}

当切片按值传递时,返回后,您将看不到对字段副本所做的任何更改struct。您只会看到底层数组元素的任何更改。


在您的情况下,您将覆盖b( array, cap, len) 副本。


b = []byte(result)

当您返回时,该副本将被丢弃。


您想要做的是更改bs的元素array。


例如,


package main


import (

    "io"

    "os"

    "strings"

)


func rot13(b byte) byte {

    switch {

    case b >= 'A' && b <= 'Z':

        return 'A' + (b-'A'+13)%26

    case b >= 'a' && b <= 'z':

        return 'a' + (b-'a'+13)%26

    }

    return b

}


type rot13Reader struct {

    r io.Reader

}


func (reader *rot13Reader) Read(b []byte) (int, error) {

    n, err := reader.r.Read(b)

    b = b[:n]

    for i := range b {

        b[i] = rot13(b[i])

    }

    return n, err

}


func main() {

    s := strings.NewReader("Lbh penpxrq gur pbqr!")

    r := rot13Reader{s}

    io.Copy(os.Stdout, &r)

}


查看完整回答
反对 回复 2023-07-10
?
烙印99

TA贡献1829条经验 获得超13个赞

我不太确定,所以请在服用下面的食物时加入几粒到几磅的盐。


首先,您应该尽早添加错误检查:


n, err := reader.r.Read(b)

if err != nil && err == io.EOF {

    fmt.Printf("\n%s, %d bytes read", err, n)

    return n, err

}

添加后,输出就是您所期望的:


You cracked the code!

Lbh penpxrq gur pbqr!

EOF, 0 bytes read

这里的原因是读者应该返回 io.EOF,以防没有任何内容可供阅读。


那么你为什么会经历上述奇怪的行为呢?快速浏览一下源代码就会io.Copy发现,b分配一次并重用。但由于b未修改(未读取任何字节)并且您访问它以从中读取,因此它仍然保留与以前相同的值。不过,我认为,根据最小意外原则,如果没有读到任何内容,底层io.Reader应该是清晰的。b



查看完整回答
反对 回复 2023-07-10
  • 2 回答
  • 0 关注
  • 108 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信