質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.98%

  • Go

    422questions

    Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

[go] utf8からsjisへの変換で"rune not supported by encoding"エラー

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 376

u28epGUsk

score 15

 問題

GoでSJISでCSVを書き出す際に"rune not supported by encoding"というエラーが発生して、CSVが正常に出力されない。

 環境

go version go1.9.2 windows/amd64

 コード

package main

import (
    "encoding/csv"
    "fmt"
    "log"
    "os"

    "golang.org/x/text/encoding/japanese"
    "golang.org/x/text/transform"
)

const (
    NO_BREAK_SPACE  = "\u00A0"
    WAVE_DASH       = "\u301C"
)

func main() {
    file, err := os.Create("out.csv")
    if err != nil {
        log.Fatal(err)
        panic(err)
    }
    defer file.Close()

    writer := csv.NewWriter(transform.NewWriter(file, japanese.ShiftJIS.NewEncoder()))
    writer.UseCRLF = true

    header := []string{
        "header1",
        "header2",
    }
    body := []string{
        fmt.Sprintf("あ%sい", utils.NO_BREAK_SPACE),
        fmt.Sprintf("十時 %s 十二時", utils.WAVE_DASH),
    }

    err = writer.Write(header)
    if err != nil {
        log.Println(err)
    }

    err = writer.Write(body)
    if err != nil {
        log.Println(err)
    }

    writer.Flush()
    err = writer.Error()
    if err != nil {
        log.Println(err) // encoding: rune not supported by encoding.
    }
}

 出力されるCSV

sjisで以下のように出力されます

header1,header2
あ

 質問

上記の現象はUTF8には存在するがSJISに存在しない文字を変換した際に起こるものだと思っています。

func main() {
    iostr := strings.NewReader(WAVE_DASH)
    rio := transform.NewReader(iostr, japanese.ShiftJIS.NewEncoder())
    ret, err := ioutil.ReadAll(rio)
    log.Println(ret) // []
    log.Println(err) // encoding: rune not supported by encoding.
}

SJISに変換する前にSJISに存在しない文字は?に置換するというのが一般的な方法だと思うのですが、nkfやiconvなどを使用せずにgoの標準ライブラリのみで対応できるのでしょうか?

よろしくお願いいたします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

変換できない文字を ? で描き直す Writer で wrap してあげれば良いかと思います。

package main

import (
    "encoding/csv"
    "fmt"
    "io"
    "log"
    "os"
    "unicode/utf8"

    "golang.org/x/text/encoding/japanese"
    "golang.org/x/text/transform"
)

const (
    NO_BREAK_SPACE = "\u00A0"
    WAVE_DASH      = "\u301C"
)

type runeWriter struct {
    w io.Writer
}

func (rw *runeWriter) Write(b []byte) (int, error) {
    var err error
    l := 0

loop:
    for len(b) > 0 {
        _, n := utf8.DecodeRune(b)
        if n == 0 {
            break loop
        }
        _, err = rw.w.Write(b[:n])
        if err != nil {
            _, err = rw.w.Write([]byte{'?'})
            if err != nil {
                break loop
            }
        }
        l += n
        b = b[n:]
    }
    return l, err
}

func main() {
    file, err := os.Create("out.csv")
    if err != nil {
        log.Fatal(err)
        panic(err)
    }
    defer file.Close()

    writer := csv.NewWriter(
        &runeWriter{transform.NewWriter(file, japanese.ShiftJIS.NewEncoder())})
    writer.UseCRLF = true

    header := []string{
        "header1",
        "header2",
    }
    body := []string{
        fmt.Sprintf("あ%sい", NO_BREAK_SPACE),
        fmt.Sprintf("十時 %s 十二時", WAVE_DASH),
    }

    err = writer.Write(header)
    if err != nil {
        log.Fatal(err)
    }

    err = writer.Write(body)
    if err != nil {
        log.Fatal(err)
    }

    writer.Flush()
    err = writer.Error()
    if err != nil {
        log.Fatal(err) // encoding: rune not supported by encoding.
    }
}

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/12/25 22:33

    mattnさんありがとうございました!

    なるほどGoではこのように対応できるのですね。
    Rubyのようにライブラリや言語がたくさんの機能を持つのではなく、
    自分でカスタマイズして使っていくという点がGoらしさなのかなと印象を受けました。

    ありがとうございます、勉強になりました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.98%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • Go

    422questions

    Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。