[英]base64 decoder (io.Reader implementation) misbehaviour
我已經嘗試在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個字節?
這不是encoding/base64
的問題。 使用io.Reader
,不能保證讀取的字節數完全等於緩沖區大小(即示例代碼中的blocksize
)。 文件說明:
讀取讀取最多len(p)個字節到p。 它返回讀取的字節數(0 <= n <= len(p))和遇到的任何錯誤。 即使Read返回n <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
時始終處理數據,即使發生錯誤也是如此。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.