サイド プロジェクトの 1 つでデータ変換用のユーティリティを構築する作業中に、JSON 形式のファイルを CSV 形式に変換する必要がありました。根本原因を特定するまでにデバッグに 1 時間近くかかる難しい問題に遭遇しました。
このプロセスは、次の 3 つの主要なステップで構成される単純なものであるはずです。
func JsonToCSV(data *SrcSheet) { // Create file name in a format like "email_241030172647.csv" (email_yymmddhhmmss.csv) fName := fileName() // Create file f, err := os.Create(fName) if err != nil { log.Println("Unable to create file", err) return } defer f.Close() // Closing to release resources w := csv.NewWriter(f) // Initializing CSV writer // Add header header := []string{"email", "provider", "added_on"} if err = w.Write(header); err != nil { log.Println("Unable to write header", err) return } count := 0 for domain, elm := range data.Email { if err := w.Write(newRecord(domain, elm)); err != nil { log.Println("Unable to add new record", domain, err) return } else { count++ } } log.Println("Number of records written =", count) } func newRecord(email string, e *SrcElements) []string { if e == nil { return nil } DBFormat := "2006-01-02 15:04:05.000" addedOn := time.Now().UTC().Format(DBFormat) r := []string{email, e.Provider, addedOn} return r }
コードは単純です。特定の名前形式で新しいファイルを作成し、そのファイルを閉じるのを延期し、CSV ライターを初期化し、ファイルへの書き込みを開始します。とてもシンプルですよね?
ステップ 1 と 2 はうまく機能したため、省略しました。ステップ 3 に焦点を移しましょう。ここで、予期せぬことが起こりました。CSV 出力には 65,032 レコードしか含まれておらず、310 レコードが欠落していることを意味します。
トラブルシューティングを行うために、65,032 個の JSON 要素ではなく 7 個だけの JSON 要素を使用してコードを試してみました。驚いたことに、CSV ファイルには何も書き込まれませんでした!
ファイルを閉じていないなどの単純な間違いがないか再確認しましたが、すべて問題ないようでした。次に、より多くの手がかりが得られることを期待して、65,032 個の要素全体を使用して再試行しました。そのとき、310 件のレコードが欠落しているだけでなく、最後のレコードも不完全であることに気づきました。
65030 adam@gmail.com, gmail, 2023-03-17 15:04:05.000 65031 jac@hotmail.com, hotmail, 2023-03-17 15:04:05.000 65032 nancy@xyz.com, hotmail, 2023-03-
これは進歩でした。問題を絞り込んで、w.Write(newRecord(domain, elm))、特に w.Write(...) メソッドに焦点を当てることができました。ドキュメントを確認したところ、次の理由が見つかりました:
...書き込みはバッファリングされるため、レコードが基礎となる io.Writer ...
に確実に書き込まれるように、最終的に [Writer.Flush] を呼び出す必要があります。
w.Flush() を呼び出すのを忘れていました。 CSV ライターは、w.Write() が呼び出されるたびに I/O 操作を実行するのではなく、書き込みをバッファーするため、パフォーマンスの観点からすると、これは理にかなっています。データをバッファリングすることで I/O 負荷が軽減され、最後に w.Flush() を呼び出すことで、バッファーに残っているデータが確実にファイルに書き込まれます。
修正されたコードは次のとおりです:
... f, err := os.Create(fName) if err != nil { log.Println("Unable to create file", err) return } defer f.Close() w := csv.NewWriter(f) defer w.Flush() // Add header header := []string{"email", "provider", "added_on"} ...
確認のために、bufio.go のソース コードを確認したところ、デフォルトのバッファ サイズが 4K であることがわかりました。 WriteRune(...) メソッドでは、バッファーが制限に達するたびに Flush を呼び出していることがわかります。
以上です!楽しんで読んでいただければ幸いです。私は、自分の間違いであれ、他人の間違いであれ、間違いから多くを学ぶ傾向があります。たとえすぐに解決策がなかったとしても、間違ったアプローチを発見することは、将来同じような落とし穴に陥るのを避けるのに役立ちます。だからこそ、この経験を共有したいと思いました!
以上がレコード欠落の謎: Go での JSON から CSV への変換のデバッグの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。