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

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

新規登録して質問してみよう
ただいま回答率
85.50%
Go

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

Q&A

解決済

1回答

11889閲覧

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

yukihigasi

総合スコア7

Go

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

0グッド

0クリップ

投稿2016/07/29 00:15

###前提・実現したいこと

  1. 目的

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

  1. 環境
  • 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を再出力するとき、要素を区切るダブルクオートをうまく出力することができません
ダブルクオートを表示しようとした場合、重複して出力されるなどの動きになってしまいます。以下ソースで説明いたします

###該当のソースコード

go

1package main 2 3import ( 4 "encoding/csv" 5 "io" 6 "flag" 7 "log" 8 "os" 9) 10 11func failOnError(err error) { 12 if err != nil { 13 log.Fatal("Error:", err) 14 } 15} 16 17func main() { 18 flag.Parse() 19 20 // 入力CSV 21 file1, err := os.Open("data/input_T.csv") 22 failOnError(err) 23 defer file1.Close() 24 25 // 出力CSV 26 file2, err := os.Create("data/test_o2.csv") 27 failOnError(err) 28 defer file2.Close() 29 reader := csv.NewReader(file1) //utf8 30 31 writer := csv.NewWriter(file2) //utf8 32 writer.UseCRLF = true //デフォルトはLFのみ 33 34 log.Printf("Start") 35 for { 36 record, err := reader.Read() // 1行読み出す 37 if err == io.EOF { 38 break 39 } else { 40 failOnError(err) 41 } 42 var new_record []string 43 for i, v := range record { 44 if i >= 0 { 45 new_record = append(new_record, v) 46 } 47 } 48 writer.Write(new_record) // 1行書き出す 49 } 50 writer.Flush() 51 log.Printf("Finish !") 52} 53
  • 最初のCSVを登録したとき、

この場合は

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

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

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

go

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

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

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

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

  • ちなみに、以下のように片側だけにダブルクオートをつけた場合

go

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

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

"test1""","test2""","test3"""
  • シングルクオートでくくった場合

go

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

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

'test1','test2','test3'

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

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

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

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/07/29 00:32

編集2016/07/29 00:33
moonphase

総合スコア6621

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yukihigasi

2016/08/01 00: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" ``` ありがとうございました。
moonphase

2016/08/01 01:09

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問