123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210 |
- package csvwriter
- import (
- "encoding/csv"
- "fmt"
- "os"
- "path/filepath"
- "strconv"
- )
- // WriteRecordsToCSV 将数据写入 CSV 文件
- // 参数:
- // - filePath: CSV 文件路径
- // - headers: 表头字段列表
- // - records: 要写入的记录列表,每个记录是一个字符串切片
- // - delimiter: CSV 文件的分隔符
- // - appendToFile: 是否追加写入,true 表示在文件末尾追加,false 表示新建文件
- // 返回值:
- // - error: 如果发生错误,返回错误信息
- func WriteRecordsToCSV(filePath string, headers []string, records [][]string, delimiter rune, appendToFile bool) error {
- var file *os.File
- var err error
- if appendToFile {
- // 追加写入
- file, err = os.OpenFile(filePath, os.O_APPEND|os.O_WRONLY, 0644)
- if err != nil {
- return fmt.Errorf("failed to open file for appending: %v", err)
- }
- } else {
- // 新建文件
- file, err = os.Create(filePath)
- if err != nil {
- return fmt.Errorf("failed to create file: %v", err)
- }
- }
- defer file.Close()
- writer := csv.NewWriter(file)
- writer.Comma = delimiter
- defer writer.Flush()
- // 如果是新文件且有表头,需要写入表头
- if !appendToFile && headers != nil {
- if err := writer.Write(headers); err != nil {
- return fmt.Errorf("failed to write headers to CSV: %v", err)
- }
- }
- // 写入记录
- if err := writer.WriteAll(records); err != nil {
- return fmt.Errorf("failed to write records to CSV: %v", err)
- }
- return nil
- }
- /*
- WriteRecordsToCSVWithFileSizeLimit 将数据写入 CSV 文件,并基于文件大小进行分片
- 参数:
- - baseFilePath: CSV 文件的基本路径,用于生成不同分片的文件名
- - headers: 表的列名,用于 CSV 表头
- - records: 要写入的表记录
- - delimiter: CSV 的分隔符
- - maxFileSizeMB: 单个 CSV 文件的最大大小 MB
- - fileIndex: 当前文件的索引号,用于管理分片文件的命名
- - currentFileSize: 当前文件的大小,用于判断是否需要创建新文件
- - currentFilePath: 当前正在写入的文件路径,用于管理文件的创建和打开
- 返回值:
- - error: 如果写入过程中有错误,返回相关的错误信息
- */
- func WriteRecordsToCSVWithFileSizeLimit(baseFilePath string, headers []string, records [][]string, delimiter rune, maxFileSizeMB int64, fileIndex *int, currentFileSize *int64, currentFilePath *string) error {
- var file *os.File
- var writer *csv.Writer
- // 将 maxFileSize 从 MB 转换为字节
- maxFileSize := maxFileSizeMB * 1024 * 1024
- // 检查是否需要创建新文件
- if *currentFileSize == 0 {
- // 如果是第一次写入,创建新文件
- *currentFilePath = filepath.Join(baseFilePath + "_" + strconv.Itoa(*fileIndex) + ".csv")
- var err error
- file, err = os.Create(*currentFilePath)
- if err != nil {
- return fmt.Errorf("failed to create file: %v", err)
- }
- defer file.Close()
- writer = csv.NewWriter(file)
- writer.Comma = delimiter // 设置 CSV 分隔符
- defer writer.Flush()
- // 写入表头到 CSV 文件
- if err := writer.Write(headers); err != nil {
- return fmt.Errorf("failed to write header to CSV: %v", err)
- }
- *currentFileSize += estimateRecordSize(headers, delimiter)
- } else {
- // 如果文件已存在,追加写入
- var err error
- file, err = os.OpenFile(*currentFilePath, os.O_APPEND|os.O_WRONLY, os.ModeAppend)
- if err != nil {
- return fmt.Errorf("failed to open file: %v", err)
- }
- defer file.Close()
- writer = csv.NewWriter(file)
- writer.Comma = delimiter // 设置 CSV 分隔符
- defer writer.Flush()
- }
- // 遍历记录逐行写入 CSV 文件
- for _, record := range records {
- // 如果文件大小超过限制,创建新文件
- if *currentFileSize > maxFileSize {
- writer.Flush()
- file.Close()
- // 增加文件索引,创建新的文件
- *fileIndex++
- *currentFilePath = filepath.Join(baseFilePath + "_" + strconv.Itoa(*fileIndex) + ".csv")
- var err error
- file, err = os.Create(*currentFilePath)
- if err != nil {
- return fmt.Errorf("failed to create file: %v", err)
- }
- writer = csv.NewWriter(file)
- writer.Comma = delimiter // 设置 CSV 分隔符
- defer writer.Flush()
- // 写入表头到新文件
- if err := writer.Write(headers); err != nil {
- return fmt.Errorf("failed to write header to new CSV: %v", err)
- }
- *currentFileSize = estimateRecordSize(headers, delimiter)
- }
- // 写入当前记录
- if err := writer.Write(record); err != nil {
- return fmt.Errorf("failed to write row to CSV: %v", err)
- }
- // 更新当前文件大小
- *currentFileSize += estimateRecordSize(record, delimiter)
- }
- return nil
- }
- // WriteRecordsAppendToCSVFile 将数据写入 CSV 文件
- func WriteRecordsAppendToCSVFile(filePath string, headers []string, records [][]string, delimiter rune, appendToFile bool) error {
- var file *os.File
- var err error
- if appendToFile {
- // 追加写入
- file, err = os.OpenFile(filePath, os.O_APPEND|os.O_WRONLY, os.ModeAppend)
- if err != nil {
- return fmt.Errorf("failed to open file for appending: %v", err)
- }
- } else {
- // 新建文件
- file, err = os.Create(filePath)
- if err != nil {
- return fmt.Errorf("failed to create file: %v", err)
- }
- // 写入表头
- writer := csv.NewWriter(file)
- writer.Comma = delimiter
- defer writer.Flush()
- if err := writer.Write(headers); err != nil {
- return fmt.Errorf("failed to write header to CSV: %v", err)
- }
- }
- defer file.Close()
- writer := csv.NewWriter(file)
- writer.Comma = delimiter
- defer writer.Flush()
- // 写入记录
- if err := writer.WriteAll(records); err != nil {
- return fmt.Errorf("failed to write records to CSV: %v", err)
- }
- return nil
- }
- // EstimateRecordSize 估算一条记录在 CSV 中的大小
- // 参数:
- // - record: 要估算的 CSV 记录
- // - delimiter: CSV 的分隔符
- // 返回值:
- // - 记录的估算大小,以字节为单位
- func estimateRecordSize(record []string, delimiter rune) int64 {
- size := int64(0)
- for i, field := range record {
- size += int64(len(field))
- // 每个字段之间的分隔符也会占用空间
- if i < len(record)-1 {
- size++
- }
- }
- // 加上换行符
- size++
- return size
- }
|