2090 lines
53 KiB
Go
2090 lines
53 KiB
Go
package main
|
||
|
||
/*
|
||
#include <stdlib.h>
|
||
*/
|
||
import "C"
|
||
import (
|
||
"bufio"
|
||
"encoding/csv"
|
||
"encoding/json"
|
||
"fmt"
|
||
"io"
|
||
"os"
|
||
"path/filepath"
|
||
"sync"
|
||
"sync/atomic"
|
||
"time"
|
||
"unsafe"
|
||
)
|
||
|
||
// ========================== 数据结构定义 ==========================
|
||
|
||
// CSVHandle CSV文件句柄
|
||
type CSVHandle struct {
|
||
ID int64 // 句柄唯一ID
|
||
Filename string // 文件名
|
||
Delimiter rune // 分隔符
|
||
HasHeader bool // 是否有表头
|
||
Header []string // 表头(如果有)
|
||
File *os.File // 底层文件句柄
|
||
CSVReader *csv.Reader // CSV阅读器
|
||
CSVWriter *csv.Writer // CSV写入器
|
||
TotalRows int64 // 总行数(如果已计算)
|
||
IsOpen bool // 是否已打开
|
||
OpenTime time.Time // 打开时间
|
||
AccessTime time.Time // 最后访问时间
|
||
AccessCount int64 // 访问计数
|
||
mu sync.RWMutex // 读写锁(保护数据结构)
|
||
autoCloseTimer *time.Timer // 自动关闭计时器
|
||
cachedRowCount int64 // 缓存的行数(避免重复计算)
|
||
rowCountCached bool // 行数是否已缓存
|
||
writeBuffer *bufio.Writer // 写入缓冲区
|
||
|
||
// 新增:引用计数和状态管理
|
||
refCount int64 // 引用计数
|
||
refCountMu sync.RWMutex // 引用计数锁
|
||
closing bool // 正在关闭中
|
||
closed bool // 已关闭
|
||
statusMu sync.RWMutex // 状态锁
|
||
activeOps int64 // 正在进行的操作数
|
||
}
|
||
|
||
// CSVManager CSV文件管理器
|
||
type CSVManager struct {
|
||
handles sync.Map // map[int64]*CSVHandle
|
||
fileLocks sync.Map // map[string]*sync.RWMutex 文件级锁
|
||
nextHandle int64 // 下一个句柄ID
|
||
maxOpen int // 最大打开文件数
|
||
semaphore chan struct{} // 信号量控制并发打开
|
||
config ManagerConfig // 管理器配置
|
||
}
|
||
|
||
// ManagerConfig 管理器配置
|
||
type ManagerConfig struct {
|
||
MaxOpenFiles int // 最大打开文件数
|
||
AutoCloseTimeout time.Duration // 自动关闭超时
|
||
BufferSize int // 缓冲区大小
|
||
UseMMap bool // 是否使用内存映射(大文件)
|
||
MMapThreshold int64 // 使用内存映射的阈值(字节)
|
||
}
|
||
|
||
// MergeOptions 合并选项
|
||
type MergeOptions struct {
|
||
AppendMode bool // true=追加模式,false=覆盖模式
|
||
SkipHeader bool // 是否跳过源文件的表头
|
||
SkipDuplicates bool // 是否跳过重复行
|
||
ColumnMapping map[int]int // 列映射:源文件列索引 -> 目标文件列索引
|
||
ConflictResolution ConflictStrategy // 冲突解决策略
|
||
TransformFunc func([]string) ([]string, error) // 数据转换函数
|
||
}
|
||
|
||
// ConflictStrategy 冲突解决策略
|
||
type ConflictStrategy int
|
||
|
||
const (
|
||
Overwrite ConflictStrategy = iota // 覆盖目标文件的数据
|
||
Skip // 跳过冲突的行
|
||
KeepBoth // 保留两者
|
||
UseSource // 使用源数据
|
||
UseTarget // 使用目标数据
|
||
)
|
||
|
||
// CSVResponse CSV响应结构体
|
||
type CSVResponse struct {
|
||
Success bool `json:"success"`
|
||
Message string `json:"message,omitempty"`
|
||
Data interface{} `json:"data,omitempty"`
|
||
}
|
||
|
||
// DefaultConfig 默认配置
|
||
var DefaultConfig = ManagerConfig{
|
||
MaxOpenFiles: 100,
|
||
AutoCloseTimeout: 5 * time.Minute,
|
||
BufferSize: 32 * 1024, // 32KB
|
||
UseMMap: true,
|
||
MMapThreshold: 10 * 1024 * 1024, // 10MB
|
||
}
|
||
|
||
// 全局管理器实例
|
||
var (
|
||
globalManager *CSVManager
|
||
managerInitOnce sync.Once
|
||
)
|
||
|
||
// ========================== 辅助函数 ==========================
|
||
|
||
func min(a, b int) int {
|
||
if a < b {
|
||
return a
|
||
}
|
||
return b
|
||
}
|
||
|
||
// ========================== 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{},
|
||
fileLocks: sync.Map{},
|
||
nextHandle: 1,
|
||
maxOpen: config.MaxOpenFiles,
|
||
semaphore: make(chan struct{}, config.MaxOpenFiles),
|
||
config: config,
|
||
}
|
||
}
|
||
|
||
// ========================== CSVHandle 引用计数方法 ==========================
|
||
|
||
// addRef 增加引用计数
|
||
func (h *CSVHandle) addRef() {
|
||
atomic.AddInt64(&h.refCount, 1)
|
||
h.AccessTime = time.Now()
|
||
atomic.AddInt64(&h.AccessCount, 1)
|
||
}
|
||
|
||
// releaseRef 减少引用计数,如果为0则返回true表示可以关闭
|
||
func (h *CSVHandle) releaseRef() bool {
|
||
h.refCountMu.Lock()
|
||
defer h.refCountMu.Unlock()
|
||
|
||
oldCount := atomic.AddInt64(&h.refCount, -1)
|
||
return oldCount <= 0
|
||
}
|
||
|
||
// getRefCount 获取引用计数
|
||
func (h *CSVHandle) getRefCount() int64 {
|
||
return atomic.LoadInt64(&h.refCount)
|
||
}
|
||
|
||
// isValid 检查句柄是否有效
|
||
func (h *CSVHandle) isValid() bool {
|
||
h.statusMu.RLock()
|
||
defer h.statusMu.RUnlock()
|
||
return !h.closing && !h.closed && h.IsOpen
|
||
}
|
||
|
||
// markClosing 标记为正在关闭
|
||
func (h *CSVHandle) markClosing() {
|
||
h.statusMu.Lock()
|
||
defer h.statusMu.Unlock()
|
||
h.closing = true
|
||
}
|
||
|
||
// markClosed 标记为已关闭
|
||
func (h *CSVHandle) markClosed() {
|
||
h.statusMu.Lock()
|
||
defer h.statusMu.Unlock()
|
||
h.closing = false
|
||
h.closed = true
|
||
h.IsOpen = false
|
||
}
|
||
|
||
// beginOperation 开始一个操作
|
||
func (h *CSVHandle) beginOperation() bool {
|
||
h.statusMu.RLock()
|
||
if h.closing || h.closed || !h.IsOpen {
|
||
h.statusMu.RUnlock()
|
||
return false
|
||
}
|
||
atomic.AddInt64(&h.activeOps, 1)
|
||
h.statusMu.RUnlock()
|
||
return true
|
||
}
|
||
|
||
// endOperation 结束一个操作
|
||
func (h *CSVHandle) endOperation() {
|
||
atomic.AddInt64(&h.activeOps, -1)
|
||
}
|
||
|
||
// waitForActiveOps 等待所有活动操作完成
|
||
func (h *CSVHandle) waitForActiveOps(timeout time.Duration) bool {
|
||
start := time.Now()
|
||
for atomic.LoadInt64(&h.activeOps) > 0 {
|
||
if time.Since(start) > timeout {
|
||
return false
|
||
}
|
||
time.Sleep(10 * time.Millisecond)
|
||
}
|
||
return true
|
||
}
|
||
|
||
// ========================== CSVHandle 文件操作方法 ==========================
|
||
|
||
// close 关闭CSV句柄(内部方法)
|
||
func (h *CSVHandle) close() error {
|
||
h.mu.Lock()
|
||
defer h.mu.Unlock()
|
||
|
||
if !h.IsOpen {
|
||
return nil
|
||
}
|
||
|
||
// 停止自动关闭计时器
|
||
if h.autoCloseTimer != nil {
|
||
h.autoCloseTimer.Stop()
|
||
h.autoCloseTimer = nil
|
||
}
|
||
|
||
// 确保缓冲区数据写入文件
|
||
if h.CSVWriter != nil {
|
||
h.CSVWriter.Flush()
|
||
}
|
||
if h.writeBuffer != nil {
|
||
h.writeBuffer.Flush()
|
||
}
|
||
|
||
// 关闭文件
|
||
if h.File != nil {
|
||
if err := h.File.Close(); err != nil {
|
||
return err
|
||
}
|
||
h.File = nil
|
||
}
|
||
|
||
h.CSVReader = nil
|
||
h.CSVWriter = nil
|
||
h.writeBuffer = nil
|
||
h.IsOpen = false
|
||
h.markClosed()
|
||
|
||
return nil
|
||
}
|
||
|
||
// getHeader 获取表头
|
||
func (h *CSVHandle) getHeader() []string {
|
||
h.mu.RLock()
|
||
defer h.mu.RUnlock()
|
||
return h.Header
|
||
}
|
||
|
||
// calculateTotalRows 计算CSV文件的总行数
|
||
func (h *CSVHandle) calculateTotalRows() (int64, error) {
|
||
h.mu.Lock()
|
||
defer h.mu.Unlock()
|
||
|
||
// 如果已缓存,直接返回
|
||
if h.rowCountCached {
|
||
return h.cachedRowCount, nil
|
||
}
|
||
|
||
if !h.IsOpen {
|
||
return 0, fmt.Errorf("文件未打开")
|
||
}
|
||
|
||
// 保存当前文件位置
|
||
currentPos, err := h.File.Seek(0, 1) // 当前位置
|
||
if err != nil {
|
||
return 0, fmt.Errorf("获取当前文件位置失败: %w", err)
|
||
}
|
||
|
||
// 重置到文件开始
|
||
if _, err := h.File.Seek(0, 0); err != nil {
|
||
return 0, fmt.Errorf("重置文件指针失败: %w", err)
|
||
}
|
||
|
||
// 重置CSV阅读器
|
||
reader := csv.NewReader(bufio.NewReader(h.File))
|
||
reader.Comma = h.Delimiter
|
||
reader.LazyQuotes = true
|
||
reader.TrimLeadingSpace = true
|
||
reader.FieldsPerRecord = -1 // 允许字段数量可变(不检查每行字段数)
|
||
|
||
// 统计行数
|
||
var rowCount int64 = 0
|
||
for {
|
||
_, err := reader.Read()
|
||
if err != nil {
|
||
if err == io.EOF {
|
||
break
|
||
}
|
||
// 恢复文件位置
|
||
h.File.Seek(currentPos, 0)
|
||
return 0, fmt.Errorf("读取行失败: %w", err)
|
||
}
|
||
rowCount++
|
||
}
|
||
|
||
// 恢复文件位置
|
||
if _, err := h.File.Seek(currentPos, 0); err != nil {
|
||
return 0, fmt.Errorf("恢复文件位置失败: %w", err)
|
||
}
|
||
|
||
// 更新缓存
|
||
h.cachedRowCount = rowCount
|
||
h.rowCountCached = true
|
||
h.TotalRows = rowCount
|
||
|
||
// 如果有表头,需要减去表头行
|
||
if h.HasHeader && rowCount > 0 {
|
||
h.cachedRowCount = rowCount - 1
|
||
h.TotalRows = rowCount - 1
|
||
}
|
||
|
||
return h.cachedRowCount, nil
|
||
}
|
||
|
||
// ========================== CSVManager 文件操作方法 ==========================
|
||
|
||
// 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) {
|
||
// 获取文件锁
|
||
fileLock := mgr.getFileLock(filename)
|
||
fileLock.Lock()
|
||
defer fileLock.Unlock()
|
||
|
||
// 首先检查文件是否已经有打开的句柄
|
||
if existingHandleID := mgr.findHandleByFilename(filename); existingHandleID != -1 {
|
||
if handle, err := mgr.getHandle(existingHandleID); err == nil {
|
||
handle.addRef()
|
||
return existingHandleID, nil
|
||
}
|
||
}
|
||
|
||
// 检查文件是否存在,如果不存在则创建
|
||
fileInfo, err := os.Stat(filename)
|
||
fileExists := true
|
||
if os.IsNotExist(err) {
|
||
fileExists = false
|
||
// 创建文件
|
||
file, createErr := os.Create(filename)
|
||
if createErr != nil {
|
||
return -1, fmt.Errorf("创建文件失败: %w", createErr)
|
||
}
|
||
file.Close() // 关闭文件,后续会重新打开
|
||
|
||
// 重新获取文件信息
|
||
fileInfo, err = os.Stat(filename)
|
||
if err != nil {
|
||
return -1, fmt.Errorf("获取文件信息失败: %w", err)
|
||
}
|
||
} else if err != nil {
|
||
// 其他错误
|
||
return -1, fmt.Errorf("检查文件状态失败: %w", err)
|
||
}
|
||
|
||
// 如果是新创建的空文件,需要特殊处理
|
||
if !fileExists && fileInfo.Size() == 0 {
|
||
return mgr.createEmptyCSVHandle(filename, delimiter, hasHeader)
|
||
}
|
||
|
||
// 限制并发打开文件数
|
||
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(),
|
||
AccessCount: 1,
|
||
refCount: 1, // 初始引用计数为1
|
||
}
|
||
|
||
// 打开文件
|
||
if err := mgr.openFile(csvHandle); err != nil {
|
||
return -1, fmt.Errorf("打开文件失败: %w", err)
|
||
}
|
||
|
||
// 如果是新创建的文件,可能需要写入表头
|
||
if !fileExists && hasHeader {
|
||
// 创建空表头,用户后续可以写入实际表头
|
||
csvHandle.Header = []string{}
|
||
} else if hasHeader && fileInfo.Size() > 0 {
|
||
// 读取现有文件的表头
|
||
if err := mgr.readHeader(csvHandle); err != nil {
|
||
csvHandle.close()
|
||
return -1, fmt.Errorf("读取表头失败: %w", err)
|
||
}
|
||
}
|
||
|
||
// 如果有数据行,计算总行数
|
||
if fileInfo.Size() > 0 {
|
||
// 计算总行数
|
||
rows, err := csvHandle.calculateTotalRows()
|
||
if err != nil {
|
||
csvHandle.close()
|
||
return -1, fmt.Errorf("计算总行数失败: %w", err)
|
||
}
|
||
csvHandle.TotalRows = rows
|
||
} else {
|
||
csvHandle.TotalRows = 0
|
||
csvHandle.cachedRowCount = 0
|
||
csvHandle.rowCountCached = true
|
||
}
|
||
|
||
// 注册到管理器
|
||
mgr.handles.Store(handleID, csvHandle)
|
||
|
||
// 启动自动关闭计时器
|
||
if mgr.config.AutoCloseTimeout > 0 {
|
||
csvHandle.startAutoClose(mgr.config.AutoCloseTimeout, mgr)
|
||
}
|
||
|
||
return handleID, nil
|
||
}
|
||
|
||
// findHandleByFilename 根据文件名查找已存在的句柄ID
|
||
func (mgr *CSVManager) findHandleByFilename(filename string) int64 {
|
||
var existingHandleID int64 = -1
|
||
|
||
// 遍历所有句柄,查找相同文件名的句柄
|
||
mgr.handles.Range(func(key, value interface{}) bool {
|
||
handle := value.(*CSVHandle)
|
||
|
||
// 检查文件名是否相同
|
||
if handle.Filename == filename {
|
||
// 检查句柄是否有效
|
||
if handle.isValid() {
|
||
existingHandleID = handle.ID
|
||
return false // 停止遍历
|
||
}
|
||
}
|
||
return true // 继续遍历
|
||
})
|
||
|
||
return existingHandleID
|
||
}
|
||
|
||
// createEmptyCSVHandle 创建空的CSV文件句柄
|
||
func (mgr *CSVManager) createEmptyCSVHandle(filename string, delimiter rune, hasHeader bool) (int64, error) {
|
||
// 限制并发打开文件数
|
||
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(),
|
||
TotalRows: 0,
|
||
AccessCount: 1,
|
||
refCount: 1, // 初始引用计数为1
|
||
}
|
||
|
||
// 以读写模式打开文件
|
||
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, 0644)
|
||
if err != nil {
|
||
return -1, fmt.Errorf("打开文件失败: %w", err)
|
||
}
|
||
|
||
// 创建CSV写入器
|
||
writer := csv.NewWriter(file)
|
||
writer.Comma = delimiter
|
||
|
||
// 如果是新创建的文件且有表头,写入空表头占位
|
||
if hasHeader {
|
||
// 写入空表头(用户后续需要设置实际表头)
|
||
if err := writer.Write([]string{}); err != nil {
|
||
file.Close()
|
||
return -1, fmt.Errorf("写入空表头失败: %w", err)
|
||
}
|
||
writer.Flush()
|
||
|
||
// 重置到文件开始位置
|
||
if _, err := file.Seek(0, 0); err != nil {
|
||
file.Close()
|
||
return -1, fmt.Errorf("重置文件指针失败: %w", err)
|
||
}
|
||
}
|
||
|
||
// 创建CSV阅读器
|
||
reader := csv.NewReader(bufio.NewReaderSize(file, mgr.config.BufferSize))
|
||
reader.Comma = delimiter
|
||
reader.LazyQuotes = true
|
||
reader.TrimLeadingSpace = true
|
||
|
||
// 创建写入缓冲区
|
||
writeBuffer := bufio.NewWriterSize(file, mgr.config.BufferSize)
|
||
|
||
csvHandle.File = file
|
||
csvHandle.CSVReader = reader
|
||
csvHandle.CSVWriter = csv.NewWriter(writeBuffer)
|
||
csvHandle.CSVWriter.Comma = delimiter
|
||
csvHandle.writeBuffer = writeBuffer
|
||
csvHandle.IsOpen = true
|
||
csvHandle.AccessTime = time.Now()
|
||
|
||
// 如果有表头,初始化空表头数组
|
||
if hasHeader {
|
||
csvHandle.Header = []string{}
|
||
}
|
||
|
||
// 注册到管理器
|
||
mgr.handles.Store(handleID, csvHandle)
|
||
|
||
// 启动自动关闭计时器
|
||
if mgr.config.AutoCloseTimeout > 0 {
|
||
csvHandle.startAutoClose(mgr.config.AutoCloseTimeout, mgr)
|
||
}
|
||
|
||
return handleID, 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)
|
||
|
||
// 检查句柄是否有效
|
||
if !handle.isValid() {
|
||
return nil, fmt.Errorf("句柄无效: %d", handleID)
|
||
}
|
||
|
||
// 增加引用计数
|
||
handle.addRef()
|
||
|
||
// 确保文件已打开
|
||
handle.mu.RLock()
|
||
isOpen := handle.IsOpen
|
||
handle.mu.RUnlock()
|
||
|
||
if !isOpen {
|
||
// 获取文件锁
|
||
fileLock := mgr.getFileLock(handle.Filename)
|
||
fileLock.Lock()
|
||
defer fileLock.Unlock()
|
||
|
||
// 重新检查,防止其他协程已经重新打开了
|
||
handle.mu.RLock()
|
||
isOpen = handle.IsOpen
|
||
handle.mu.RUnlock()
|
||
|
||
if !isOpen {
|
||
if err := mgr.openFile(handle); err != nil {
|
||
// 恢复引用计数
|
||
handle.releaseRef()
|
||
return nil, fmt.Errorf("重新打开文件失败: %w", err)
|
||
}
|
||
}
|
||
}
|
||
|
||
return handle, nil
|
||
}
|
||
|
||
// releaseHandle 释放句柄引用
|
||
func (mgr *CSVManager) releaseHandle(handleID int64) {
|
||
value, ok := mgr.handles.Load(handleID)
|
||
if !ok {
|
||
return
|
||
}
|
||
|
||
handle := value.(*CSVHandle)
|
||
// 减少引用计数,如果为0则真正关闭
|
||
if handle.releaseRef() {
|
||
mgr.closeHandleInternal(handleID, handle)
|
||
}
|
||
}
|
||
|
||
// ========================== 句柄管理方法 ==========================
|
||
|
||
// closeHandleInternal 内部关闭句柄方法
|
||
func (mgr *CSVManager) closeHandleInternal(handleID int64, handle *CSVHandle) {
|
||
// 获取文件锁
|
||
fileLock := mgr.getFileLock(handle.Filename)
|
||
fileLock.Lock()
|
||
defer fileLock.Unlock()
|
||
|
||
// 再次检查引用计数,防止在获取锁期间有新的引用
|
||
if handle.getRefCount() > 0 {
|
||
return
|
||
}
|
||
|
||
// 标记为正在关闭
|
||
handle.markClosing()
|
||
|
||
// 等待所有活动操作完成
|
||
if !handle.waitForActiveOps(30 * time.Second) {
|
||
// 超时,强制关闭
|
||
fmt.Printf("警告:句柄 %d 关闭超时,强制关闭\n", handleID)
|
||
}
|
||
|
||
// 真正关闭文件
|
||
handle.close()
|
||
|
||
// 从管理器移除
|
||
mgr.handles.Delete(handleID)
|
||
}
|
||
|
||
// CloseHandle 关闭指定句柄(外部调用)
|
||
func (mgr *CSVManager) CloseHandle(handleID int64) error {
|
||
value, ok := mgr.handles.Load(handleID)
|
||
if !ok {
|
||
return fmt.Errorf("句柄不存在: %d", handleID)
|
||
}
|
||
|
||
handle := value.(*CSVHandle)
|
||
|
||
// 减少引用计数,如果为0则真正关闭
|
||
if handle.releaseRef() {
|
||
mgr.closeHandleInternal(handleID, handle)
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// GracefulCloseHandle 优雅关闭句柄(等待所有操作完成)
|
||
func (mgr *CSVManager) GracefulCloseHandle(handleID int64, timeout time.Duration) error {
|
||
value, ok := mgr.handles.Load(handleID)
|
||
if !ok {
|
||
return fmt.Errorf("句柄不存在: %d", handleID)
|
||
}
|
||
|
||
handle := value.(*CSVHandle)
|
||
|
||
// 标记为正在关闭,阻止新操作
|
||
handle.markClosing()
|
||
|
||
// 等待现有操作完成
|
||
done := make(chan bool, 1)
|
||
go func() {
|
||
// 等待引用计数降为1(只剩当前引用)
|
||
for handle.getRefCount() > 1 {
|
||
time.Sleep(100 * time.Millisecond)
|
||
}
|
||
done <- true
|
||
}()
|
||
|
||
// 设置超时
|
||
select {
|
||
case <-done:
|
||
// 真正关闭
|
||
return mgr.ForceCloseHandle(handleID)
|
||
case <-time.After(timeout):
|
||
return fmt.Errorf("关闭句柄超时,仍有 %d 个引用", handle.getRefCount())
|
||
}
|
||
}
|
||
|
||
// ForceCloseHandle 强制关闭句柄(无论引用计数如何)
|
||
func (mgr *CSVManager) ForceCloseHandle(handleID int64) error {
|
||
value, ok := mgr.handles.Load(handleID)
|
||
if !ok {
|
||
return fmt.Errorf("句柄不存在: %d", handleID)
|
||
}
|
||
|
||
handle := value.(*CSVHandle)
|
||
|
||
// 强制设置引用计数为0
|
||
handle.refCountMu.Lock()
|
||
atomic.StoreInt64(&handle.refCount, 0)
|
||
handle.refCountMu.Unlock()
|
||
|
||
// 获取文件锁
|
||
fileLock := mgr.getFileLock(handle.Filename)
|
||
fileLock.Lock()
|
||
defer fileLock.Unlock()
|
||
|
||
// 标记为正在关闭
|
||
handle.markClosing()
|
||
|
||
// 等待所有活动操作完成
|
||
handle.waitForActiveOps(5 * time.Second)
|
||
|
||
// 真正关闭文件
|
||
handle.close()
|
||
mgr.handles.Delete(handle.ID)
|
||
|
||
return nil
|
||
}
|
||
|
||
// closeAllHandles 关闭所有句柄
|
||
func (mgr *CSVManager) closeAllHandles() {
|
||
// 收集所有句柄ID
|
||
var handleIDs []int64
|
||
mgr.handles.Range(func(key, value interface{}) bool {
|
||
handleIDs = append(handleIDs, key.(int64))
|
||
return true
|
||
})
|
||
|
||
// 关闭每个句柄
|
||
for _, handleID := range handleIDs {
|
||
mgr.ForceCloseHandle(handleID)
|
||
}
|
||
}
|
||
|
||
// ========================== 文件操作方法 ==========================
|
||
|
||
// 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)
|
||
}
|
||
|
||
// 正常打开文件
|
||
func (mgr *CSVManager) openFileNormal(handle *CSVHandle) error {
|
||
// 以读写模式打开文件
|
||
file, err := os.OpenFile(handle.Filename, os.O_RDWR, 0644)
|
||
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
|
||
|
||
// 创建写入缓冲区
|
||
writeBuffer := bufio.NewWriterSize(file, mgr.config.BufferSize)
|
||
|
||
handle.File = file
|
||
handle.CSVReader = reader
|
||
handle.CSVWriter = csv.NewWriter(writeBuffer)
|
||
handle.CSVWriter.Comma = handle.Delimiter
|
||
handle.writeBuffer = writeBuffer
|
||
handle.IsOpen = true
|
||
handle.AccessTime = time.Now()
|
||
|
||
return nil
|
||
}
|
||
|
||
// 使用内存映射打开大文件
|
||
func (mgr *CSVManager) openFileWithMMap(handle *CSVHandle, fileSize int64) error {
|
||
// 对于大文件,使用只读模式打开,写入需要特殊处理
|
||
file, err := os.OpenFile(handle.Filename, os.O_RDWR, 0644)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
// 对于大文件,我们可以先只打开,按需读取
|
||
reader := csv.NewReader(bufio.NewReaderSize(file, mgr.config.BufferSize))
|
||
reader.Comma = handle.Delimiter
|
||
reader.LazyQuotes = true
|
||
|
||
// 创建写入缓冲区
|
||
writeBuffer := bufio.NewWriterSize(file, mgr.config.BufferSize)
|
||
|
||
handle.File = file
|
||
handle.CSVReader = reader
|
||
handle.CSVWriter = csv.NewWriter(writeBuffer)
|
||
handle.CSVWriter.Comma = handle.Delimiter
|
||
handle.writeBuffer = writeBuffer
|
||
handle.IsOpen = true
|
||
handle.AccessTime = time.Now()
|
||
|
||
return nil
|
||
}
|
||
|
||
// 读取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(bufio.NewReader(handle.File))
|
||
handle.CSVReader.Comma = handle.Delimiter
|
||
|
||
// 读取表头
|
||
header, err := handle.CSVReader.Read()
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
handle.Header = header
|
||
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 {
|
||
// 检查引用计数
|
||
if h.getRefCount() == 0 {
|
||
h.close()
|
||
// 从管理器移除
|
||
mgr.handles.Delete(h.ID)
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
// ========================== 写入功能 ==========================
|
||
|
||
// WriteHeader 写入CSV文件表头
|
||
func (mgr *CSVManager) WriteHeader(handleID int64, header []string) error {
|
||
// 获取句柄
|
||
handle, err := mgr.getHandle(handleID)
|
||
if err != nil {
|
||
return fmt.Errorf("获取句柄失败: %w", err)
|
||
}
|
||
defer mgr.releaseHandle(handleID)
|
||
|
||
// 检查句柄状态
|
||
if !handle.beginOperation() {
|
||
return fmt.Errorf("句柄已关闭或正在关闭: %d", handleID)
|
||
}
|
||
defer handle.endOperation()
|
||
|
||
handle.mu.Lock()
|
||
defer handle.mu.Unlock()
|
||
|
||
if !handle.IsOpen {
|
||
return fmt.Errorf("文件未打开")
|
||
}
|
||
|
||
// 检查文件是否已经有内容
|
||
if handle.TotalRows > 0 {
|
||
return fmt.Errorf("文件已有数据,无法修改表头")
|
||
}
|
||
|
||
// 移动到文件开头
|
||
if _, err := handle.File.Seek(0, 0); err != nil {
|
||
return fmt.Errorf("移动文件指针失败: %w", err)
|
||
}
|
||
|
||
// 清空文件内容
|
||
if err := handle.File.Truncate(0); err != nil {
|
||
return fmt.Errorf("清空文件失败: %w", err)
|
||
}
|
||
|
||
// 写入表头
|
||
if err := handle.CSVWriter.Write(header); err != nil {
|
||
return fmt.Errorf("写入表头失败: %w", err)
|
||
}
|
||
|
||
handle.CSVWriter.Flush()
|
||
if handle.writeBuffer != nil {
|
||
handle.writeBuffer.Flush()
|
||
}
|
||
|
||
// 更新句柄状态
|
||
handle.Header = header
|
||
handle.HasHeader = true
|
||
|
||
return nil
|
||
}
|
||
|
||
// WriteRow 写入单行数据到CSV文件
|
||
func (mgr *CSVManager) WriteRow(handleID int64, row []string) error {
|
||
// 获取句柄
|
||
handle, err := mgr.getHandle(handleID)
|
||
if err != nil {
|
||
return fmt.Errorf("获取句柄失败: %w", err)
|
||
}
|
||
defer mgr.releaseHandle(handleID)
|
||
|
||
// 检查句柄状态
|
||
if !handle.beginOperation() {
|
||
return fmt.Errorf("句柄已关闭或正在关闭: %d", handleID)
|
||
}
|
||
defer handle.endOperation()
|
||
|
||
handle.mu.Lock()
|
||
defer handle.mu.Unlock()
|
||
|
||
if !handle.IsOpen {
|
||
return fmt.Errorf("文件未打开")
|
||
}
|
||
|
||
// 检查是否有表头且表头长度是否匹配
|
||
if handle.HasHeader && len(handle.Header) > 0 && len(row) != len(handle.Header) {
|
||
return fmt.Errorf("行数据列数(%d)与表头列数(%d)不匹配", len(row), len(handle.Header))
|
||
}
|
||
|
||
// 移动到文件末尾
|
||
if _, err := handle.File.Seek(0, 2); err != nil {
|
||
return fmt.Errorf("移动到文件末尾失败: %w", err)
|
||
}
|
||
|
||
// 写入行数据
|
||
if err := handle.CSVWriter.Write(row); err != nil {
|
||
return fmt.Errorf("写入行失败: %w", err)
|
||
}
|
||
|
||
// 更新行数统计
|
||
handle.TotalRows++
|
||
handle.cachedRowCount = handle.TotalRows
|
||
handle.rowCountCached = true
|
||
|
||
return nil
|
||
}
|
||
|
||
// WriteRows 批量写入多行数据到CSV文件
|
||
func (mgr *CSVManager) WriteRows(handleID int64, rows [][]string) (int64, error) {
|
||
// 首先记录日志
|
||
if err := mgr.logWriteRows(handleID, rows); err != nil {
|
||
// 日志记录失败不影响主流程,但可以打印警告
|
||
fmt.Printf("警告:记录日志失败: %v\n", err)
|
||
}
|
||
|
||
// 获取句柄
|
||
handle, err := mgr.getHandle(handleID)
|
||
if err != nil {
|
||
return -1, fmt.Errorf("WriteRows 获取句柄失败: %w", err)
|
||
}
|
||
defer mgr.releaseHandle(handleID)
|
||
|
||
// 检查句柄状态
|
||
if !handle.beginOperation() {
|
||
return -1, fmt.Errorf("句柄已关闭或正在关闭: %d", handleID)
|
||
}
|
||
defer handle.endOperation()
|
||
|
||
handle.mu.Lock()
|
||
defer handle.mu.Unlock()
|
||
|
||
if !handle.IsOpen {
|
||
return -1, fmt.Errorf("文件未打开")
|
||
}
|
||
|
||
// 检查是否有表头
|
||
if handle.HasHeader && len(handle.Header) > 0 {
|
||
// 验证每行的列数
|
||
for i, row := range rows {
|
||
if len(row) != len(handle.Header) {
|
||
return -1, fmt.Errorf("第%d行列数(%d)与表头列数(%d)不匹配", i+1, len(row), len(handle.Header))
|
||
}
|
||
}
|
||
}
|
||
|
||
// 移动到文件末尾
|
||
if _, err := handle.File.Seek(0, 2); err != nil {
|
||
return -1, fmt.Errorf("移动到文件末尾失败: %w", err)
|
||
}
|
||
|
||
// 批量写入行数据
|
||
for _, row := range rows {
|
||
if err := handle.CSVWriter.Write(row); err != nil {
|
||
return -1, fmt.Errorf("写入行失败: %w", err)
|
||
}
|
||
}
|
||
|
||
// 更新行数统计
|
||
handle.TotalRows += int64(len(rows))
|
||
handle.cachedRowCount = handle.TotalRows
|
||
handle.rowCountCached = true
|
||
|
||
return handle.TotalRows, nil
|
||
}
|
||
|
||
// logWriteRows 记录行数据到日志文件
|
||
func (mgr *CSVManager) logWriteRows(handleID int64, rows [][]string) error {
|
||
// 获取句柄信息(但不锁定,因为我们只是读取元数据)
|
||
handle, err := mgr.getHandle(handleID)
|
||
if err != nil {
|
||
return fmt.Errorf("获取句柄信息失败: %w", err)
|
||
}
|
||
defer mgr.releaseHandle(handleID)
|
||
|
||
// 创建日志目录
|
||
logDir := filepath.Join(filepath.Dir("csv"), "logs")
|
||
if err := os.MkdirAll(logDir, 0755); err != nil {
|
||
return fmt.Errorf("创建日志目录失败: %w", err)
|
||
}
|
||
|
||
// 生成日志文件名(基于CSV文件名和时间)
|
||
baseName := filepath.Base("csv")
|
||
logFileName := fmt.Sprintf("%s_%s_write.log",
|
||
baseName[:len(baseName)-len(filepath.Ext(baseName))],
|
||
time.Now().Format("20060102"))
|
||
logFilePath := filepath.Join(logDir, logFileName)
|
||
|
||
// 打开或创建日志文件
|
||
logFile, err := os.OpenFile(logFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||
if err != nil {
|
||
return fmt.Errorf("打开日志文件失败: %w", err)
|
||
}
|
||
defer logFile.Close()
|
||
|
||
// 创建日志条目
|
||
logEntry := map[string]interface{}{
|
||
"timestamp": time.Now().Format("2006-01-02 15:04:05.000"),
|
||
"handle_id": handleID,
|
||
"filename": "csv",
|
||
"operation": "write_rows",
|
||
"row_count": len(rows),
|
||
"total_rows": handle.TotalRows + int64(len(rows)), // 预计的总行数
|
||
"data": rows,
|
||
}
|
||
|
||
// 序列化为JSON
|
||
logData, err := json.Marshal(logEntry)
|
||
if err != nil {
|
||
return fmt.Errorf("序列化日志数据失败: %w", err)
|
||
}
|
||
|
||
// 写入日志(每行一个JSON对象)
|
||
if _, err := logFile.Write(append(logData, '\n')); err != nil {
|
||
return fmt.Errorf("写入日志文件失败: %w", err)
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// Flush 将缓冲区数据写入文件
|
||
func (mgr *CSVManager) Flush(handleID int64) error {
|
||
// 获取句柄
|
||
handle, err := mgr.getHandle(handleID)
|
||
if err != nil {
|
||
return fmt.Errorf("Flush 获取句柄失败: %w", err)
|
||
}
|
||
defer mgr.releaseHandle(handleID)
|
||
|
||
// 检查句柄状态
|
||
if !handle.beginOperation() {
|
||
return fmt.Errorf("句柄已关闭或正在关闭: %d", handleID)
|
||
}
|
||
defer handle.endOperation()
|
||
|
||
handle.mu.Lock()
|
||
defer handle.mu.Unlock()
|
||
|
||
if !handle.IsOpen {
|
||
return fmt.Errorf("文件未打开")
|
||
}
|
||
|
||
if handle.CSVWriter == nil {
|
||
return fmt.Errorf("CSV写入器未初始化")
|
||
}
|
||
|
||
// 刷新缓冲区
|
||
handle.CSVWriter.Flush()
|
||
if handle.writeBuffer != nil {
|
||
if err := handle.writeBuffer.Flush(); err != nil {
|
||
return fmt.Errorf("刷新写入缓冲区失败: %w", err)
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// AppendRow 追加行数据(自动Flush)
|
||
func (mgr *CSVManager) AppendRow(handleID int64, row []string) error {
|
||
if err := mgr.WriteRow(handleID, row); err != nil {
|
||
return err
|
||
}
|
||
return mgr.Flush(handleID)
|
||
}
|
||
|
||
// AppendRows 批量追加行数据(自动Flush)
|
||
func (mgr *CSVManager) AppendRows(handleID int64, rows [][]string) (int64, error) {
|
||
totalRows, err := mgr.WriteRows(handleID, rows)
|
||
if err != nil {
|
||
return -1, err
|
||
}
|
||
err = mgr.Flush(handleID)
|
||
if err != nil {
|
||
return -1, err
|
||
}
|
||
return totalRows, nil
|
||
}
|
||
|
||
// GetHeader 获取CSV文件表头
|
||
func (mgr *CSVManager) GetHeader(handleID int64) ([]string, error) {
|
||
// 获取句柄
|
||
handle, err := mgr.getHandle(handleID)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("获取句柄失败: %w", err)
|
||
}
|
||
defer mgr.releaseHandle(handleID)
|
||
|
||
// 检查句柄状态
|
||
if !handle.beginOperation() {
|
||
return nil, fmt.Errorf("句柄已关闭或正在关闭: %d", handleID)
|
||
}
|
||
defer handle.endOperation()
|
||
|
||
handle.mu.RLock()
|
||
defer handle.mu.RUnlock()
|
||
|
||
if !handle.HasHeader {
|
||
return nil, fmt.Errorf("文件没有表头")
|
||
}
|
||
|
||
return handle.Header, nil
|
||
}
|
||
|
||
// ========================== 获取总行数 ==========================
|
||
|
||
// GetTotalRows 快速获取总行数(如果已计算过则直接返回,否则重新计算)
|
||
func (mgr *CSVManager) GetTotalRows(handleID int64) (int64, error) {
|
||
// 获取句柄
|
||
handle, err := mgr.getHandle(handleID)
|
||
if err != nil {
|
||
return 0, fmt.Errorf("获取句柄失败: %w", err)
|
||
}
|
||
defer mgr.releaseHandle(handleID)
|
||
|
||
// 检查句柄状态
|
||
if !handle.beginOperation() {
|
||
return 0, fmt.Errorf("句柄已关闭或正在关闭: %d", handleID)
|
||
}
|
||
defer handle.endOperation()
|
||
|
||
handle.mu.RLock()
|
||
// 如果已缓存,直接返回缓存值
|
||
if handle.rowCountCached {
|
||
count := handle.cachedRowCount
|
||
handle.mu.RUnlock()
|
||
return count, nil
|
||
}
|
||
handle.mu.RUnlock()
|
||
|
||
// 否则计算总行数
|
||
return handle.calculateTotalRows()
|
||
}
|
||
|
||
// ========================== 修改指定行功能 ==========================
|
||
|
||
// UpdateRow 修改指定行的数据
|
||
func (mgr *CSVManager) UpdateRow(handleID int64, rowNumber int64, newData []string) error {
|
||
// 获取句柄
|
||
handle, err := mgr.getHandle(handleID)
|
||
if err != nil {
|
||
return fmt.Errorf("获取句柄失败: %w", err)
|
||
}
|
||
defer mgr.releaseHandle(handleID)
|
||
|
||
// 检查句柄状态
|
||
if !handle.beginOperation() {
|
||
return fmt.Errorf("句柄已关闭或正在关闭: %d", handleID)
|
||
}
|
||
defer handle.endOperation()
|
||
|
||
//handle.mu.Lock()
|
||
//defer handle.mu.Unlock()
|
||
|
||
if !handle.IsOpen {
|
||
return fmt.Errorf("文件未打开")
|
||
}
|
||
|
||
// 检查行号是否有效
|
||
if rowNumber < 0 {
|
||
return fmt.Errorf("行号不能为负数: %d", rowNumber)
|
||
}
|
||
|
||
// 获取总行数
|
||
totalRows, err := handle.calculateTotalRows()
|
||
if err != nil {
|
||
return fmt.Errorf("获取总行数失败: %w", err)
|
||
}
|
||
|
||
if rowNumber >= totalRows {
|
||
return fmt.Errorf("行号超出范围: %d, 总行数: %d", rowNumber, totalRows)
|
||
}
|
||
|
||
// 检查新数据列数是否匹配
|
||
if handle.HasHeader && len(handle.Header) > 0 {
|
||
if len(newData) != len(handle.Header) {
|
||
return fmt.Errorf("新数据列数(%d)与表头列数(%d)不匹配", len(newData), len(handle.Header))
|
||
}
|
||
}
|
||
|
||
// 读取整个文件到内存
|
||
allRows, err := mgr.readAllRows(handle)
|
||
if err != nil {
|
||
return fmt.Errorf("读取文件失败: %w", err)
|
||
}
|
||
|
||
// 修改指定行的数据
|
||
if handle.HasHeader && len(allRows) > 0 {
|
||
// 如果有表头,第一行是表头,实际数据从第二行开始
|
||
if rowNumber+1 >= int64(len(allRows)) {
|
||
return fmt.Errorf("行号超出范围: %d, 总数据行数: %d", rowNumber, len(allRows)-1)
|
||
}
|
||
allRows[rowNumber+1] = newData
|
||
} else {
|
||
if rowNumber >= int64(len(allRows)) {
|
||
return fmt.Errorf("行号超出范围: %d, 总行数: %d", rowNumber, len(allRows))
|
||
}
|
||
allRows[rowNumber] = newData
|
||
}
|
||
|
||
// 写回文件
|
||
if err := mgr.writeAllRows(handle, allRows); err != nil {
|
||
return fmt.Errorf("写回文件失败: %w", err)
|
||
}
|
||
|
||
// 更新缓存的行数
|
||
handle.rowCountCached = false
|
||
|
||
// 重新计算总行数
|
||
if _, err := handle.calculateTotalRows(); err != nil {
|
||
return fmt.Errorf("重新计算总行数失败: %w", err)
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// UpdateRows 批量修改多行数据
|
||
func (mgr *CSVManager) UpdateRows(handleID int64, updates map[int64][]string) (int, error) {
|
||
// 获取句柄
|
||
handle, err := mgr.getHandle(handleID)
|
||
if err != nil {
|
||
return 0, fmt.Errorf("获取句柄失败: %w", err)
|
||
}
|
||
defer mgr.releaseHandle(handleID)
|
||
|
||
// 检查句柄状态
|
||
if !handle.beginOperation() {
|
||
return 0, fmt.Errorf("句柄已关闭或正在关闭: %d", handleID)
|
||
}
|
||
defer handle.endOperation()
|
||
|
||
handle.mu.Lock()
|
||
defer handle.mu.Unlock()
|
||
|
||
if !handle.IsOpen {
|
||
return 0, fmt.Errorf("文件未打开")
|
||
}
|
||
|
||
// 检查行号是否有效
|
||
for rowNumber := range updates {
|
||
if rowNumber < 0 {
|
||
return 0, fmt.Errorf("行号不能为负数: %d", rowNumber)
|
||
}
|
||
}
|
||
|
||
// 获取总行数
|
||
totalRows, err := handle.calculateTotalRows()
|
||
if err != nil {
|
||
return 0, fmt.Errorf("获取总行数失败: %w", err)
|
||
}
|
||
|
||
// 检查新数据列数是否匹配
|
||
if handle.HasHeader && len(handle.Header) > 0 {
|
||
for rowNumber, newData := range updates {
|
||
if rowNumber >= totalRows {
|
||
return 0, fmt.Errorf("行号超出范围: %d, 总行数: %d", rowNumber, totalRows)
|
||
}
|
||
if len(newData) != len(handle.Header) {
|
||
return 0, fmt.Errorf("行%d的新数据列数(%d)与表头列数(%d)不匹配", rowNumber, len(newData), len(handle.Header))
|
||
}
|
||
}
|
||
}
|
||
|
||
// 读取整个文件到内存
|
||
allRows, err := mgr.readAllRows(handle)
|
||
if err != nil {
|
||
return 0, fmt.Errorf("读取文件失败: %w", err)
|
||
}
|
||
|
||
// 修改指定行的数据
|
||
updatedCount := 0
|
||
for rowNumber, newData := range updates {
|
||
if handle.HasHeader && len(allRows) > 0 {
|
||
// 如果有表头,第一行是表头,实际数据从第二行开始
|
||
if rowNumber+1 >= int64(len(allRows)) {
|
||
return 0, fmt.Errorf("行号超出范围: %d, 总数据行数: %d", rowNumber, len(allRows)-1)
|
||
}
|
||
allRows[rowNumber+1] = newData
|
||
updatedCount++
|
||
} else {
|
||
if rowNumber >= int64(len(allRows)) {
|
||
return 0, fmt.Errorf("行号超出范围: %d, 总行数: %d", rowNumber, len(allRows))
|
||
}
|
||
allRows[rowNumber] = newData
|
||
updatedCount++
|
||
}
|
||
}
|
||
|
||
// 写回文件
|
||
if err := mgr.writeAllRows(handle, allRows); err != nil {
|
||
return 0, fmt.Errorf("写回文件失败: %w", err)
|
||
}
|
||
|
||
// 更新缓存的行数
|
||
handle.rowCountCached = false
|
||
|
||
// 重新计算总行数
|
||
if _, err := handle.calculateTotalRows(); err != nil {
|
||
return 0, fmt.Errorf("重新计算总行数失败: %w", err)
|
||
}
|
||
|
||
return updatedCount, nil
|
||
}
|
||
|
||
// readAllRows 读取文件中的所有行
|
||
func (mgr *CSVManager) readAllRows(handle *CSVHandle) ([][]string, error) {
|
||
// 保存当前文件位置
|
||
currentPos, err := handle.File.Seek(0, 1) // 当前位置
|
||
if err != nil {
|
||
return nil, fmt.Errorf("获取当前文件位置失败: %w", err)
|
||
}
|
||
|
||
// 重置到文件开始
|
||
if _, err := handle.File.Seek(0, 0); err != nil {
|
||
return nil, fmt.Errorf("重置文件指针失败: %w", err)
|
||
}
|
||
|
||
// 重置CSV阅读器
|
||
reader := csv.NewReader(bufio.NewReader(handle.File))
|
||
reader.Comma = handle.Delimiter
|
||
reader.LazyQuotes = true
|
||
reader.TrimLeadingSpace = true
|
||
|
||
// 读取所有行
|
||
var allRows [][]string
|
||
for {
|
||
row, err := reader.Read()
|
||
if err != nil {
|
||
if err == io.EOF {
|
||
break
|
||
}
|
||
// 恢复文件位置
|
||
handle.File.Seek(currentPos, 0)
|
||
return nil, fmt.Errorf("读取行失败: %w", err)
|
||
}
|
||
allRows = append(allRows, row)
|
||
}
|
||
|
||
// 恢复文件位置
|
||
if _, err := handle.File.Seek(currentPos, 0); err != nil {
|
||
return nil, fmt.Errorf("恢复文件位置失败: %w", err)
|
||
}
|
||
|
||
return allRows, nil
|
||
}
|
||
|
||
// writeAllRows 将所有行写回文件
|
||
func (mgr *CSVManager) writeAllRows(handle *CSVHandle, rows [][]string) error {
|
||
// 清空文件
|
||
if err := handle.File.Truncate(0); err != nil {
|
||
return fmt.Errorf("清空文件失败: %w", err)
|
||
}
|
||
|
||
// 移动到文件开头
|
||
if _, err := handle.File.Seek(0, 0); err != nil {
|
||
return fmt.Errorf("移动文件指针失败: %w", err)
|
||
}
|
||
|
||
// 写入所有行
|
||
for _, row := range rows {
|
||
if err := handle.CSVWriter.Write(row); err != nil {
|
||
return fmt.Errorf("写入行失败: %w", err)
|
||
}
|
||
}
|
||
|
||
// 刷新缓冲区
|
||
handle.CSVWriter.Flush()
|
||
if handle.writeBuffer != nil {
|
||
if err := handle.writeBuffer.Flush(); err != nil {
|
||
return fmt.Errorf("刷新写入缓冲区失败: %w", err)
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// GetRow 获取指定行的数据
|
||
func (mgr *CSVManager) GetRow(handleID int64, rowNumber int64) ([]string, error) {
|
||
// 获取句柄
|
||
handle, err := mgr.getHandle(handleID)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("获取句柄失败: %w", err)
|
||
}
|
||
defer mgr.releaseHandle(handleID)
|
||
|
||
// 检查句柄状态
|
||
if !handle.beginOperation() {
|
||
return nil, fmt.Errorf("句柄已关闭或正在关闭: %d", handleID)
|
||
}
|
||
defer handle.endOperation()
|
||
|
||
//handle.mu.RLock()
|
||
//defer handle.mu.RUnlock()
|
||
|
||
if !handle.IsOpen {
|
||
return nil, fmt.Errorf("文件未打开")
|
||
}
|
||
|
||
// 检查行号是否有效
|
||
if rowNumber < 0 {
|
||
return nil, fmt.Errorf("行号不能为负数: %d", rowNumber)
|
||
}
|
||
|
||
// 获取总行数
|
||
totalRows, err := handle.calculateTotalRows()
|
||
if err != nil {
|
||
return nil, fmt.Errorf("获取总行数失败: %w", err)
|
||
}
|
||
|
||
if rowNumber >= totalRows {
|
||
return nil, fmt.Errorf("行号超出范围: %d, 总行数: %d", rowNumber, totalRows)
|
||
}
|
||
|
||
// 保存当前文件位置
|
||
currentPos, err := handle.File.Seek(0, 1) // 当前位置
|
||
if err != nil {
|
||
return nil, fmt.Errorf("获取当前文件位置失败: %w", err)
|
||
}
|
||
|
||
// 重置到文件开始
|
||
if _, err := handle.File.Seek(0, 0); err != nil {
|
||
return nil, fmt.Errorf("重置文件指针失败: %w", err)
|
||
}
|
||
|
||
// 重置CSV阅读器
|
||
reader := csv.NewReader(bufio.NewReader(handle.File))
|
||
reader.Comma = handle.Delimiter
|
||
reader.LazyQuotes = true
|
||
reader.TrimLeadingSpace = true
|
||
|
||
// 定位到指定行
|
||
var currentRow int64 = 0
|
||
var targetRow []string
|
||
for {
|
||
row, err := reader.Read()
|
||
if err != nil {
|
||
if err == io.EOF {
|
||
break
|
||
}
|
||
// 恢复文件位置
|
||
handle.File.Seek(currentPos, 0)
|
||
return nil, fmt.Errorf("读取行失败: %w", err)
|
||
}
|
||
|
||
// 判断是否到达目标行
|
||
if handle.HasHeader {
|
||
// 如果有表头,跳过第一行
|
||
if currentRow == rowNumber+1 {
|
||
targetRow = row
|
||
break
|
||
}
|
||
} else {
|
||
// 如果没有表头,直接匹配
|
||
if currentRow == rowNumber {
|
||
targetRow = row
|
||
break
|
||
}
|
||
}
|
||
currentRow++
|
||
}
|
||
|
||
// 恢复文件位置
|
||
if _, err := handle.File.Seek(currentPos, 0); err != nil {
|
||
return nil, fmt.Errorf("恢复文件位置失败: %w", err)
|
||
}
|
||
|
||
if targetRow == nil {
|
||
return nil, fmt.Errorf("未找到行号 %d", rowNumber)
|
||
}
|
||
|
||
return targetRow, nil
|
||
}
|
||
|
||
// ========================== 文件合并功能 ==========================
|
||
|
||
// MergeCSVFiles 合并两个CSV文件
|
||
func (mgr *CSVManager) MergeCSVFiles(srcHandleID, dstHandleID int64, options MergeOptions) (int64, error) {
|
||
// 获取源文件句柄
|
||
srcHandle, err := mgr.getHandle(srcHandleID)
|
||
if err != nil {
|
||
return 0, fmt.Errorf("获取源文件句柄失败: %w", err)
|
||
}
|
||
defer mgr.releaseHandle(srcHandleID)
|
||
|
||
// 获取目标文件句柄
|
||
dstHandle, err := mgr.getHandle(dstHandleID)
|
||
if err != nil {
|
||
return 0, fmt.Errorf("获取目标文件句柄失败: %w", err)
|
||
}
|
||
defer mgr.releaseHandle(dstHandleID)
|
||
|
||
// 检查句柄状态
|
||
if !srcHandle.beginOperation() {
|
||
return 0, fmt.Errorf("源文件句柄已关闭或正在关闭: %d", srcHandleID)
|
||
}
|
||
defer srcHandle.endOperation()
|
||
|
||
if !dstHandle.beginOperation() {
|
||
return 0, fmt.Errorf("目标文件句柄已关闭或正在关闭: %d", dstHandleID)
|
||
}
|
||
defer dstHandle.endOperation()
|
||
|
||
srcHandle.mu.RLock()
|
||
dstHandle.mu.Lock()
|
||
defer func() {
|
||
srcHandle.mu.RUnlock()
|
||
dstHandle.mu.Unlock()
|
||
}()
|
||
|
||
if !srcHandle.IsOpen {
|
||
return 0, fmt.Errorf("源文件未打开")
|
||
}
|
||
if !dstHandle.IsOpen {
|
||
return 0, fmt.Errorf("目标文件未打开")
|
||
}
|
||
|
||
// 1. 检查表头兼容性
|
||
if err := mgr.checkHeaderCompatibility(srcHandle, dstHandle, options); err != nil {
|
||
return 0, fmt.Errorf("表头不兼容: %w", err)
|
||
}
|
||
|
||
// 2. 读取源文件的所有行
|
||
srcRows, err := mgr.readAllRows(srcHandle)
|
||
if err != nil {
|
||
return 0, fmt.Errorf("读取源文件失败: %w", err)
|
||
}
|
||
|
||
// 3. 合并数据
|
||
mergedRows, err := mgr.mergeDataRows(srcRows, srcHandle, dstHandle, options)
|
||
if err != nil {
|
||
return 0, fmt.Errorf("合并数据失败: %w", err)
|
||
}
|
||
|
||
// 4. 写入目标文件
|
||
if options.AppendMode {
|
||
// 追加模式:写入到文件末尾
|
||
if err := mgr.appendRowsToFile(dstHandle, mergedRows); err != nil {
|
||
return 0, fmt.Errorf("追加数据失败: %w", err)
|
||
}
|
||
} else {
|
||
// 覆盖模式:先读取目标文件的所有行,然后合并后写回
|
||
dstRows, err := mgr.readAllRows(dstHandle)
|
||
if err != nil {
|
||
return 0, fmt.Errorf("读取目标文件失败: %w", err)
|
||
}
|
||
|
||
// 如果是第一次合并且目标文件为空,可能需要保留表头
|
||
allRows := mgr.mergeAllRows(dstRows, mergedRows, dstHandle, srcHandle, options)
|
||
if err := mgr.writeAllRows(dstHandle, allRows); err != nil {
|
||
return 0, fmt.Errorf("写入合并数据失败: %w", err)
|
||
}
|
||
}
|
||
|
||
// 5. 更新目标文件的行数缓存
|
||
dstHandle.rowCountCached = false
|
||
newTotalRows, err := dstHandle.calculateTotalRows()
|
||
if err != nil {
|
||
return 0, fmt.Errorf("重新计算行数失败: %w", err)
|
||
}
|
||
|
||
return newTotalRows, nil
|
||
}
|
||
|
||
// 检查表头兼容性
|
||
func (mgr *CSVManager) checkHeaderCompatibility(srcHandle, dstHandle *CSVHandle, options MergeOptions) error {
|
||
// 如果两个文件都有表头
|
||
if srcHandle.HasHeader && dstHandle.HasHeader {
|
||
srcHeader := srcHandle.Header
|
||
dstHeader := dstHandle.Header
|
||
|
||
// 如果指定了列映射,跳过表头检查
|
||
if len(options.ColumnMapping) > 0 {
|
||
return nil
|
||
}
|
||
|
||
// 检查列数是否相同
|
||
if len(srcHeader) != len(dstHeader) && !options.SkipHeader {
|
||
return fmt.Errorf("源文件列数(%d)与目标文件列数(%d)不一致", len(srcHeader), len(dstHeader))
|
||
}
|
||
|
||
// 检查列名是否相同(可选)
|
||
for i := 0; i < min(len(srcHeader), len(dstHeader)); i++ {
|
||
if srcHeader[i] != dstHeader[i] {
|
||
fmt.Printf("警告: 第%d列列名不同: 源='%s', 目标='%s'\n", i+1, srcHeader[i], dstHeader[i])
|
||
}
|
||
}
|
||
} else if srcHandle.HasHeader != dstHandle.HasHeader {
|
||
// 一个有表头,一个没有表头
|
||
if !options.SkipHeader {
|
||
return fmt.Errorf("源文件与目标文件的表头设置不一致")
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// 合并数据行
|
||
func (mgr *CSVManager) mergeDataRows(srcRows [][]string, srcHandle, dstHandle *CSVHandle, options MergeOptions) ([][]string, error) {
|
||
var result [][]string
|
||
|
||
startIndex := 0
|
||
// 如果源文件有表头且需要跳过表头
|
||
if srcHandle.HasHeader && options.SkipHeader {
|
||
if len(srcRows) > 0 {
|
||
startIndex = 1
|
||
}
|
||
}
|
||
|
||
// 处理列映射
|
||
for i := startIndex; i < len(srcRows); i++ {
|
||
srcRow := srcRows[i]
|
||
transformedRow, err := mgr.transformRow(srcRow, srcHandle, dstHandle, options)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("转换第%d行数据失败: %w", i+1, err)
|
||
}
|
||
|
||
// 跳过空行
|
||
if transformedRow == nil {
|
||
continue
|
||
}
|
||
|
||
// 检查重复行
|
||
if options.SkipDuplicates && mgr.isDuplicateRow(result, transformedRow) {
|
||
continue
|
||
}
|
||
|
||
result = append(result, transformedRow)
|
||
}
|
||
|
||
return result, nil
|
||
}
|
||
|
||
// 转换行数据
|
||
func (mgr *CSVManager) transformRow(srcRow []string, srcHandle, dstHandle *CSVHandle, options MergeOptions) ([]string, error) {
|
||
// 如果有自定义转换函数,优先使用
|
||
if options.TransformFunc != nil {
|
||
return options.TransformFunc(srcRow)
|
||
}
|
||
|
||
// 如果有列映射,按照映射转换
|
||
if len(options.ColumnMapping) > 0 {
|
||
// 确定目标行的最大列数
|
||
maxCol := 0
|
||
for _, dstIndex := range options.ColumnMapping {
|
||
if dstIndex > maxCol {
|
||
maxCol = dstIndex
|
||
}
|
||
}
|
||
|
||
// 创建目标行
|
||
dstRow := make([]string, maxCol+1)
|
||
for srcIndex, dstIndex := range options.ColumnMapping {
|
||
if srcIndex < len(srcRow) && dstIndex >= 0 && dstIndex < len(dstRow) {
|
||
dstRow[dstIndex] = srcRow[srcIndex]
|
||
}
|
||
}
|
||
return dstRow, nil
|
||
}
|
||
|
||
// 如果没有列映射,直接复制
|
||
dstRow := make([]string, len(srcRow))
|
||
copy(dstRow, srcRow)
|
||
|
||
// 如果目标文件有表头且列数不同,进行调整
|
||
if dstHandle.HasHeader && len(dstRow) != len(dstHandle.Header) {
|
||
if len(dstRow) < len(dstHandle.Header) {
|
||
// 源列数少,用空字符串填充
|
||
for i := len(dstRow); i < len(dstHandle.Header); i++ {
|
||
dstRow = append(dstRow, "")
|
||
}
|
||
} else {
|
||
// 源列数多,截断
|
||
dstRow = dstRow[:len(dstHandle.Header)]
|
||
}
|
||
}
|
||
|
||
return dstRow, nil
|
||
}
|
||
|
||
// 检查是否为重复行
|
||
func (mgr *CSVManager) isDuplicateRow(rows [][]string, newRow []string) bool {
|
||
for _, row := range rows {
|
||
if mgr.compareRows(row, newRow) {
|
||
return true
|
||
}
|
||
}
|
||
return false
|
||
}
|
||
|
||
// 比较两行是否相同
|
||
func (mgr *CSVManager) compareRows(row1, row2 []string) bool {
|
||
if len(row1) != len(row2) {
|
||
return false
|
||
}
|
||
for i := range row1 {
|
||
if row1[i] != row2[i] {
|
||
return false
|
||
}
|
||
}
|
||
return true
|
||
}
|
||
|
||
// 追加行到文件末尾
|
||
func (mgr *CSVManager) appendRowsToFile(handle *CSVHandle, rows [][]string) error {
|
||
// 移动到文件末尾
|
||
if _, err := handle.File.Seek(0, 2); err != nil {
|
||
return fmt.Errorf("移动到文件末尾失败: %w", err)
|
||
}
|
||
|
||
// 批量写入行数据
|
||
for _, row := range rows {
|
||
if err := handle.CSVWriter.Write(row); err != nil {
|
||
return fmt.Errorf("写入行失败: %w", err)
|
||
}
|
||
}
|
||
|
||
// 刷新缓冲区
|
||
handle.CSVWriter.Flush()
|
||
if handle.writeBuffer != nil {
|
||
if err := handle.writeBuffer.Flush(); err != nil {
|
||
return fmt.Errorf("刷新写入缓冲区失败: %w", err)
|
||
}
|
||
}
|
||
|
||
// 更新行数统计
|
||
handle.TotalRows += int64(len(rows))
|
||
handle.cachedRowCount = handle.TotalRows
|
||
handle.rowCountCached = true
|
||
|
||
return nil
|
||
}
|
||
|
||
// 合并所有行(用于覆盖模式)
|
||
func (mgr *CSVManager) mergeAllRows(dstRows, srcRows [][]string, dstHandle, srcHandle *CSVHandle, options MergeOptions) [][]string {
|
||
var result [][]string
|
||
|
||
// 处理表头
|
||
if dstHandle.HasHeader && len(dstRows) > 0 {
|
||
// 保留目标文件的表头
|
||
result = append(result, dstRows[0])
|
||
dstRows = dstRows[1:]
|
||
}
|
||
|
||
// 根据冲突解决策略合并数据
|
||
switch options.ConflictResolution {
|
||
case Overwrite:
|
||
// 覆盖模式:源数据完全替换目标数据
|
||
result = append(result, srcRows...)
|
||
case Skip:
|
||
// 跳过模式:只保留目标数据中不冲突的行
|
||
result = append(result, dstRows...)
|
||
for _, srcRow := range srcRows {
|
||
if !mgr.isDuplicateRow(result, srcRow) {
|
||
result = append(result, srcRow)
|
||
}
|
||
}
|
||
case KeepBoth:
|
||
// 保留两者:先写目标数据,再写源数据
|
||
result = append(result, dstRows...)
|
||
result = append(result, srcRows...)
|
||
case UseSource:
|
||
// 使用源数据:源数据优先级更高
|
||
result = append(result, srcRows...)
|
||
case UseTarget:
|
||
// 使用目标数据:目标数据优先级更高
|
||
result = append(result, dstRows...)
|
||
default:
|
||
// 默认:追加模式
|
||
result = append(result, dstRows...)
|
||
result = append(result, srcRows...)
|
||
}
|
||
|
||
return result
|
||
}
|
||
|
||
// MergeCSVFilesSimple 简单合并CSV文件(默认选项)
|
||
func (mgr *CSVManager) MergeCSVFilesSimple(srcHandleID, dstHandleID int64, appendMode bool) (int64, error) {
|
||
options := MergeOptions{
|
||
AppendMode: appendMode,
|
||
SkipHeader: true, // 跳过源文件表头
|
||
SkipDuplicates: false, // 不跳过重复行
|
||
ConflictResolution: KeepBoth, // 保留两者
|
||
}
|
||
return mgr.MergeCSVFiles(srcHandleID, dstHandleID, options)
|
||
}
|
||
|
||
// ========================== C 导出函数 ==========================
|
||
|
||
// OpenCSVFile 打开CSV文件
|
||
//
|
||
//export OpenCSVFile
|
||
func OpenCSVFile(filename *C.char, delimiter C.char, hasHeader C.int) *C.char {
|
||
filenameStr := C.GoString(filename)
|
||
delimiterStr := rune(delimiter)
|
||
handleID, err := GetManager().OpenCSVFile(filenameStr, delimiterStr, hasHeader != 0)
|
||
response := struct {
|
||
HandleID int64 `json:"handleID"`
|
||
}{
|
||
HandleID: handleID,
|
||
}
|
||
var csvResponse CSVResponse
|
||
if err != nil {
|
||
csvResponse = CSVResponse{
|
||
Success: false,
|
||
Message: err.Error(),
|
||
}
|
||
} else {
|
||
csvResponse = CSVResponse{
|
||
Success: true,
|
||
Data: response,
|
||
}
|
||
}
|
||
csvResponseStr, _ := json.Marshal(csvResponse)
|
||
return C.CString(string(csvResponseStr))
|
||
}
|
||
|
||
// WriteHeader 写入表头
|
||
//
|
||
//export WriteHeader
|
||
func WriteHeader(handleID C.int, header *C.char) *C.char {
|
||
var csvResponse CSVResponse
|
||
goHandleID := int64(handleID)
|
||
goHeader := C.GoString(header)
|
||
var data []string
|
||
err := json.Unmarshal([]byte(goHeader), &data)
|
||
if err != nil {
|
||
csvResponse = CSVResponse{
|
||
Success: false,
|
||
Message: fmt.Sprintf("header JSON解析失败: %v", err),
|
||
}
|
||
csvResponseStr, _ := json.Marshal(csvResponse)
|
||
return C.CString(string(csvResponseStr))
|
||
}
|
||
errs := GetManager().WriteHeader(goHandleID, data)
|
||
if errs != nil {
|
||
csvResponse = CSVResponse{
|
||
Success: false,
|
||
Message: errs.Error(),
|
||
}
|
||
} else {
|
||
csvResponse = CSVResponse{
|
||
Success: true,
|
||
}
|
||
}
|
||
csvResponseStr, _ := json.Marshal(csvResponse)
|
||
return C.CString(string(csvResponseStr))
|
||
}
|
||
|
||
// WriteRows 写入/覆盖行数据
|
||
//
|
||
//export WriteRows
|
||
func WriteRows(handleID C.int, rowsData *C.char) *C.char {
|
||
var csvResponse CSVResponse
|
||
goHandleID := int64(handleID)
|
||
goRowsData := C.GoString(rowsData)
|
||
// json解析
|
||
var data [][]string
|
||
err := json.Unmarshal([]byte(goRowsData), &data)
|
||
if err != nil {
|
||
csvResponse = CSVResponse{
|
||
Success: false,
|
||
Message: fmt.Sprintf("rowsData JSON解析失败: %v", err),
|
||
}
|
||
csvResponseStr, _ := json.Marshal(csvResponse)
|
||
return C.CString(string(csvResponseStr))
|
||
}
|
||
// 写入数据
|
||
rows, err := GetManager().AppendRows(goHandleID, data)
|
||
response := struct {
|
||
TotalRows int64 `json:"totalRows"`
|
||
}{
|
||
TotalRows: rows,
|
||
}
|
||
if err != nil {
|
||
csvResponse = CSVResponse{
|
||
Success: false,
|
||
Message: err.Error(),
|
||
}
|
||
} else {
|
||
csvResponse = CSVResponse{
|
||
Success: true,
|
||
Data: response,
|
||
}
|
||
}
|
||
csvResponseStr, _ := json.Marshal(csvResponse)
|
||
return C.CString(string(csvResponseStr))
|
||
}
|
||
|
||
// UpdateRow C导出函数 - 修改指定行数据
|
||
//
|
||
//export UpdateRow
|
||
func UpdateRow(handleID C.int, rowNumber C.int, rowData *C.char) *C.char {
|
||
var csvResponse CSVResponse
|
||
goHandleID := int64(handleID)
|
||
goRowNumber := int64(rowNumber)
|
||
goRowData := C.GoString(rowData)
|
||
|
||
// JSON解析行数据
|
||
var data []string
|
||
err := json.Unmarshal([]byte(goRowData), &data)
|
||
if err != nil {
|
||
csvResponse = CSVResponse{
|
||
Success: false,
|
||
Message: fmt.Sprintf("rowsData JSON解析失败: %v", err),
|
||
}
|
||
csvResponseStr, _ := json.Marshal(csvResponse)
|
||
return C.CString(string(csvResponseStr))
|
||
}
|
||
// 修改行数据
|
||
err = GetManager().UpdateRow(goHandleID, goRowNumber, data)
|
||
if err != nil {
|
||
csvResponse = CSVResponse{
|
||
Success: false,
|
||
Message: err.Error(),
|
||
}
|
||
} else {
|
||
csvResponse = CSVResponse{
|
||
Success: true,
|
||
Message: fmt.Sprintf("成功修改第%d行数据", goRowNumber),
|
||
}
|
||
}
|
||
csvResponseStr, _ := json.Marshal(csvResponse)
|
||
return C.CString(string(csvResponseStr))
|
||
}
|
||
|
||
// GetRow C导出函数 - 获取指定行数据
|
||
//
|
||
//export GetRow
|
||
func GetRow(handleID C.int, rowNumber C.int) *C.char {
|
||
goHandleID := int64(handleID)
|
||
goRowNumber := int64(rowNumber)
|
||
|
||
// 获取行数据
|
||
rowData, err := GetManager().GetRow(goHandleID, goRowNumber)
|
||
var csvResponse CSVResponse
|
||
if err != nil {
|
||
csvResponse = CSVResponse{
|
||
Success: false,
|
||
Message: err.Error(),
|
||
}
|
||
} else {
|
||
csvResponse = CSVResponse{
|
||
Success: true,
|
||
Data: rowData,
|
||
}
|
||
}
|
||
|
||
csvResponseStr, _ := json.Marshal(csvResponse)
|
||
return C.CString(string(csvResponseStr))
|
||
}
|
||
|
||
// MergeCSVFilesSimple 合并两个csv文件
|
||
//
|
||
//export MergeCSVFilesSimple
|
||
func MergeCSVFilesSimple(srcHandleID, dstHandleID, appendMode C.int) *C.char {
|
||
var csvResponse CSVResponse
|
||
goSrcHandleID := int64(srcHandleID)
|
||
goDstHandleID := int64(dstHandleID)
|
||
goAppendMode := int(appendMode)
|
||
rows, err := GetManager().MergeCSVFilesSimple(goSrcHandleID, goDstHandleID, goAppendMode == 0)
|
||
response := struct {
|
||
TotalRows int64 `json:"totalRows"`
|
||
}{
|
||
TotalRows: rows,
|
||
}
|
||
if err != nil {
|
||
csvResponse = CSVResponse{
|
||
Success: false,
|
||
Message: err.Error(),
|
||
}
|
||
} else {
|
||
csvResponse = CSVResponse{
|
||
Success: true,
|
||
Data: response,
|
||
}
|
||
}
|
||
csvResponseStr, _ := json.Marshal(csvResponse)
|
||
return C.CString(string(csvResponseStr))
|
||
}
|
||
|
||
// CloseHandles 关闭指定句柄(使用优雅关闭)
|
||
//
|
||
//export CloseHandles
|
||
func CloseHandles(handleID C.int) *C.char {
|
||
goHandleID := int64(handleID)
|
||
|
||
// 使用优雅关闭
|
||
err := GetManager().CloseHandle(goHandleID)
|
||
|
||
var csvResponse CSVResponse
|
||
if err != nil {
|
||
csvResponse = CSVResponse{
|
||
Success: false,
|
||
Message: err.Error(),
|
||
}
|
||
} else {
|
||
csvResponse = CSVResponse{
|
||
Success: true,
|
||
Message: fmt.Sprintf("成功关闭句柄 %d", goHandleID),
|
||
}
|
||
}
|
||
|
||
csvResponseStr, _ := json.Marshal(csvResponse)
|
||
return C.CString(string(csvResponseStr))
|
||
}
|
||
|
||
// CloseAllHandles 关闭所有句柄
|
||
//
|
||
//export CloseAllHandles
|
||
func CloseAllHandles() *C.char {
|
||
GetManager().closeAllHandles()
|
||
|
||
csvResponse := CSVResponse{
|
||
Success: true,
|
||
Message: "成功关闭所有句柄",
|
||
}
|
||
|
||
csvResponseStr, _ := json.Marshal(csvResponse)
|
||
return C.CString(string(csvResponseStr))
|
||
}
|
||
|
||
// FreeCString 释放C字符串内存
|
||
//
|
||
//export FreeCString
|
||
func FreeCString(str *C.char) {
|
||
C.free(unsafe.Pointer(str))
|
||
}
|
||
|
||
// 主函数
|
||
func main() {
|
||
}
|