【问题标题】:Unmarshal an ISO-8859-1 XML input in Go在 Go 中解组 ISO-8859-1 XML 输入
【发布时间】:2011-08-25 13:09:25
【问题描述】:

当您的 XML 输入未以 UTF-8 编码时,xml 包的 Unmarshal 函数似乎需要 CharsetReader

你在哪里找到这样的东西?

【问题讨论】:

  • 这个常见问题的最佳答案是随着go的变化而变化。为了避免人们使用过时的解决方案,我已经给另一个答案加了两倍的“接受”标记。

标签: utf-8 character-encoding go


【解决方案1】:

2015 年及以后的更新答案:

import (
    "encoding/xml"
    "golang.org/x/net/html/charset"
)
reader := bytes.NewReader(theXml)
decoder := xml.NewDecoder(reader)
decoder.CharsetReader = charset.NewReaderLabel
err = decoder.Decode(&parsed)

【讨论】:

    【解决方案2】:

    扩展@anschel-schaffer-cohen 的建议和@mjibson 的评论, 使用上面提到的go-charset 包允许您使用这三行

    decoder := xml.NewDecoder(reader)
    decoder.CharsetReader = charset.NewReader
    err = decoder.Decode(&parsed)
    

    达到要求的结果。只要记住通过调用让charset 知道它的数据文件在哪里

    charset.CharsetDir = ".../src/code.google.com/p/go-charset/datafiles"
    

    在应用启动时的某个时刻。

    编辑

    而不是上面的,charset.CharsetDir = 等。只导入数据文件更明智。它们被视为嵌入式资源:

    import (
        "code.google.com/p/go-charset/charset"
        _ "code.google.com/p/go-charset/data"
        ...
    )
    

    go install 只会做它的事,这也避免了部署的麻烦(我在哪里/如何获取与正在执行的应用程序相关的数据文件?)。

    使用带下划线的 import 只是调用包的 init() 函数,它将所需的东西加载到内存中。

    【讨论】:

    • 有一个赞成票,但是对于这样一个简单的操作而不是这种转换,必须有外部资源是令人失望的。
    • 同意。可能只是“还处于起步阶段”.. 我对“去获取”很满意,因为它使整个事情变得非常接近无痛
    • @mschuett 在 2011 年,peterSo 的答案是最好的,因此我接受了它。在开始编码之前,您应该查看所有答案。无论如何,我现在就接受这个以避免混淆。
    • @dystroy 抱歉,我在阅读顶部评论之前发布了这个,只是忘记删除它。
    【解决方案3】:

    这是一个示例 Go 程序,它使用 CharsetReader 函数将 XML 输入从 ISO-8859-1 转换为 UTF-8。程序打印测试文件 XML cmets。

    package main
    
    import (
        "bytes"
        "fmt"
        "io"
        "os"
        "strings"
        "utf8"
        "xml"
    )
    
    type CharsetISO88591er struct {
        r   io.ByteReader
        buf *bytes.Buffer
    }
    
    func NewCharsetISO88591(r io.Reader) *CharsetISO88591er {
        buf := bytes.NewBuffer(make([]byte, 0, utf8.UTFMax))
        return &CharsetISO88591er{r.(io.ByteReader), buf}
    }
    
    func (cs *CharsetISO88591er) ReadByte() (b byte, err os.Error) {
        // http://unicode.org/Public/MAPPINGS/ISO8859/8859-1.TXT
        // Date: 1999 July 27; Last modified: 27-Feb-2001 05:08
        if cs.buf.Len() <= 0 {
            r, err := cs.r.ReadByte()
            if err != nil {
                return 0, err
            }
            if r < utf8.RuneSelf {
                return r, nil
            }
            cs.buf.WriteRune(int(r))
        }
        return cs.buf.ReadByte()
    }
    
    func (cs *CharsetISO88591er) Read(p []byte) (int, os.Error) {
        // Use ReadByte method.
        return 0, os.EINVAL
    }
    
    func isCharset(charset string, names []string) bool {
        charset = strings.ToLower(charset)
        for _, n := range names {
            if charset == strings.ToLower(n) {
                return true
            }
        }
        return false
    }
    
    func IsCharsetISO88591(charset string) bool {
        // http://www.iana.org/assignments/character-sets
        // (last updated 2010-11-04)
        names := []string{
            // Name
            "ISO_8859-1:1987",
            // Alias (preferred MIME name)
            "ISO-8859-1",
            // Aliases
            "iso-ir-100",
            "ISO_8859-1",
            "latin1",
            "l1",
            "IBM819",
            "CP819",
            "csISOLatin1",
        }
        return isCharset(charset, names)
    }
    
    func IsCharsetUTF8(charset string) bool {
        names := []string{
            "UTF-8",
            // Default
            "",
        }
        return isCharset(charset, names)
    }
    
    func CharsetReader(charset string, input io.Reader) (io.Reader, os.Error) {
        switch {
        case IsCharsetUTF8(charset):
            return input, nil
        case IsCharsetISO88591(charset):
            return NewCharsetISO88591(input), nil
        }
        return nil, os.NewError("CharsetReader: unexpected charset: " + charset)
    }
    
    func main() {
        // Print the XML comments from the test file, which should
        // contain most of the printable ISO-8859-1 characters.
        r, err := os.Open("ISO88591.xml")
        if err != nil {
            fmt.Println(err)
            return
        }
        defer r.Close()
        fmt.Println("file:", r.Name())
        p := xml.NewParser(r)
        p.CharsetReader = CharsetReader
        for t, err := p.Token(); t != nil && err == nil; t, err = p.Token() {
            switch t := t.(type) {
            case xml.ProcInst:
                fmt.Println(t.Target, string(t.Inst))
            case xml.Comment:
                fmt.Println(string([]byte(t)))
            }
        }
    }
    

    要将带有encoding="ISO-8859-1" 的XML 从io.Reader r 解组为结构result,同时使用程序中的CharsetReader 函数将ISO-8859-1 转换为UTF-8,请编写:

    p := xml.NewParser(r)
    p.CharsetReader = CharsetReader
    err := p.Unmarshal(&result, nil)
    

    【讨论】:

    • @dystroy:为了保护您自己和您的用户,请通过包含对源代码 (Stack Overflow) 和作者 (peterSO) 的明确确认来建立审计跟踪。包括此问题或我的答案的完整链接。包含指向我的 Stack Overflow 用户页面的完整链接。很高兴您发现代码很有用。
    • 哎呀。这仍然是最好的答案吗?每个想要阅读 ISO-8859 的人都必须跳过这些障碍?
    • 这应该被打包到一个库中,这样其他人就可以把它放到 xml 解析器中。
    • 这个答案有点过时了。它使用了许多已经重组的包,并且在 go 1.3 中无法使用。我在下面有一个答案,它将适用于较新版本的 go,并且还将处理许多不同的字符集,这些字符集没有被这个答案处理。虽然值得了解这是如何实现的。
    • @peterSO 当我在 2011 年询问时,您的回答是最好的,对我和其他开发人员都有帮助。但我们现在是 2015 年,开发人员仍在解决这个问题,寻找解决方案。我被多次要求接受今天最好的解决方案,我现在就去做。很抱歉不得不不接受您的回答。
    【解决方案4】:

    似乎有一个外部库可以处理此问题:go-charset。我自己没试过;对你有用吗?

    【讨论】:

    • 我会尝试的,谢谢(现在我只是制作了一个仅适用于 ASCII 缩减集的简单字符集阅读器,我会在解决其他问题后立即尝试完整的翻译)。但令我惊讶的是,Go 似乎认为当今世界是由 UTF-8 构成的。
    • 我认为更多的是认为 UTF-8 是 Unicode 的最佳 internal 表示,并且还没有完成库。
    【解决方案5】:

    编辑:不要使用这个,使用 go-charset 答案。

    这是@peterSO 代码的更新版本,适用于 go1:

    package main
    
    import (
        "bytes"
        "io"
        "strings"
    )
    
    type CharsetISO88591er struct {
        r   io.ByteReader
        buf *bytes.Buffer
    }
    
    func NewCharsetISO88591(r io.Reader) *CharsetISO88591er {
        buf := bytes.Buffer{}
        return &CharsetISO88591er{r.(io.ByteReader), &buf}
    }
    
    func (cs *CharsetISO88591er) Read(p []byte) (n int, err error) {
        for _ = range p {
            if r, err := cs.r.ReadByte(); err != nil {
                break
            } else {
                cs.buf.WriteRune(rune(r))
            }
        }
        return cs.buf.Read(p)
    }
    
    func isCharset(charset string, names []string) bool {
        charset = strings.ToLower(charset)
        for _, n := range names {
            if charset == strings.ToLower(n) {
                return true
            }
        }
        return false
    }
    
    func IsCharsetISO88591(charset string) bool {
        // http://www.iana.org/assignments/character-sets
        // (last updated 2010-11-04)
        names := []string{
            // Name
            "ISO_8859-1:1987",
            // Alias (preferred MIME name)
            "ISO-8859-1",
            // Aliases
            "iso-ir-100",
            "ISO_8859-1",
            "latin1",
            "l1",
            "IBM819",
            "CP819",
            "csISOLatin1",
        }
        return isCharset(charset, names)
    }
    
    func CharsetReader(charset string, input io.Reader) (io.Reader, error) {
        if IsCharsetISO88591(charset) {
            return NewCharsetISO88591(input), nil
        }
        return input, nil
    }
    

    调用:

    d := xml.NewDecoder(reader)
    d.CharsetReader = CharsetReader
    err := d.Decode(&dst)
    

    【讨论】:

    • 使用 go-charset 实际上是最好的答案,所以我宁愿 peterSO 的答案仍然错误,将人们指向那个方向。 (我是在移植他的代码后才发现 go-charset 的。)
    【解决方案6】:

    目前 go 发行版或我能找到的其他任何地方都没有提供任何内容。这并不奇怪,因为在撰写本文时该钩子是 less than a month old

    由于 CharsetReader 被定义为CharsetReader func(charset string, input io.Reader) (io.Reader, os.Error),您可以自己制作。 tests 中有一个示例,但这对您可能并不完全有用。

    【讨论】:

    • 我在测试中看到了这个例子,它确实不是很有用。事实上,我不明白这个 CharsetReader 应该如何工作。
    猜你喜欢
    • 2017-09-30
    • 1970-01-01
    • 1970-01-01
    • 2014-11-19
    • 2010-11-18
    • 2014-11-18
    • 2014-01-17
    • 2015-10-11
    • 1970-01-01
    相关资源
    最近更新 更多