daShangDao_kfzgw-info/csv/csv.go
2025-12-22 19:09:56 +08:00

1883 lines
45 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
/*
#include <stdlib.h>
*/
import "C"
import (
"bufio"
"encoding/csv"
"encoding/json"
"fmt"
"io"
"os"
"path/filepath"
"sync"
"sync/atomic"
"time"
)
// 修改操作类型
type ModifyType int
const (
ModifyRow ModifyType = iota // 修改整行
ModifyCell // 修改单元格
InsertRow // 插入行
DeleteRow // 删除行
)
// ModifyRequest 修改请求
type ModifyRequest struct {
HandleID int64 // 句柄ID
RowNumber int64 // 行号从1开始
ModifyType ModifyType // 修改类型
NewRow []string // 新行数据(整行修改)
NewCellValue string // 新单元格值
ColumnIndex int // 列索引(单元格修改时使用)
ColumnName string // 列名(可选,单元格修改时使用)
}
// ModifyResult 修改结果
type ModifyResult struct {
Success bool // 是否成功
Message string // 消息
RowsAffected int64 // 影响的行数
BackupFile string // 备份文件路径
OperationID string // 操作ID用于追踪
}
// RowPosition 行位置信息
type RowPosition struct {
StartOffset int64 // 行开始偏移
EndOffset int64 // 行结束偏移
RowLength int // 行长度(字节数)
}
// CSVHandle CSV文件句柄
type CSVHandle struct {
ID int64 // 句柄唯一ID
Filename string // 文件名
Delimiter rune // 分隔符
HasHeader bool // 是否有表头
Header []string // 表头(如果有)
File *os.File // 底层文件句柄
CSVReader *csv.Reader // CSV阅读器
TotalRows int64 // 总行数(如果已计算)
IsOpen bool // 是否已打开
OpenTime time.Time // 打开时间
AccessTime time.Time // 最后访问时间
AccessCount int64 // 访问计数
mu sync.RWMutex // 读写锁(保护数据结构)
writeMu sync.Mutex // 写入专用锁(保证写入互斥)
autoCloseTimer *time.Timer // 自动关闭计时器
// 修改相关
rowIndex []RowPosition // 行索引(用于快速定位)
indexBuilt bool // 索引是否已构建
tempDir string // 临时目录
backupFiles []string // 备份文件列表
operationLog []string // 操作日志
}
// CSVManager CSV文件管理器
type CSVManager struct {
handles sync.Map // map[int64]*CSVHandle
nextHandle int64 // 下一个句柄ID
maxOpen int // 最大打开文件数
semaphore chan struct{} // 信号量控制并发打开
config ManagerConfig // 管理器配置
fileLocks *sync.Map // 文件级锁映射
}
// ManagerConfig 管理器配置
type ManagerConfig struct {
MaxOpenFiles int // 最大打开文件数
AutoCloseTimeout time.Duration // 自动关闭超时
BufferSize int // 缓冲区大小
UseMMap bool // 是否使用内存映射(大文件)
MMapThreshold int64 // 使用内存映射的阈值(字节)
CacheRows int // 缓存行数
MaxWriteRetries int // 最大写入重试次数
WriteTimeout time.Duration // 写入超时时间
}
// DefaultConfig 默认配置
var DefaultConfig = ManagerConfig{
MaxOpenFiles: 100,
AutoCloseTimeout: 5 * time.Minute,
BufferSize: 32 * 1024, // 32KB
UseMMap: true,
MMapThreshold: 10 * 1024 * 1024, // 10MB
CacheRows: 1000,
MaxWriteRetries: 3,
WriteTimeout: 30 * time.Second,
}
// 全局管理器实例
var (
globalManager *CSVManager
managerInitOnce sync.Once
)
// GetManager 获取全局管理器
func GetManager() *CSVManager {
managerInitOnce.Do(func() {
globalManager = NewCSVManager(DefaultConfig)
})
return globalManager
}
// NewCSVManager 创建新的CSV管理器
func NewCSVManager(config ManagerConfig) *CSVManager {
if config.MaxOpenFiles <= 0 {
config.MaxOpenFiles = DefaultConfig.MaxOpenFiles
}
return &CSVManager{
handles: sync.Map{},
nextHandle: 1,
maxOpen: config.MaxOpenFiles,
semaphore: make(chan struct{}, config.MaxOpenFiles),
config: config,
fileLocks: &sync.Map{},
}
}
// getFileLock 获取文件级锁
func (mgr *CSVManager) getFileLock(filename string) *sync.RWMutex {
lock, _ := mgr.fileLocks.LoadOrStore(filename, &sync.RWMutex{})
return lock.(*sync.RWMutex)
}
// ========================== 文件操作 ==========================
// OpenCSVFile 打开CSV文件并返回句柄
func (mgr *CSVManager) OpenCSVFile(filename string, delimiter rune, hasHeader bool) (int64, error) {
// 检查文件是否存在
if _, err := os.Stat(filename); os.IsNotExist(err) {
return -1, fmt.Errorf("文件不存在: %s", filename)
}
// 限制并发打开文件数
mgr.semaphore <- struct{}{}
defer func() { <-mgr.semaphore }()
// 生成唯一句柄ID
handleID := atomic.AddInt64(&mgr.nextHandle, 1)
// 创建CSV句柄
csvHandle := &CSVHandle{
ID: handleID,
Filename: filename,
Delimiter: delimiter,
HasHeader: hasHeader,
OpenTime: time.Now(),
AccessTime: time.Now(),
}
// 打开文件
if err := mgr.openFile(csvHandle); err != nil {
return -1, fmt.Errorf("打开文件失败: %w", err)
}
// 获取总行数
rows, err := csvHandle.GetTotalRows()
if err != nil {
return -1, err
}
csvHandle.TotalRows = rows
// 读取表头(如果需要)
if hasHeader {
if err := mgr.readHeader(csvHandle); err != nil {
csvHandle.Close()
return -1, fmt.Errorf("读取表头失败: %w", err)
}
}
// 注册到管理器
mgr.handles.Store(handleID, csvHandle)
// 启动自动关闭计时器
if mgr.config.AutoCloseTimeout > 0 {
csvHandle.startAutoClose(mgr.config.AutoCloseTimeout, mgr)
}
return handleID, nil
}
// openFile 打开文件
func (mgr *CSVManager) openFile(handle *CSVHandle) error {
handle.mu.Lock()
defer handle.mu.Unlock()
if handle.IsOpen {
return nil
}
// 获取文件大小
fileInfo, err := os.Stat(handle.Filename)
if err != nil {
return err
}
fileSize := fileInfo.Size()
// 根据文件大小选择打开策略
if mgr.config.UseMMap && fileSize > mgr.config.MMapThreshold {
return mgr.openFileWithMMap(handle, fileSize)
}
return mgr.openFileNormal(handle)
}
// openFileNormal 正常打开文件
func (mgr *CSVManager) openFileNormal(handle *CSVHandle) error {
file, err := os.Open(handle.Filename)
if err != nil {
return err
}
// 创建带缓冲的CSV阅读器
reader := csv.NewReader(bufio.NewReaderSize(file, mgr.config.BufferSize))
reader.Comma = handle.Delimiter
reader.LazyQuotes = true
reader.TrimLeadingSpace = true
handle.File = file
handle.CSVReader = reader
handle.IsOpen = true
handle.AccessTime = time.Now()
return nil
}
// openFileWithMMap 使用内存映射打开大文件
func (mgr *CSVManager) openFileWithMMap(handle *CSVHandle, fileSize int64) error {
file, err := os.Open(handle.Filename)
if err != nil {
return err
}
// 对于大文件,我们可以先只打开,按需读取
reader := csv.NewReader(file)
reader.Comma = handle.Delimiter
reader.LazyQuotes = true
handle.File = file
handle.CSVReader = reader
handle.IsOpen = true
handle.AccessTime = time.Now()
return nil
}
// readHeader 读取CSV表头
func (mgr *CSVManager) readHeader(handle *CSVHandle) error {
handle.mu.Lock()
defer handle.mu.Unlock()
if !handle.IsOpen {
return fmt.Errorf("文件未打开")
}
// 确保文件指针在开头
if _, err := handle.File.Seek(0, 0); err != nil {
return err
}
// 重置CSV阅读器
handle.CSVReader = csv.NewReader(handle.File)
handle.CSVReader.Comma = handle.Delimiter
// 读取表头
header, err := handle.CSVReader.Read()
if err != nil {
return err
}
handle.Header = header
return nil
}
// GetHandle 获取句柄对象
func (mgr *CSVManager) GetHandle(handleID int64) (*CSVHandle, error) {
value, ok := mgr.handles.Load(handleID)
if !ok {
return nil, fmt.Errorf("句柄不存在: %d", handleID)
}
handle := value.(*CSVHandle)
// 确保文件已打开
handle.mu.RLock()
isOpen := handle.IsOpen
handle.mu.RUnlock()
if !isOpen {
if err := mgr.openFile(handle); err != nil {
return nil, fmt.Errorf("重新打开文件失败: %w", err)
}
}
// 更新访问时间
atomic.AddInt64(&handle.AccessCount, 1)
handle.AccessTime = time.Now()
return handle, nil
}
// ========================== 读取操作 ==========================
// ReadRow 读取一行数据
func (h *CSVHandle) ReadRow() ([]string, error) {
h.mu.RLock()
defer h.mu.RUnlock()
if !h.IsOpen {
return nil, fmt.Errorf("文件未打开")
}
if h.CSVReader == nil {
return nil, fmt.Errorf("CSV阅读器未初始化")
}
return h.CSVReader.Read()
}
// ReadAllRows 读取所有行
func (h *CSVHandle) ReadAllRows() ([][]string, error) {
h.mu.Lock()
defer h.mu.Unlock()
if !h.IsOpen {
return nil, fmt.Errorf("文件未打开")
}
// 重置文件指针到数据开始位置
if _, err := h.File.Seek(0, 0); err != nil {
return nil, err
}
// 重新创建CSV阅读器
h.CSVReader = csv.NewReader(h.File)
h.CSVReader.Comma = h.Delimiter
// 跳过表头
if h.HasHeader {
if _, err := h.CSVReader.Read(); err != nil {
return nil, err
}
}
// 读取所有行
return h.CSVReader.ReadAll()
}
// ReadRows 读取指定数量的行
func (h *CSVHandle) ReadRows(count int) ([][]string, error) {
h.mu.Lock()
defer h.mu.Unlock()
if !h.IsOpen {
return nil, fmt.Errorf("文件未打开")
}
rows := make([][]string, 0, count)
for i := 0; i < count; i++ {
row, err := h.CSVReader.Read()
if err == io.EOF {
break
}
if err != nil {
return rows, err
}
rows = append(rows, row)
}
return rows, nil
}
// GetTotalRows 获取总行数
func (h *CSVHandle) GetTotalRows() (int64, error) {
h.mu.Lock()
defer h.mu.Unlock()
if h.TotalRows > 0 {
return h.TotalRows, nil
}
// 保存当前文件位置
currentPos, err := h.File.Seek(0, io.SeekCurrent)
if err != nil {
return 0, err
}
defer h.File.Seek(currentPos, io.SeekStart)
// 重置到文件开始
if _, err := h.File.Seek(0, 0); err != nil {
return 0, err
}
// 创建新的CSV阅读器进行计数
reader := csv.NewReader(h.File)
reader.Comma = h.Delimiter
var count int64 = 0
// 跳过表头
if h.HasHeader {
if _, err := reader.Read(); err != nil {
return 0, err
}
}
// 计数行数
for {
_, err := reader.Read()
if err == io.EOF {
break
}
if err != nil {
return count, err
}
count++
}
h.TotalRows = count
return count, nil
}
// ========================== 修改操作 ==========================
// ModifyRow 修改指定行
func (mgr *CSVManager) ModifyRow(handleID int64, rowNumber int64, newRow []string) (*ModifyResult, error) {
req := &ModifyRequest{
HandleID: handleID,
RowNumber: rowNumber,
ModifyType: ModifyRow,
NewRow: newRow,
}
return mgr.ModifyCSV(req)
}
// ModifyCell 修改指定单元格
func (mgr *CSVManager) ModifyCell(handleID int64, rowNumber int64, columnIndex int, newValue string) (*ModifyResult, error) {
req := &ModifyRequest{
HandleID: handleID,
RowNumber: rowNumber,
ModifyType: ModifyCell,
ColumnIndex: columnIndex,
NewCellValue: newValue,
}
return mgr.ModifyCSV(req)
}
// ModifyCSV 通用的CSV修改函数
func (mgr *CSVManager) ModifyCSV(req *ModifyRequest) (*ModifyResult, error) {
// 1. 获取句柄
handle, err := mgr.GetHandle(req.HandleID)
if err != nil {
return &ModifyResult{
Success: false,
Message: fmt.Sprintf("获取句柄失败: %v", err),
}, err
}
// 2. 生成操作ID
operationID := fmt.Sprintf("%d_%d_%d", req.HandleID, time.Now().UnixNano(), req.RowNumber)
// 3. 获取文件级写锁
fileLock := mgr.getFileLock(handle.Filename)
fileLock.Lock()
defer fileLock.Unlock()
// 4. 验证基本参数
if err := mgr.validateModifyRequestBeforeLock(handle, req); err != nil {
return &ModifyResult{
Success: false,
Message: fmt.Sprintf("参数验证失败: %v", err),
}, err
}
// 5. 获取句柄写入锁
handle.writeMu.Lock()
defer handle.writeMu.Unlock()
// 6. 执行修改操作
result, err := mgr.executeModifyWithRetry(handle, req, operationID)
if result != nil {
result.OperationID = operationID
}
return result, err
}
// validateModifyRequestBeforeLock 验证修改请求(无需句柄锁)
func (mgr *CSVManager) validateModifyRequestBeforeLock(handle *CSVHandle, req *ModifyRequest) error {
if req.RowNumber <= 0 {
return fmt.Errorf("行号必须大于0")
}
// 验证列数
handle.mu.RLock()
header := handle.Header
handle.mu.RUnlock()
if req.ModifyType == ModifyRow {
expectedCols := len(header)
if len(req.NewRow) != expectedCols {
return fmt.Errorf("新行数据列数不匹配,期望: %d实际: %d", expectedCols, len(req.NewRow))
}
}
if req.ModifyType == ModifyCell {
expectedCols := len(header)
if req.ColumnIndex < 0 || req.ColumnIndex >= expectedCols {
return fmt.Errorf("列索引超出范围,有效范围: 0-%d", expectedCols-1)
}
}
return nil
}
// executeModifyWithRetry 带重试的修改执行
func (mgr *CSVManager) executeModifyWithRetry(handle *CSVHandle, req *ModifyRequest, operationID string) (*ModifyResult, error) {
var lastErr error
var result *ModifyResult
for retry := 0; retry <= mgr.config.MaxWriteRetries; retry++ {
if retry > 0 {
// 重试前等待
time.Sleep(time.Duration(retry*100) * time.Millisecond)
}
result, lastErr = mgr.executeModifyOnce(handle, req, operationID)
if lastErr == nil {
// 记录操作日志
handle.mu.Lock()
handle.operationLog = append(handle.operationLog,
fmt.Sprintf("%s: %s", operationID, result.Message))
handle.mu.Unlock()
return result, nil
}
}
return &ModifyResult{
Success: false,
Message: fmt.Sprintf("修改失败,重试%d次后仍失败: %v", mgr.config.MaxWriteRetries, lastErr),
OperationID: operationID,
}, lastErr
}
// executeModifyOnce 执行单次修改
func (mgr *CSVManager) executeModifyOnce(handle *CSVHandle, req *ModifyRequest, operationID string) (*ModifyResult, error) {
// 1. 创建备份
backupFile, err := mgr.createBackup(handle)
if err != nil {
return &ModifyResult{
Success: false,
Message: fmt.Sprintf("创建备份失败: %v", err),
}, err
}
// 2. 获取总行数(使用更高效的方式)
totalRows, err := mgr.getTotalRowsForModification(handle)
if err != nil {
return &ModifyResult{
Success: false,
Message: fmt.Sprintf("获取总行数失败: %v", err),
}, err
}
//// 2. 获取总行数用于验证
//totalRows, err := mgr.calculateTotalRowsWithLock(handle)
//if err != nil {
// return &ModifyResult{
// Success: false,
// Message: fmt.Sprintf("获取总行数失败: %v", err),
// }, err
//}
// 3. 验证行号范围
if err := mgr.validateRowNumber(handle, req, totalRows); err != nil {
return &ModifyResult{
Success: false,
Message: fmt.Sprintf("行号验证失败: %v", err),
}, err
}
// 4. 执行修改逻辑
startTime := time.Now()
var result *ModifyResult
var modifyErr error
switch req.ModifyType {
case ModifyRow:
result, modifyErr = mgr.modifyRowInternal(handle, req, totalRows)
case ModifyCell:
result, modifyErr = mgr.modifyCellInternal(handle, req, totalRows)
case InsertRow:
result, modifyErr = mgr.insertRowInternal(handle, req, totalRows)
case DeleteRow:
result, modifyErr = mgr.deleteRowInternal(handle, req, totalRows)
default:
modifyErr = fmt.Errorf("不支持的修改类型: %v", req.ModifyType)
result = &ModifyResult{
Success: false,
Message: fmt.Sprintf("不支持的修改类型: %v", req.ModifyType),
}
}
// 记录执行时间
elapsed := time.Since(startTime)
if elapsed > 1*time.Second {
fmt.Printf("警告:修改操作耗时较长: %v\n", elapsed)
}
// 5. 设置备份文件路径
if result != nil {
result.BackupFile = backupFile
result.OperationID = operationID
}
// 6. 记录备份文件
if backupFile != "" {
handle.mu.Lock()
handle.backupFiles = append(handle.backupFiles, backupFile)
handle.mu.Unlock()
}
return result, modifyErr
}
// getTotalRowsForModification 为修改操作获取总行数(优化版本)
func (mgr *CSVManager) getTotalRowsForModification(handle *CSVHandle) (int64, error) {
// 先检查是否已有缓存
if handle.TotalRows > 0 {
return handle.TotalRows, nil
}
// 如果文件不大,直接快速计算
fileInfo, err := os.Stat(handle.Filename)
if err != nil {
return 0, err
}
// 对于小文件,使用快速计算方法
if fileInfo.Size() < 10*1024*1024 { // 10MB以下
return mgr.fastCountRows(handle)
}
// 大文件使用原来的方法
return mgr.calculateTotalRowsWithLock(handle)
}
// fastCountRows 快速计算行数(无需复杂锁)
func (mgr *CSVManager) fastCountRows(handle *CSVHandle) (int64, error) {
file, err := os.Open(handle.Filename)
if err != nil {
return 0, err
}
defer file.Close()
reader := csv.NewReader(file)
reader.Comma = handle.Delimiter
var count int64 = 0
for {
_, err := reader.Read()
if err == io.EOF {
break
}
if err != nil {
return count, err
}
count++
}
// 减去表头行
if handle.HasHeader {
count--
}
// 缓存结果
handle.mu.Lock()
handle.TotalRows = count
handle.mu.Unlock()
return count, nil
}
// calculateTotalRowsWithLock 计算总行数(带锁)
func (mgr *CSVManager) calculateTotalRowsWithLock(handle *CSVHandle) (int64, error) {
handle.mu.Lock()
defer handle.mu.Unlock()
if handle.TotalRows > 0 {
return handle.TotalRows, nil
}
// 保存当前文件位置
currentPos, err := handle.File.Seek(0, io.SeekCurrent)
if err != nil {
return 0, err
}
defer handle.File.Seek(currentPos, io.SeekStart)
// 重置到文件开始
if _, err := handle.File.Seek(0, 0); err != nil {
return 0, err
}
// 创建新的CSV阅读器进行计数
reader := csv.NewReader(handle.File)
reader.Comma = handle.Delimiter
var count int64 = 0
// 跳过表头
if handle.HasHeader {
if _, err := reader.Read(); err != nil {
return 0, err
}
}
// 计数行数
for {
_, err := reader.Read()
if err == io.EOF {
break
}
if err != nil {
return count, err
}
count++
}
handle.TotalRows = count
return count, nil
}
// validateRowNumber 验证行号
func (mgr *CSVManager) validateRowNumber(handle *CSVHandle, req *ModifyRequest, totalRows int64) error {
// 调整行号(考虑表头)
adjustedRowNumber := req.RowNumber
if handle.HasHeader {
adjustedRowNumber = req.RowNumber + 1 // 表头占第1行
}
// 验证行号范围
if req.ModifyType == ModifyRow || req.ModifyType == ModifyCell {
if adjustedRowNumber > totalRows {
return fmt.Errorf("行号超出范围,最大行数为: %d", totalRows-1)
}
} else if req.ModifyType == InsertRow {
// 插入行可以在末尾
if adjustedRowNumber > totalRows+1 {
return fmt.Errorf("插入行号超出范围,最大行数为: %d", totalRows)
}
} else if req.ModifyType == DeleteRow {
if adjustedRowNumber > totalRows {
return fmt.Errorf("删除行号超出范围,最大行数为: %d", totalRows-1)
}
}
return nil
}
// createBackup 创建备份文件
func (mgr *CSVManager) createBackup(handle *CSVHandle) (string, error) {
handle.mu.RLock()
defer handle.mu.RUnlock()
// 创建临时目录
if handle.tempDir == "" {
tempDir, err := os.MkdirTemp("", fmt.Sprintf("csv_backup_%d_", handle.ID))
if err != nil {
return "", fmt.Errorf("创建临时目录失败: %v", err)
}
handle.tempDir = tempDir
}
// 生成备份文件名
backupFile := filepath.Join(handle.tempDir,
fmt.Sprintf("backup_%d_%s.csv", time.Now().UnixNano(),
filepath.Base(handle.Filename)))
// 复制文件
src, err := os.Open(handle.Filename)
if err != nil {
return "", err
}
defer src.Close()
dst, err := os.Create(backupFile)
if err != nil {
return "", err
}
defer dst.Close()
_, err = io.Copy(dst, src)
if err != nil {
os.Remove(backupFile) // 删除失败的备份文件
return "", err
}
return backupFile, nil
}
// modifyRowInternal 修改整行
func (mgr *CSVManager) modifyRowInternal(handle *CSVHandle, req *ModifyRequest, totalRows int64) (*ModifyResult, error) {
// 1. 创建临时文件
tempFile, err := os.CreateTemp(handle.tempDir, "temp_*.csv")
if err != nil {
return nil, fmt.Errorf("创建临时文件失败: %v", err)
}
defer func() {
tempFile.Close()
os.Remove(tempFile.Name())
}()
// 2. 复制并修改文件
modifiedCount, err := mgr.copyAndModifyRow(handle, tempFile, req)
if err != nil {
return nil, fmt.Errorf("复制修改文件失败: %v", err)
}
// 3. 关闭临时文件以确保数据写入磁盘
if err := tempFile.Close(); err != nil {
return nil, fmt.Errorf("关闭临时文件失败: %v", err)
}
// 4. 原子替换原文件
if err := mgr.atomicReplaceFile(tempFile.Name(), handle.Filename); err != nil {
// 尝试恢复备份
mgr.restoreFromBackup(handle)
return nil, fmt.Errorf("替换文件失败: %v", err)
}
// 5. 重新打开文件
handle.mu.Lock()
if handle.File != nil {
handle.File.Close()
handle.File = nil
handle.IsOpen = false
}
if err := mgr.openFile(handle); err != nil {
handle.mu.Unlock()
return nil, fmt.Errorf("重新打开文件失败: %v", err)
}
// 更新总行数
handle.TotalRows = totalRows
handle.mu.Unlock()
return &ModifyResult{
Success: true,
Message: fmt.Sprintf("修改第%d行成功", req.RowNumber),
RowsAffected: modifiedCount,
}, nil
}
// copyAndModifyRow 复制文件并修改指定行
func (mgr *CSVManager) copyAndModifyRow(handle *CSVHandle, dst *os.File, req *ModifyRequest) (int64, error) {
// 打开源文件
src, err := os.Open(handle.Filename)
if err != nil {
return 0, err
}
defer src.Close()
// 创建CSV读写器
csvReader := csv.NewReader(src)
csvReader.Comma = handle.Delimiter
csvReader.LazyQuotes = true
csvWriter := csv.NewWriter(dst)
csvWriter.Comma = handle.Delimiter
var rowCount int64 = 0
var modifiedCount int64 = 0
for {
row, err := csvReader.Read()
if err == io.EOF {
break
}
if err != nil {
csvWriter.Flush()
return modifiedCount, fmt.Errorf("读取第%d行失败: %v", rowCount+1, err)
}
rowCount++
// 检查是否是目标行(考虑表头)
targetRowNumber := req.RowNumber
if handle.HasHeader {
targetRowNumber = req.RowNumber + 1
}
if rowCount == targetRowNumber {
// 修改这一行
row = req.NewRow
modifiedCount++
}
// 写入行
if err := csvWriter.Write(row); err != nil {
csvWriter.Flush()
return modifiedCount, fmt.Errorf("写入第%d行失败: %v", rowCount, err)
}
}
// 确保所有数据写入文件
if err := csvWriter.Error(); err != nil {
return modifiedCount, fmt.Errorf("CSV写入错误: %v", err)
}
csvWriter.Flush()
return modifiedCount, nil
}
// modifyCellInternal 修改单元格
func (mgr *CSVManager) modifyCellInternal(handle *CSVHandle, req *ModifyRequest, totalRows int64) (*ModifyResult, error) {
// 1. 读取目标行
targetRow, err := mgr.readSpecificRow(handle, req.RowNumber)
if err != nil {
return nil, fmt.Errorf("读取目标行失败: %v", err)
}
// 2. 修改单元格
if req.ColumnIndex >= 0 && req.ColumnIndex < len(targetRow) {
targetRow[req.ColumnIndex] = req.NewCellValue
} else {
return nil, fmt.Errorf("列索引超出范围: %d", req.ColumnIndex)
}
// 3. 构建修改请求(转换为整行修改)
rowReq := &ModifyRequest{
HandleID: req.HandleID,
RowNumber: req.RowNumber,
ModifyType: ModifyRow,
NewRow: targetRow,
}
// 4. 调用整行修改
return mgr.modifyRowInternal(handle, rowReq, totalRows)
}
// insertRowInternal 插入行
func (mgr *CSVManager) insertRowInternal(handle *CSVHandle, req *ModifyRequest, totalRows int64) (*ModifyResult, error) {
// 创建临时文件
tempFile, err := os.CreateTemp(handle.tempDir, "insert_*.csv")
if err != nil {
return nil, fmt.Errorf("创建临时文件失败: %v", err)
}
defer func() {
tempFile.Close()
os.Remove(tempFile.Name())
}()
// 打开源文件
src, err := os.Open(handle.Filename)
if err != nil {
return nil, err
}
defer src.Close()
// 创建CSV读写器
csvReader := csv.NewReader(src)
csvReader.Comma = handle.Delimiter
csvWriter := csv.NewWriter(tempFile)
csvWriter.Comma = handle.Delimiter
var rowCount int64 = 0
for {
row, err := csvReader.Read()
if err == io.EOF {
break
}
if err != nil {
csvWriter.Flush()
return nil, fmt.Errorf("读取第%d行失败: %v", rowCount+1, err)
}
rowCount++
// 写入当前行
if err := csvWriter.Write(row); err != nil {
csvWriter.Flush()
return nil, fmt.Errorf("写入第%d行失败: %v", rowCount, err)
}
// 检查是否到达插入位置(考虑表头)
insertPosition := req.RowNumber
if handle.HasHeader {
insertPosition = req.RowNumber + 1
}
if rowCount == insertPosition {
// 插入新行
if err := csvWriter.Write(req.NewRow); err != nil {
csvWriter.Flush()
return nil, fmt.Errorf("插入新行失败: %v", err)
}
}
}
// 如果是最后一行之后插入
adjustedTotalRows := totalRows
if handle.HasHeader {
adjustedTotalRows = totalRows - 1
}
if req.RowNumber == adjustedTotalRows+1 {
// 在文件末尾插入
if err := csvWriter.Write(req.NewRow); err != nil {
csvWriter.Flush()
return nil, fmt.Errorf("在末尾插入新行失败: %v", err)
}
}
// 确保所有数据写入
csvWriter.Flush()
if err := csvWriter.Error(); err != nil {
return nil, fmt.Errorf("CSV写入错误: %v", err)
}
// 关闭临时文件
if err := tempFile.Close(); err != nil {
return nil, fmt.Errorf("关闭临时文件失败: %v", err)
}
// 原子替换原文件
handle.mu.Lock()
if handle.File != nil {
handle.File.Close()
handle.File = nil
handle.IsOpen = false
}
if err := mgr.atomicReplaceFile(tempFile.Name(), handle.Filename); err != nil {
handle.mu.Unlock()
mgr.restoreFromBackup(handle)
return nil, fmt.Errorf("替换文件失败: %v", err)
}
// 重新打开文件
if err := mgr.openFile(handle); err != nil {
handle.mu.Unlock()
return nil, fmt.Errorf("重新打开文件失败: %v", err)
}
// 更新总行数
handle.TotalRows = totalRows + 1
handle.mu.Unlock()
return &ModifyResult{
Success: true,
Message: fmt.Sprintf("在第%d行后插入成功", req.RowNumber),
RowsAffected: 1,
}, nil
}
// deleteRowInternal 删除行
func (mgr *CSVManager) deleteRowInternal(handle *CSVHandle, req *ModifyRequest, totalRows int64) (*ModifyResult, error) {
// 创建临时文件
tempFile, err := os.CreateTemp(handle.tempDir, "delete_*.csv")
if err != nil {
return nil, fmt.Errorf("创建临时文件失败: %v", err)
}
defer func() {
tempFile.Close()
os.Remove(tempFile.Name())
}()
// 打开源文件
src, err := os.Open(handle.Filename)
if err != nil {
return nil, err
}
defer src.Close()
// 创建CSV读写器
csvReader := csv.NewReader(src)
csvReader.Comma = handle.Delimiter
csvWriter := csv.NewWriter(tempFile)
csvWriter.Comma = handle.Delimiter
var rowCount int64 = 0
var deletedCount int64 = 0
for {
row, err := csvReader.Read()
if err == io.EOF {
break
}
if err != nil {
csvWriter.Flush()
return nil, fmt.Errorf("读取第%d行失败: %v", rowCount+1, err)
}
rowCount++
// 检查是否是要删除的行(考虑表头)
deleteRowNumber := req.RowNumber
if handle.HasHeader {
deleteRowNumber = req.RowNumber + 1
}
if rowCount == deleteRowNumber {
// 跳过这一行(不写入)
deletedCount++
continue
}
// 写入行
if err := csvWriter.Write(row); err != nil {
csvWriter.Flush()
return nil, fmt.Errorf("写入第%d行失败: %v", rowCount, err)
}
}
// 确保所有数据写入
csvWriter.Flush()
if err := csvWriter.Error(); err != nil {
return nil, fmt.Errorf("CSV写入错误: %v", err)
}
// 关闭临时文件
if err := tempFile.Close(); err != nil {
return nil, fmt.Errorf("关闭临时文件失败: %v", err)
}
// 原子替换原文件
handle.mu.Lock()
if handle.File != nil {
handle.File.Close()
handle.File = nil
handle.IsOpen = false
}
if err := mgr.atomicReplaceFile(tempFile.Name(), handle.Filename); err != nil {
handle.mu.Unlock()
mgr.restoreFromBackup(handle)
return nil, fmt.Errorf("替换文件失败: %v", err)
}
// 重新打开文件
if err := mgr.openFile(handle); err != nil {
handle.mu.Unlock()
return nil, fmt.Errorf("重新打开文件失败: %v", err)
}
// 更新总行数
handle.TotalRows = totalRows - deletedCount
handle.mu.Unlock()
return &ModifyResult{
Success: true,
Message: fmt.Sprintf("删除第%d行成功", req.RowNumber),
RowsAffected: deletedCount,
}, nil
}
// readSpecificRow 读取指定行
func (mgr *CSVManager) readSpecificRow(handle *CSVHandle, rowNumber int64) ([]string, error) {
// 保存当前位置
currentPos, err := handle.File.Seek(0, io.SeekCurrent)
if err != nil {
return nil, err
}
defer handle.File.Seek(currentPos, io.SeekStart)
// 重置到文件开始
if _, err := handle.File.Seek(0, 0); err != nil {
return nil, err
}
// 创建新的CSV阅读器
reader := csv.NewReader(handle.File)
reader.Comma = handle.Delimiter
var rowCount int64 = 0
targetRowNumber := rowNumber
if handle.HasHeader {
targetRowNumber = rowNumber + 1
}
for {
row, err := reader.Read()
if err == io.EOF {
break
}
if err != nil {
return nil, err
}
rowCount++
if rowCount == targetRowNumber {
return row, nil
}
}
return nil, fmt.Errorf("行号超出范围: %d", rowNumber)
}
// ========================== 工具方法 ==========================
// atomicReplaceFile 原子替换文件
func (mgr *CSVManager) atomicReplaceFile(src, dst string) error {
// 先尝试原子重命名Unix/Linux上这是原子的
if err := os.Rename(src, dst); err == nil {
return nil
}
// 如果重命名失败比如Windows或跨分区使用复制方式
// 创建临时目标文件
tmpDst := dst + ".tmp"
if err := mgr.copyFile(src, tmpDst); err != nil {
return err
}
// 重命名为目标文件
if err := os.Rename(tmpDst, dst); err != nil {
os.Remove(tmpDst)
return err
}
return nil
}
// copyFile 复制文件
func (mgr *CSVManager) copyFile(src, dst string) error {
source, err := os.Open(src)
if err != nil {
return err
}
defer source.Close()
destination, err := os.Create(dst)
if err != nil {
return err
}
defer destination.Close()
_, err = io.Copy(destination, source)
return err
}
// restoreFromBackup 从备份恢复
func (mgr *CSVManager) restoreFromBackup(handle *CSVHandle) error {
handle.mu.Lock()
defer handle.mu.Unlock()
if len(handle.backupFiles) == 0 {
return fmt.Errorf("没有可用的备份文件")
}
// 使用最新的备份
latestBackup := handle.backupFiles[len(handle.backupFiles)-1]
// 关闭当前文件
if handle.File != nil {
handle.File.Close()
handle.File = nil
handle.IsOpen = false
}
// 恢复备份
if err := mgr.atomicReplaceFile(latestBackup, handle.Filename); err != nil {
return fmt.Errorf("恢复备份失败: %v", err)
}
// 重新打开文件
return mgr.openFile(handle)
}
// ========================== 其他操作 ==========================
// GetRow 获取指定行数据
func (mgr *CSVManager) GetRow(handleID int64, rowNumber int64) ([]string, error) {
handle, err := mgr.GetHandle(handleID)
if err != nil {
return nil, err
}
// 获取文件级读锁
fileLock := mgr.getFileLock(handle.Filename)
fileLock.RLock()
defer fileLock.RUnlock()
return mgr.readSpecificRow(handle, rowNumber)
}
// UndoLastModify 撤销最后一次修改
func (mgr *CSVManager) UndoLastModify(handleID int64) (*ModifyResult, error) {
handle, err := mgr.GetHandle(handleID)
if err != nil {
return nil, err
}
// 获取文件级写锁
fileLock := mgr.getFileLock(handle.Filename)
fileLock.Lock()
defer fileLock.Unlock()
handle.mu.Lock()
defer handle.mu.Unlock()
if len(handle.backupFiles) == 0 {
return &ModifyResult{
Success: false,
Message: "没有可撤销的修改",
}, nil
}
// 关闭当前文件
if handle.File != nil {
handle.File.Close()
handle.File = nil
handle.IsOpen = false
}
// 获取最新的备份
latestBackup := handle.backupFiles[len(handle.backupFiles)-1]
// 恢复文件
if err := mgr.atomicReplaceFile(latestBackup, handle.Filename); err != nil {
return &ModifyResult{
Success: false,
Message: fmt.Sprintf("恢复文件失败: %v", err),
}, err
}
// 重新打开文件
if err := mgr.openFile(handle); err != nil {
return &ModifyResult{
Success: false,
Message: fmt.Sprintf("重新打开文件失败: %v", err),
}, err
}
// 移除已使用的备份
handle.backupFiles = handle.backupFiles[:len(handle.backupFiles)-1]
// 重新计算总行数
totalRows, _ := mgr.calculateTotalRowsWithLock(handle)
fmt.Printf("总行数: %d\n", totalRows)
return &ModifyResult{
Success: true,
Message: "撤销成功",
RowsAffected: 1,
BackupFile: latestBackup,
}, nil
}
// CleanupBackups 清理备份文件
func (mgr *CSVManager) CleanupBackups(handleID int64) error {
handle, err := mgr.GetHandle(handleID)
if err != nil {
return err
}
handle.mu.Lock()
defer handle.mu.Unlock()
// 清理所有备份文件
for _, backupFile := range handle.backupFiles {
os.Remove(backupFile)
}
handle.backupFiles = nil
// 清理临时目录
if handle.tempDir != "" {
os.RemoveAll(handle.tempDir)
handle.tempDir = ""
}
return nil
}
// ========================== 句柄管理 ==========================
// Close 关闭CSV句柄
func (h *CSVHandle) Close() error {
h.mu.Lock()
defer h.mu.Unlock()
h.writeMu.Lock()
defer h.writeMu.Unlock()
if !h.IsOpen {
return nil
}
// 停止自动关闭计时器
if h.autoCloseTimer != nil {
h.autoCloseTimer.Stop()
h.autoCloseTimer = nil
}
// 关闭文件
if h.File != nil {
if err := h.File.Close(); err != nil {
return err
}
h.File = nil
}
h.CSVReader = nil
h.IsOpen = false
return nil
}
// startAutoClose 启动自动关闭计时器
func (h *CSVHandle) startAutoClose(timeout time.Duration, mgr *CSVManager) {
h.autoCloseTimer = time.AfterFunc(timeout, func() {
h.mu.Lock()
defer h.mu.Unlock()
// 检查是否长时间未访问
if h.IsOpen && time.Since(h.AccessTime) > timeout {
h.Close()
// 从管理器移除
mgr.handles.Delete(h.ID)
}
})
}
// GetHeader 获取表头
func (h *CSVHandle) GetHeader() []string {
h.mu.RLock()
defer h.mu.RUnlock()
return h.Header
}
// GetInfo 获取句柄信息
func (h *CSVHandle) GetInfo() map[string]interface{} {
h.mu.RLock()
defer h.mu.RUnlock()
return map[string]interface{}{
"id": h.ID,
"filename": h.Filename,
"delimiter": string(h.Delimiter),
"has_header": h.HasHeader,
"is_open": h.IsOpen,
"open_time": h.OpenTime,
"access_time": h.AccessTime,
"access_count": h.AccessCount,
"total_rows": h.TotalRows,
"header": h.Header,
}
}
// CloseHandle 关闭指定句柄
func (mgr *CSVManager) CloseHandle(handleID int64) error {
value, ok := mgr.handles.Load(handleID)
if !ok {
return fmt.Errorf("句柄不存在: %d", handleID)
}
handle := value.(*CSVHandle)
if err := handle.Close(); err != nil {
return err
}
// 清理文件锁
mgr.fileLocks.Delete(handle.Filename)
mgr.handles.Delete(handleID)
return nil
}
// CloseAllHandles 关闭所有句柄
func (mgr *CSVManager) CloseAllHandles() {
mgr.handles.Range(func(key, value interface{}) bool {
handle := value.(*CSVHandle)
handle.Close()
mgr.fileLocks.Delete(handle.Filename)
mgr.handles.Delete(key)
return true
})
}
// ========================== 简便函数 ==========================
//// OpenCSVFile 打开CSV文件
//func OpenCSVFile(filename string, delimiter rune, hasHeader bool) (int64, error) {
// return GetManager().OpenCSVFile(filename, delimiter, hasHeader, "utf-8")
//}
// GetCSVHandle 获取CSV句柄
func GetCSVHandle(handleID int64) (*CSVHandle, error) {
return GetManager().GetHandle(handleID)
}
// CloseCSVHandle 关闭CSV句柄
func CloseCSVHandle(handleID int64) error {
return GetManager().CloseHandle(handleID)
}
// ModifyCSVRow 修改CSV行
func ModifyCSVRow(handleID int64, rowNumber int64, newRow []string) (*ModifyResult, error) {
return GetManager().ModifyRow(handleID, rowNumber, newRow)
}
// ModifyCSVCell 修改CSV单元格
func ModifyCSVCell(handleID int64, rowNumber int64, columnIndex int, newValue string) (*ModifyResult, error) {
return GetManager().ModifyCell(handleID, rowNumber, columnIndex, newValue)
}
// GetCSVRow 获取CSV行
func GetCSVRow(handleID int64, rowNumber int64) ([]string, error) {
return GetManager().GetRow(handleID, rowNumber)
}
// UndoLastCSVModify 撤销最后修改
func UndoLastCSVModify(handleID int64) (*ModifyResult, error) {
return GetManager().UndoLastModify(handleID)
}
// CleanupCSVBackups 清理CSV备份
func CleanupCSVBackups(handleID int64) error {
return GetManager().CleanupBackups(handleID)
}
// UpdateCSVRowSafe 修改csv文件行数据
func (mgr *CSVManager) UpdateCSVRowSafe(handleID int64, rowNum int, newRow []string) error {
getHandle, err := mgr.GetHandle(handleID)
if err != nil {
return fmt.Errorf("获取句柄信息失败: %v", err)
}
// 1. 创建临时文件
tempDir := filepath.Dir(getHandle.Filename)
if tempDir == "" {
tempDir = "."
}
tempFile, err := os.CreateTemp(tempDir, "temp_*.csv")
if err != nil {
return fmt.Errorf("创建临时文件失败: %v", err)
}
tempFileName := tempFile.Name()
// 确保临时文件被关闭和清理
defer func() {
tempFile.Close()
// 如果临时文件还存在(替换失败),则清理它
if _, err := os.Stat(tempFileName); err == nil {
os.Remove(tempFileName)
}
}()
// 2. 读取原始文件
sourceFile, err := os.Open(getHandle.Filename)
if err != nil {
return fmt.Errorf("打开源文件失败: %v", err)
}
defer sourceFile.Close()
// 3. 读取并处理数据
reader := csv.NewReader(sourceFile)
allRows, err := reader.ReadAll()
if err != nil {
return fmt.Errorf("读取CSV失败: %v", err)
}
// 4. 验证并更新
if rowNum < 1 || rowNum > len(allRows) {
return fmt.Errorf("行号 %d 超出范围 (1-%d)", rowNum, len(allRows))
}
// 显示修改前后的对比
oldRow := allRows[rowNum-1]
fmt.Printf("修改前第 %d 行: %v\n", rowNum, oldRow)
fmt.Printf("修改后第 %d 行: %v\n", rowNum, newRow)
allRows[rowNum-1] = newRow
// 5. 写入临时文件
writer := csv.NewWriter(tempFile)
if err := writer.WriteAll(allRows); err != nil {
return fmt.Errorf("写入临时文件失败: %v", err)
}
writer.Flush()
if err := writer.Error(); err != nil {
return fmt.Errorf("刷新写入失败: %v", err)
}
// 6. 确保数据写入磁盘
if err := tempFile.Sync(); err != nil {
return fmt.Errorf("同步文件失败: %v", err)
}
tempFile.Close()
// 7. 使用复制而不是重命名解决Windows文件占用问题
if err := copyFile(tempFileName, getHandle.Filename); err != nil {
return fmt.Errorf("复制文件失败: %v", err)
}
fmt.Printf("✅ 成功更新第 %d 行,文件已直接更新\n", rowNum)
return nil
}
// copyFile 复制文件内容
func copyFile(src, dst string) error {
// 打开源文件
source, err := os.Open(src)
if err != nil {
return err
}
defer source.Close()
// 创建目标文件
destination, err := os.Create(dst)
if err != nil {
return err
}
defer destination.Close()
// 复制内容
_, err = io.Copy(destination, source)
if err != nil {
return err
}
// 确保数据写入磁盘
err = destination.Sync()
if err != nil {
return err
}
return nil
}
// CSV响应结构体
type CSVResponse struct {
Success bool `json:"success"`
Message string `json:"message,omitempty"`
Data interface{} `json:"data,omitempty"`
}
// ===================== C 函数导入 ==================
// OpenCSVFile 打开/创建CSV文件
//
//export OpenCSVFile
func OpenCSVFile(filename *C.char, delimiter C.char, hasHeader C.int) *C.char {
goFilename := C.GoString(filename)
goDelimiter := rune(delimiter)
var hasHeaderBool bool
if hasHeader != 0 {
hasHeaderBool = true
}
hasHeaderBool = false
handle, err := GetManager().OpenCSVFile(goFilename, goDelimiter, hasHeaderBool)
response := struct {
Handle int64 `json:"handle"`
}{
Handle: handle,
}
var csvResponse CSVResponse
if err != nil {
csvResponse = CSVResponse{
Success: false,
Message: err.Error(),
}
} else {
csvResponse = CSVResponse{
Success: true,
Data: response,
}
}
// 转换为JSON字符串
jsonData, err := json.Marshal(csvResponse)
if err != nil {
// 如果JSON序列化失败返回错误信息
csvResponse = CSVResponse{
Success: false,
Message: fmt.Sprintf("JSON序列化失败: %v", err),
}
errorJson, _ := json.Marshal(csvResponse)
return C.CString(string(errorJson))
}
return C.CString(string(jsonData))
}
// UpdateCSVRowSafe 修改csv文件行数据
//
//export UpdateCSVRowSafe
func UpdateCSVRowSafe(handleID C.longlong, rowNum C.int, newRow *C.char) *C.char {
goHandle := int64(handleID)
goRowNum := int(rowNum)
goNewRow := C.GoString(newRow)
var row []string
var csvResponse CSVResponse
// 解析JSON
err := json.Unmarshal([]byte(goNewRow), &row)
if err != nil {
csvResponse = CSVResponse{
Success: false,
Message: fmt.Sprintf("解析JSON失败: %v", err),
}
errorJson, _ := json.Marshal(csvResponse)
return C.CString(string(errorJson))
}
// 修改csv行数据
err = GetManager().UpdateCSVRowSafe(goHandle, goRowNum, row)
if err != nil {
csvResponse = CSVResponse{
Success: false,
Message: fmt.Sprintf(err.Error()),
}
errorJson, _ := json.Marshal(csvResponse)
return C.CString(string(errorJson))
} else {
csvResponse = CSVResponse{
Success: true,
}
}
// 转换为JSON字符串
jsonData, err := json.Marshal(csvResponse)
if err != nil {
// 如果JSON序列化失败返回错误信息
csvResponse = CSVResponse{
Success: false,
Message: fmt.Sprintf("JSON序列化失败: %v", err),
}
errorJson, _ := json.Marshal(csvResponse)
return C.CString(string(errorJson))
}
return C.CString(string(jsonData))
}
// 主函数
func main() {
//fmt.Println("=== CSV句柄管理器测试 ===")
//
//filename := "csv/taskLog.csv"
//fmt.Printf("1. 创建测试文件: %s\n", filename)
//
//// 2. 打开CSV文件
//handleID, err := OpenCSVFile(filename, ',', true)
//if err != nil {
// fmt.Printf("打开文件失败: %v\n", err)
// return
//}
//defer CloseCSVHandle(handleID)
//
//fmt.Printf("2. 打开文件成功句柄ID: %d\n", handleID)
//
////// 3. 获取句柄信息
////handle, err := GetCSVHandle(handleID)
////if err != nil {
//// fmt.Printf("获取句柄失败: %v\n", err)
//// return
////}
////
////info := handle.GetInfo()
////fmt.Printf("3. 文件信息:\n")
////fmt.Printf(" - 文件名: %s\n", info["filename"])
////fmt.Printf(" - 总行数: %v\n", info["total_rows"])
////fmt.Printf(" - 表头: %v\n", info["header"])
//
////// 准备新的行数据
////newData := []string{"9787115524539", "20.00", "20", "上传成功", ""}
////err = UpdateCSVRowSafes(handleID, 6, newData)
////if err != nil {
//// fmt.Printf("错误: %v\n", err)
////} else {
//// fmt.Println("CSV文件更新成功")
////}
//
//// 4. 读取数据
//fmt.Println("4. 读取测试:")
//row3, err := GetCSVRow(handleID, 100000)
//if err != nil {
// fmt.Printf(" 读取第100000行失败: %v\n", err)
//} else {
// fmt.Printf(" 第100000行数据: %v\n", row3)
//}
//
////// 5. 修改测试
////fmt.Println("5. 修改测试:")
////newRow := []string{"9787115524539", "20.00", "1", "上传成功", ""}
////result, err := ModifyCSVRow(handleID, 3, newRow)
////if err != nil {
//// fmt.Printf(" 修改失败: %v\n", err)
////} else {
//// fmt.Printf(" 修改结果: %s (影响行数: %d)\n", result.Message, result.RowsAffected)
////
//// // 验证修改
//// modifiedRow, _ := GetCSVRow(handleID, 3)
//// fmt.Printf(" 修改后的第3行: %v\n", modifiedRow)
////}
//
////// 6. 单元格修改测试
////fmt.Println("6. 单元格修改测试:")
////cellResult, err := ModifyCSVCell(handleID, 5, 1, "35")
////if err != nil {
//// fmt.Printf(" 单元格修改失败: %v\n", err)
////} else {
//// fmt.Printf(" 单元格修改结果: %s\n", cellResult.Message)
////
//// // 验证修改
//// cellModifiedRow, _ := GetCSVRow(handleID, 5)
//// fmt.Printf(" 修改后的第5行: %v\n", cellModifiedRow)
////}
////
////// 7. 并发测试
////fmt.Println("7. 并发测试:")
////TestConcurrentOperations(handleID)
////
////fmt.Println("=== 测试完成 ===")
}
// 下面是一个更简单的版本,根据你的具体需求可以选择使用
func WriteCSVSimple(filename string, data [][]string) error {
if len(data) == 0 {
return fmt.Errorf("写入的数据不能为空")
}
var file *os.File
var err error
var fileMode int
// 检查文件是否存在和有内容
if info, err := os.Stat(filename); err == nil && info.Size() > 0 {
// 文件存在且有内容,追加模式
fileMode = os.O_APPEND | os.O_WRONLY
} else {
// 文件不存在或为空,创建模式(会清空已有内容)
fileMode = os.O_CREATE | os.O_WRONLY | os.O_TRUNC
}
// 打开或创建文件
file, err = os.OpenFile(filename, fileMode, 0755)
if err != nil {
return err
}
defer file.Close()
// 写入CSV数据
writer := csv.NewWriter(file)
if err := writer.WriteAll(data); err != nil {
return err
}
writer.Flush()
return writer.Error()
}