Разархивируйте XML-входные данные ISO-8859-1 в Go

#utf-8 #character-encoding #go

#utf-8 #кодировка символов #Вперед

Вопрос:

Когда ваш XML-ввод не закодирован в UTF-8, Unmarshal функция xml-пакета, похоже, требует CharsetReader .

Где вы нашли такую вещь?

Комментарии:

1. Лучший ответ на эту распространенную проблему меняется при изменении 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(amp;parsed)
  

Ответ №2:

Развивая предложение @anschel-schaffer-cohen и комментарий @mbson, использование пакета go-charset, как упоминалось выше, позволяет использовать эти три строки

 decoder := xml.NewDecoder(reader)
decoder.CharsetReader = charset.NewReader
err = decoder.Decode(amp;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() функцию пакета, которая загружает требуемый материал в память.

Комментарии:

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

2. Согласен. Вероятно, просто «для go еще рано».. Я очень доволен «go get», хотя это делает все это довольно близким к безболезненному

3. @mschuett В 2011 году ответ петерСо был лучшим, и поэтому я принял его. Вам следует взглянуть на все ответы, прежде чем начинать кодировать. В любом случае, я приму это сейчас, чтобы избежать путаницы.

4. @dystroy извините, я опубликовал это до того, как прочитал комментарий вверху, и просто забыл его удалить.

Ответ №3:

Вот пример программы 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 amp;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 amp;amp; 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(amp;result, nil)
  

Комментарии:

1. @dystroy: Чтобы защитить себя и своих пользователей, создайте журнал аудита, включив четкое подтверждение источника (Stack Overflow) и автора (peterSO) кода. Включите полную ссылку на этот вопрос или мой ответ. Включите полную ссылку на мою страницу пользователя Stack Overflow. Я рад, что вы нашли код полезным.

2. Ого. Это все еще лучший ответ? Каждый, кто хочет прочитать ISO-8859, должен перепрыгивать через эти обручи?

3. это должно быть упаковано в библиотеку, чтобы другие люди могли просто загрузить его в анализатор xml.

4. Этот ответ немного устарел. Он использует множество пакетов, которые с тех пор были реструктурированы и не будут работать в go 1.3. У меня есть ответ ниже, который будет работать с более новыми версиями go, а также будет обрабатывать множество различных кодировок, которые не обрабатываются в этом ответе. Хотя стоит понять, как это реализовано.

5. @peterSO Ваш ответ был лучшим, когда я спрашивал в 2011 году, и был полезен мне и другим разработчикам. Но сейчас 2015 год, и разработчики все еще обращаются к этому вопросу в поисках решения. Меня много раз просили принять решение, которое на сегодняшний день является лучшим, и я сделаю это сейчас. Извините, что не принял ваш ответ.

Ответ №4:

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

Комментарии:

1. Я попробую, спасибо (на данный момент я просто создал тривиальный charsetreader, работающий только для сокращенного набора ASCII, и я попробую полный перевод, как только решу другие свои проблемы). Но я удивлен, что go, похоже, считает, что сегодня мир создан из UTF-8.

2. Я думаю, что это скорее вопрос рассмотрения UTF-8 как лучшего внутреннего представления для Unicode и еще не завершенного создания библиотек.

Ответ №5:

Редактировать: не используйте это, используйте ответ с кодировкой go.

Вот обновленная версия кода @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 amp;CharsetISO88591er{r.(io.ByteReader), amp;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(amp;dst)
  

Комментарии:

1. Использование go-charset на самом деле является лучшим ответом, поэтому я бы предпочел, чтобы ответ петерСО оставался неправильным, чтобы указывать людям в этом направлении. (Я обнаружил go-charset только после переноса его кода.)

Ответ №6:

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

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

Комментарии:

1. Я видел пример в тестах, на самом деле это не совсем полезно. На самом деле я не смог понять, как должен работать этот CharsetReader.