【问题标题】:base64 decoder (io.Reader implementation) misbehaviourbase64 解码器(io.Reader 实现)行为不端
【发布时间】:2017-10-31 00:32:46
【问题描述】:

我已经尝试在一个 for 循环中重新声明/分配一个 base64 解码器并使用 os.Seek 函数在此之前的循环结束时返回到文件的开头,以便调用函数(在此测试用例中为 PrintBytes)能够在整个 for 循环中一次又一次地从头到尾处理文件。

这是我的(我确信非常不习惯)代码,在 main() 中的 main for 循环的第二次迭代期间,它无法将第二个字节读入长度为 2 和容量为 2 的 [] 字节:

package main

import (
    "encoding/base64"
    "io"
    "log"
    "net/http"
    "os"
)

var (
    remote_file string = "http://cryptopals.com/static/challenge-data/6.txt"
    local_file  string = "secrets_01_06.txt"
)

func main() {
    f, err := os.Open(local_file)
    if err != nil {
        DownloadFile(local_file, remote_file)
        f, err = os.Open(local_file)
        if err != nil {
            log.Fatal(err)
        }
    }
    defer f.Close()

    for blocksize := 1; blocksize <= 5; blocksize++ {
        decoder := base64.NewDecoder(base64.StdEncoding, f)
        PrintBytes(decoder, blocksize)
        _, err := f.Seek(0, 0)
        if err != nil {
            log.Fatal(err)
        }
    }
}

func PrintBytes(reader io.Reader, blocksize int) {
    block := make([]byte, blocksize)
    for {
        n, err := reader.Read(block)
        if err != nil && err != io.EOF {
            log.Fatal(err)
        }
        if n != blocksize {
            log.Printf("n=%d\tblocksize=%d\tbreaking...", n, blocksize)
            break
        }
        log.Printf("%x\tblocksize=%d", block, blocksize)
    }
}

func DownloadFile(local string, url string) {
    f, err := os.Create(local)
    if err != nil {
        log.Fatal(err)
    }
    defer f.Close()

    resp, err := http.Get(url)
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()

    _, err = io.Copy(f, resp.Body)
    if err != nil {
        log.Fatal(err)
    }
}

这段代码的输出可以在这里查看https://gist.github.com/tomatopeel/b8e2f04179c7613e2a8c8973a72ec085

我不明白的是这种行为: https://gist.github.com/tomatopeel/b8e2f04179c7613e2a8c8973a72ec085#file-bad_reader_log-L5758

我希望它从头到尾一次简单地将文件读取 2 个字节到 2 字节片中。为什么这里只读取 1 个字节?

【问题讨论】:

  • 我不认为 base64 解码器是无状态的,所以 Seeking 底层阅读器在从 base64 阅读器读取之间可能会产生不可预知的结果。
  • 这段代码的意图非常不清楚。我不得不回到最初的挑战来了解迭代块大小的情况(这看起来很奇怪,因为它没有使用累积的 n 来确保它最终读取块大小的字节)。只需asBytes, err := ioutil.ReadAll(base64.NewDecoder(base64.StdEncoding, f)),然后继续处理数据作为字节。 (即:块长度之间的距离计算等)
  • @Rob 此处代码的唯一目的是作为问题的演示。这是针对给定挑战的一些明显有缺陷的代码的修改摘录。我只是继续使用挑战中的文件,我想是出于懒惰。不过,我会考虑使用累积的 n,谢谢,我是 golang 的新手。

标签: file go io reader


【解决方案1】:

不是encoding/base64的问题。使用io.Reader 时,不能保证读取的字节数完全等于缓冲区大小(即示例代码中的blocksize)。文档指出:

Read 最多将 len(p) 个字节读入 p。它返回读取的字节数 (0

在您的示例中,将 PrintBytes 更改为

func PrintBytes(reader io.Reader, blocksize int) {
    block := make([]byte, blocksize)
    for {
        n, err := reader.Read(block)
        //Process the data if n > 0, even when err != nil
        if n > 0 {
            log.Printf("%x\tblocksize=%d", block[:n], blocksize)
        }

        //Check for error
        if err != nil {
            if err != io.EOF {
                log.Fatal(err)
            } else if err == io.EOF {
                break
            }
        } else if n == 0 {
            //Considered as nothing happened
            log.Printf("WARNING: read return 0,nil")
        }
    }
}

更新:

正确使用io.Reader,修改代码以在n &gt; 0即使发生错误时也始终处理数据。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-26
    • 2011-05-18
    • 1970-01-01
    • 2015-03-21
    • 1970-01-01
    相关资源
    最近更新 更多