在Go中读取带有BOM的文件

Mar*_*ing 8 unicode byte-order-mark go

我需要读取可能包含或不包含字节顺序标记的Unicode文件.我当然可以自己检查文件的前几个字节,如果找到则丢弃BOM.但在此之前,有没有任何标准的方法可以做到这一点,无论是在核心库还是第三方?

kos*_*tix 7

没有标准的方法,IIRC(标准库真的是一个错误的层来实现这种签入)所以这里有两个你可以如何自己处理它的例子。

一种是在数据流上方使用缓冲读取器:

import (
    "bufio"
    "os"
    "log"
)

func main() {
    fd, err := os.Open("filename")
    if err != nil {
        log.Fatal(err)
    }
    defer closeOrDie(fd)
    br := bufio.NewReader(fd)
    r, _, err := br.ReadRune()
    if err != nil {
        log.Fatal(err)
    }
    if r != '\uFEFF' {
        br.UnreadRune() // Not a BOM -- put the rune back
    }
    // Now work with br as you would do with fd
    // ...
}
Run Code Online (Sandbox Code Playgroud)

另一种适用于实现io.Seeker接口的对象的方法是读取前三个字节,如果它们不是 BOM,则io.Seek()返回到开头,例如:

import (
    "os"
    "log"
)

func main() {
    fd, err := os.Open("filename")
    if err != nil {
        log.Fatal(err)
    }
    defer closeOrDie(fd)
    bom := [3]byte
    _, err = io.ReadFull(fd, bom[:])
    if err != nil {
        log.Fatal(err)
    }
    if bom[0] != 0xef || bom[1] != 0xbb || bom[2] != 0xbf {
        _, err = fd.Seek(0, 0) // Not a BOM -- seek back to the beginning
        if err != nil {
            log.Fatal(err)
        }
    }
    // The next read operation on fd will read real data
    // ...
}
Run Code Online (Sandbox Code Playgroud)

这是可能的,因为*os.Fileos.Open()返回的)实例支持 seek 并因此实现io.Seeker。请注意,对于BodyHTTP 响应的读取器而言,情况并非如此,因为您无法“倒带”它。 bufio.Buffer通过执行一些缓冲(显然)来解决不可搜索流的这个特性——这就是你可以UnreadRune()使用它的原因。

请注意,这两个示例都假设我们正在处理的文件是以 UTF-8 编码的。如果您需要处理其他(或未知)编码,事情会变得更加复杂。


Dim*_*sky 6

您可以使用utfbom包。io.Reader它会根据需要包装、检测和丢弃 BOM。它还可以返回 BOM 检测到的编码。