Распакуйте ввод XML ISO-8859-1 в Go - PullRequest
24 голосов
/ 14 мая 2011

Если ваш ввод XML не закодирован в UTF-8, для функции Unmarshal пакета xml требуется CharsetReader.

Где вы найдете такую ​​вещь?

Ответы [ 7 ]

38 голосов
/ 26 августа 2015

Обновленный ответ за 2015 год и далее:

import (
    "encoding/xml"
    "golang.org/x/net/html/charset"
)

decoder := xml.NewDecoder(reader)
decoder.CharsetReader = charset.NewReaderLabel
err = decoder.Decode(&parsed)
23 голосов
/ 30 сентября 2013

Расширение на предложение @ 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"

в какой-то момент, когда приложение запускается.

EDIT

Вместо вышеупомянутых charset.CharsetDir = и т. Д. Более разумно просто импортировать файлы данных. они рассматриваются как встроенный ресурс:

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

go install просто сделает свое дело, это также позволит избежать головной боли при развертывании (где / как я могу получить файлы данных относительно исполняемого приложения?).

при использовании импорта с подчеркиванием просто вызывает функцию init() пакета, которая загружает необходимые данные в память.

11 голосов
/ 16 мая 2011

Вот пример программы Go, которая использует функцию CharsetReader для преобразования XML-ввода из ISO-8859-1 в UTF-8.Программа распечатывает тестовый файл XML-комментариев.

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

Чтобы распаковать XML с encoding="ISO-8859-1" из io.Reader r в структуру result, при этом используя функцию CharsetReader из программыперевести с ISO-8859-1 на UTF-8, напишите:

p := xml.NewParser(r)
p.CharsetReader = CharsetReader
err := p.Unmarshal(&result, nil)
7 голосов
/ 14 мая 2011

Кажется, есть внешняя библиотека, которая обрабатывает это: go-charset. Я сам не пробовал; у тебя это работает?

6 голосов
/ 19 марта 2013

Редактировать: не используйте это, используйте ответ 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)
0 голосов
/ 23 апреля 2019

Я написал статью о декодировании закодированного в iso-8859-1 XML здесь.Надеюсь, это поможет всем, кто ищет сейчас

https://medium.com/@github.gkarthiks/decoding-iso-8859-1-in-go-68979f7d65c6

0 голосов
/ 14 мая 2011

На данный момент в дистрибутиве go нет нигде, ни где-либо еще, что я могу найти.Не удивительно, так как этот хук меньше, чем месяц на момент написания.

Поскольку CharsetReader определен как CharsetReader func(charset string, input io.Reader) (io.Reader, os.Error), вы можете создать свой собственный.В тестах есть один пример, но он может быть вам не совсем полезен.

...