file - base64 解码器(io.Reader 实现)不当行为

标签 file go io reader

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

这是我的(我敢肯定非常不合惯用的)代码,在 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 字节的 slice 中。为什么这里只读取了1个字节?

最佳答案

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

Read reads up to len(p) bytes into p. It returns the number of bytes read (0 <= n <= len(p)) and any error encountered. Even if Read returns n < len(p), it may use all of p as scratch space during the call. If some data is available but not len(p) bytes, Read conventionally returns what is available instead of waiting for more.

在您的示例中,将 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 > 0 时始终处理数据,即使发生错误也是如此。

关于file - base64 解码器(io.Reader 实现)不当行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44263835/

相关文章:

Haskell:将计算结果写入文件

android - 让应用程序播放3GPP音频文件

java - 如何在 Java 中更改 mp3 的标题

linux 命令 "file"显示 "for GNU/Linux 2.6.24"

go - 如何将 http.Request 包装到 proto 文件中

networking - 下载管理器如何将文件分成多个部分?

java - 从磁盘读取文件并将文件加载到主存中进行处理

mongodb - 通过 ssl 将 golang mgo 连接到 mongo

go - 我应该如何避免在微服务架构中多次实现我的方法

c - 将 fgetc() 的结果可移植地分配给 C 中的 char 的最佳方法