簡體   English   中英

base64解碼器(io.Reader實現)不當行為

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM