簡體   English   中英

在 Go 中讀取帶有 BOM 的文件

[英]Reading files with a BOM in Go

我需要讀取可能包含或不包含字節順序標記的 Unicode 文件。 我當然可以自己檢查文件的前幾個字節,如果找到 BOM,則丟棄 BOM。 但在我這樣做之前,是否有任何標准方法可以做到這一點,無論是在核心庫中還是在第三方中?

沒有標准的方法,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
    // ...
}

另一種適用於實現io.Seeker接口的對象的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 編碼的。 如果您需要處理其他(或未知)編碼,事情會變得更加復雜。

在 Go 核心包中沒有這樣做的標准方法。 遵循 Unicode 標准。

Unicode 字節順序標記 (BOM) 常見問題

您可以使用utfbom包。 它包裝io.Reader ,根據需要檢測並丟棄 BOM。 它還可以返回 BOM 檢測到的編碼。

我以為我會在這里添加到字符串中剝離字節順序標記序列的方式-而不是直接字節亂搞(如上圖所示)。

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)'

干杯!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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