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

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

ただいまの
回答率

89.25%

golangでCSV出力時、要素をダブルクオートでくくることができない

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 4,690

yukihigasi

score 7

前提・実現したいこと

  1. 目的
    CSVファイルを取り込み、変換した後、再びCSVファイルに書き出すツールを作成しております   

  2. 環境

  • windows7
  • go1.6.3 windows/amd64
  1. CSV
  • 変換前
    取り込むCSVは要素がダブルクオートでくくられたカンマ区切りで区切られ、
    改行(CRLF)で区切られています
"test1","test2","test3"
"test1","test2","test3"
"test1","test2","test3"
  • 変換後
    一旦変換内容は問わず、以下のように再びダブルクオートでくくられたCSVファイルを出力しようとしております
"test1","test2","test3"
"test1","test2","test3"
"test1","test2","test3"

発生している問題

  • 事象
    CSVを再出力するとき、要素を区切るダブルクオートをうまく出力することができません
    ダブルクオートを表示しようとした場合、重複して出力されるなどの動きになってしまいます。以下ソースで説明いたします

該当のソースコード

package main

import (
  "encoding/csv"
  "io"
  "flag"
  "log"
  "os"
)

func failOnError(err error) {
    if err != nil {
        log.Fatal("Error:", err)
    }
}

func main() {
    flag.Parse()

    // 入力CSV
    file1, err := os.Open("data/input_T.csv")
      failOnError(err)
      defer file1.Close()

    // 出力CSV
    file2, err := os.Create("data/test_o2.csv")
      failOnError(err)
      defer file2.Close()
  reader := csv.NewReader(file1) //utf8

  writer := csv.NewWriter(file2) //utf8
    writer.UseCRLF = true //デフォルトはLFのみ

    log.Printf("Start")
    for {
        record, err := reader.Read() // 1行読み出す
        if err == io.EOF {
            break
        } else {
            failOnError(err)
        }
        var new_record []string
        for i, v := range record {
            if i >= 0 {
                new_record = append(new_record, v)
            }
        }
        writer.Write(new_record) // 1行書き出す
    }
    writer.Flush()
    log.Printf("Finish !")
}
  • 最初のCSVを登録したとき、
    この場合は
test1,test2,test3
test1,test2,test3
test1,test2,test3


のように、要素をくくるダブルクオートが表示されない状態で出力されます

そこで以下のように、出力するCSVの配列を作成するとき、
要素をエスケープしたダブルクオートで文字列結合した状態で格納します

for i, v := range record {
            if i >= 0 {
                new_record = append(new_record, "\"" + v + "\"")
            }
        }
        writer.Write(new_record) // 1行書き出す


\ はバックスラッシュでダブルクオートをエスケープしています
エスケープを`(バッククオート)でくくった場合も同様の結果となります

"""test1""","""test2""","""test3"""
"""test1""","""test2""","""test3"""
"""test1""","""test2""","""test3"""

なぜダブルクオートが3つ重なるのか

  • ちなみに、以下のように片側だけにダブルクオートをつけた場合
for i, v := range record {
            if i >= 0 {
                new_record = append(new_record, v + "\"")
            }
        }
        writer.Write(new_record) // 1行書き出す

ダブルクオートを付加していない方はひとつ、つけたほうは3つつきます。

"test1""","test2""","test3"""
  • シングルクオートでくくった場合
for i, v := range record {
            if i >= 0 {
                new_record = append(new_record, "'" + v + "'")
            }
        }
        writer.Write(new_record) // 1行書き出す

シングルクオートでくくった場合は、想定に近い動きをします

'test1','test2','test3'


ダブルクオートで要素をくくった場合だけ、うまくいきません。

  • エスケープし、またはダブルクオートの文字列結合の方法に問題があるのでしょうか、
  • または、要素をくくる文字を制御する設定がなにかあるのでしょうか。
  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 1

checkベストアンサー

0

encoding/csvは強制的にダブルクォーテーションをつけることはできません。
ダブルクォーテーションが値に含まれている場合は、""となります。
これはCSVの仕様です。

以下のコードを追記してください。

func WriteWrapper(w *csv.Writer, record []string) error {
    for i, field := range record {
         record[i] = `"` + field + `"`
    }
    return w.Write(record)
}

以下を修正してください

変更前

writer.Write(new_record) // 1行書き出す

変更後

WriteWrapper(writer, new_record) // 1行書き出す

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2016/08/01 09:38

    ご回答いただきましてありがとうございます。

    encoding/csvはダブルクオートつけることはできなかったんですね。
    また、CSVの仕様として正しい動作であるということも、納得いたしました。


    教えて頂きました修正を実施いたしましたが、やはりencoding/csvを利用した場合、
    ダブルクオートをうまくくくることができないため、

    さらにWriteWrapperを追記し、
    encoding/csvをかわした形で、ファイルを出力するようにいたしました。

    ```go
    package main

    import (
    "encoding/csv"
    "io"
    "flag"
    "log"
    "os"
    "io/ioutil"
    "fmt"
    "strings"
    )

    func failOnError(err error) {
    if err != nil {
    log.Fatal("Error:", err)
    }
    }

    func main() {
    flag.Parse()

    // 入力CSV
    file1, err := os.Open("data/input_T.csv")
    failOnError(err)
    defer file1.Close()
    reader := csv.NewReader(file1) //utf8


    log.Printf("Start")
    var record_str []string
    for {
    record, err := reader.Read() // 1行読み出す
    if err == io.EOF {
    break
    } else {
    failOnError(err)
    }
    var new_record []string

    for i, v := range record {
    if i >= 0 {
    new_record = append(new_record, v)
    }
    }
    record_str = append(record_str, WriteWrapper(new_record))
    }

    content := []byte(strings.Join(record_str, "\r\n"))

    // すべての行をつなげた文字列
    ioutil.WriteFile("data/outputCsv.csv", content, os.ModePerm)

    log.Printf("Finish !")
    }

    func WriteWrapper(record []string) string {
    for i, field := range record {
    record[i] = `"` + field + `"`
    }
    // レコード毎の配列を文字列化し改行コードを入れる
    var str_pre_rec string
    str_pre_rec = strings.Join(record, ",")
    fmt.Println(str_pre_rec)

    return str_pre_rec
    }

    ```

    以上にて、最終的に期待していた出力を得ることができました。
    ```
    "test1","test2","test3"
    "test1","test2","test3"
    "test1","test2","test3"
    ```

    ありがとうございました。

    キャンセル

  • 2016/08/01 10:09

    既に解決済ですが念のため、コード追加と修正はセットです。

    キャンセル

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

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