我已经尝试在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个字节。出于什么原因,它只读取1个字节?
答案 0 :(得分:0)
这不是encoding/base64
的问题。使用io.Reader
时,不能保证读取的字节数完全等于缓冲区大小(即示例代码中的blocksize
)。文档说明:
读取读取最多len(p)个字节到p。它返回读取的字节数(0 <= n <= len(p))和遇到的任何错误。即使Read返回n&lt; len(p),它可以在调用期间将所有p用作临时空间。如果某些数据可用但不是len(p)字节,则Read通常返回可用而不是等待更多的数据。
在您的示例中,将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
数据。