我需要读取可能包含字节顺序标记的Unicode文件。当然,我可以自己检查文件的前几个字节,并在发现BOM时丢弃它。但在这样做之前,是否有任何标准方法来处理这个问题,无论是核心库还是第三方库?
我需要读取可能包含字节顺序标记的Unicode文件。当然,我可以自己检查文件的前几个字节,并在发现BOM时丢弃它。但在这样做之前,是否有任何标准方法来处理这个问题,无论是核心库还是第三方库?
如果我没记错的话,没有标准的方法可以实现这个检查(在标准库中实现这样的检查确实是错误的层次),所以这里有两个例子可以让你自己处理。
一个方法是在数据流之上使用缓冲读取器:
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
// ...
}
另一种方法是使用实现了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
// ...
}
这是可能的,因为*os.File
实例(os.Open()
返回的类型)支持寻址并且实现了io.Seeker
接口。需要注意的是,对于HTTP响应中的Body
读取器之类的类型来说,情况并非如此,因为无法“倒带”它们。 bufio.Buffer
通过执行一些缓冲(显然)绕过了这种不能寻址流的特性,这就允许你在其上使用UnreadRune()
。
请注意,这两个示例都假定我们处理的文件使用UTF-8编码。如果需要处理其他(或未知)编码方式,则情况会更加复杂。
我想在这里补充一下从字符串中删除字节顺序标记序列的方法-而不是直接处理字节(如上所示)。
package main
import (
"fmt"
"strings"
)
func main() {
s := "\uFEFF is a string that starts with a Byte Order Mark"
fmt.Printf("before: '%v' (len=%v)\n", s, len(s))
ByteOrderMarkAsString := string('\uFEFF')
if strings.HasPrefix(s, ByteOrderMarkAsString) {
fmt.Printf("Found leading Byte Order Mark sequence!\n")
s = strings.TrimPrefix(s, ByteOrderMarkAsString)
}
fmt.Printf("after: '%v' (len=%v)\n", s, len(s))
}
before: ' is a string that starts with a Byte Order Mark (len=50)'
Found leading Byte Order Mark sequence!
after: ' is a string that starts with a Byte Order Mark (len=47)'
干杯!
在Go核心包中,没有标准的方法来进行这项任务。请遵循Unicode标准。
我们使用transform包来读取CSV文件(这些文件可能已经保存为UTF8、UTF8-with-BOM或UTF16格式),具体操作如下:
import (
"encoding/csv"
"golang.org/x/text/encoding"
"golang.org/x/text/encoding/unicode"
"golang.org/x/text/transform"
"io"
}
// BOMAwareCSVReader will detect a UTF BOM (Byte Order Mark) at the
// start of the data and transform to UTF8 accordingly.
// If there is no BOM, it will read the data without any transformation.
func BOMAwareCSVReader(reader io.Reader) *csv.Reader {
var transformer = unicode.BOMOverride(encoding.Nop.NewDecoder())
return csv.NewReader(transform.NewReader(reader, transformer))
}
我们正在使用 Go 1.18。