0

我正在尝试在 Go 中实现 PPM 解码器。PPM 是一种图像格式,由纯文本标题和一些二进制图像数据组成。标头看起来像这样(来自规范):

每个 PPM 映像包含以下内容:

  1. 用于识别文件类型的“幻数”。ppm 图像的幻数是两个字符“P6”。
  2. 空格(空格、TAB、CR、LF)。
  3. 宽度,格式为十进制的 ASCII 字符。
  4. 空白。
  5. 高度,同样是 ASCII 十进制。
  6. 空白。
  7. 最大颜色值 (Maxval),同样以 ASCII 十进制表示。必须小于 65536 且大于零。
  8. 单个空白字符(通常是换行符)。

我尝试使用该fmt.Fscanf函数解码此标头。以下调用 fmt.Fscanf解析标头(不解决下面解释的警告):

var magic string
var width, height, maxVal uint

fmt.Fscanf(input,"%2s %d %d %d",&magic,&width,&height,&maxVal)

国家的文件fmt

注意:Fscanetc. 可以在它们返回的输入之后读取一个字符(符文),这意味着调用扫描例程的循环可能会跳过一些输入。仅当输入值之间没有空格时,这通常是一个问题。如果 reader 提供给Fscanimplements ReadRune,该方法将用于读取字符。如果读者也实现了UnreadRune,该方法将用于保存字符并且连续调用不会丢失数据。要将ReadRuneUnreadRune方法附加到没有该功能的阅读器,请使用 bufio.NewReader.

fmt.Fscanf由于最后一个空格之后的下一个字符已经是图像数据的开头,我必须确定读取后消耗了多少空格MaxVal。我的代码必须在调用者提供的任何阅读器上工作,并且它的部分内容不得超过标头的末尾,因此将内容包装到缓冲阅读器中不是一种选择;缓冲阅读器从输入中读取的内容可能比我实际想要阅读的内容更多。

一些测试表明,最后解析一个虚拟字符可以解决问题:

var magic string
var width, height, maxVal uint
var dummy byte

fmt.Fscanf(input,"%2s %d %d %d%c",&magic,&width,&height,&maxVal,&dummy)

可以保证按照规范工作吗?

4

1 回答 1

1

不,我不认为那是安全的。虽然它现在可以工作,但文档指出该函数保留将值读取一个字符的权利,除非您有UnreadRune()方法。

通过将您的阅读器包装在一个 中bufio.Reader,您可以确保阅读器有一个UnreadRune()方法。然后,您需要自己阅读最后的空白。

buf := bufio.NewReader(input)
fmt.Fscanf(buf,"%2s %d %d %d",&magic,&width,&height,&maxVal)
buf.ReadRune() // remove next rune (the whitespace) from the buffer.


编辑:

正如我们在聊天中讨论的那样,您可以假设 dummy char 方法有效,然后编写一个测试,以便您知道它何时停止工作。测试可以是这样的:

func TestFmtBehavior(t *testing.T) {
    // use multireader to prevent r from implementing io.RuneScanner
    r := io.MultiReader(bytes.NewReader([]byte("data  ")))

    n, err := fmt.Fscanf(r, "%s%c", new(string), new(byte))
    if n != 2 || err != nil {
        t.Error("failed scan", n, err)
    }

    // the dummy char read 1 extra char past "data".
    // one byte should still remain
    if n, err := r.Read(make([]byte, 5)); n != 1 {
        t.Error("assertion failed", n, err)
    }
}
于 2013-04-05T19:46:27.080 回答