1452 lines
35 KiB
Go
1452 lines
35 KiB
Go
package main
|
||
|
||
/*
|
||
#include <stdlib.h>
|
||
*/
|
||
import "C"
|
||
import (
|
||
"encoding/csv"
|
||
"encoding/json"
|
||
"fmt"
|
||
"github.com/xuri/excelize/v2"
|
||
"os"
|
||
"path/filepath"
|
||
"sort"
|
||
"strings"
|
||
"sync"
|
||
"unsafe"
|
||
)
|
||
|
||
// ExcelManager 线程安全的Excel文件管理器
|
||
type ExcelManager struct {
|
||
mu sync.RWMutex
|
||
fileMap map[string]*excelize.File // 存储打开的文件
|
||
}
|
||
|
||
// MergeConfig 合并配置
|
||
type MergeConfig struct {
|
||
SourceDir string // 源目录
|
||
OutputFile string // 输出文件
|
||
SheetName string // 目标sheet名称
|
||
MergeByColumn bool // 是否按列合并(默认按行)
|
||
IncludeHeaders bool // 是否包含表头(仅第一个文件)
|
||
SkipEmptyRows bool // 是否跳过空行
|
||
FilePattern string // 文件匹配模式,如 "*.xlsx"
|
||
SpecificFiles []string // 指定要合并的文件列表
|
||
SourceSheet string // 源sheet名称(为空则使用第一个sheet)
|
||
AddSourceColumn bool // 是否添加源文件列
|
||
AddIndexColumn bool // 是否添加序号列
|
||
}
|
||
|
||
// NewExcelManager 创建新的Excel管理器
|
||
func NewExcelManager() *ExcelManager {
|
||
return &ExcelManager{
|
||
fileMap: make(map[string]*excelize.File),
|
||
}
|
||
}
|
||
|
||
// ============ 基本Excel操作功能 ============
|
||
|
||
// ReadData 读取Excel数据
|
||
func (em *ExcelManager) ReadData(filename, sheet string) ([][]string, error) {
|
||
em.mu.RLock()
|
||
defer em.mu.RUnlock()
|
||
fmt.Printf("[DEBUG] ReadData开始: file=%s, sheet=%s\n", filename, sheet)
|
||
file, err := excelize.OpenFile(filename)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
defer file.Close()
|
||
rows, err := file.GetRows(sheet)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("读取sheet %s 失败: %v", sheet, err)
|
||
}
|
||
fmt.Printf("[DEBUG] 读取的数据:", rows)
|
||
return rows, nil
|
||
}
|
||
|
||
// WriteData 写入数据到指定位置
|
||
func (em *ExcelManager) WriteData(filename, sheet string, data map[string]interface{}) error {
|
||
em.mu.Lock()
|
||
defer em.mu.Unlock()
|
||
|
||
file, err := excelize.OpenFile(filename)
|
||
if err != nil {
|
||
// 文件不存在,创建新文件
|
||
file = excelize.NewFile()
|
||
file.NewSheet(sheet)
|
||
} else {
|
||
// 确保sheet存在
|
||
index, _ := file.GetSheetIndex(sheet)
|
||
if index == -1 {
|
||
file.NewSheet(sheet)
|
||
}
|
||
}
|
||
|
||
// 写入数据
|
||
for cell, value := range data {
|
||
file.SetCellValue(sheet, cell, value)
|
||
}
|
||
|
||
// 保存文件
|
||
return file.SaveAs(filename)
|
||
}
|
||
|
||
// AppendData 追加数据到末尾
|
||
func (em *ExcelManager) AppendData(filename, sheet string, rowData []interface{}) error {
|
||
em.mu.Lock()
|
||
defer em.mu.Unlock()
|
||
|
||
file, err := excelize.OpenFile(filename)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
defer func() {
|
||
file.SaveAs(filename)
|
||
file.Close()
|
||
}()
|
||
|
||
// 确保sheet存在
|
||
index, _ := file.GetSheetIndex(sheet)
|
||
if index == -1 {
|
||
file.NewSheet(sheet)
|
||
}
|
||
|
||
// 获取当前最大行号
|
||
rows, err := file.GetRows(sheet)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
nextRow := len(rows) + 1
|
||
if len(rows) == 0 {
|
||
nextRow = 1
|
||
}
|
||
|
||
// 写入新行
|
||
for col, value := range rowData {
|
||
cell, _ := excelize.CoordinatesToCellName(col+1, nextRow)
|
||
file.SetCellValue(sheet, cell, value)
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// SearchByKeyword 搜索包含关键字的单元格
|
||
func (em *ExcelManager) SearchByKeyword(filename, sheet, keyword string) ([]string, error) {
|
||
em.mu.RLock()
|
||
defer em.mu.RUnlock()
|
||
|
||
file, err := excelize.OpenFile(filename)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
defer file.Close()
|
||
|
||
var results []string
|
||
rows, err := file.GetRows(sheet)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
for rowIndex, row := range rows {
|
||
for colIndex, cell := range row {
|
||
if strings.Contains(cell, keyword) {
|
||
cellName, _ := excelize.CoordinatesToCellName(colIndex+1, rowIndex+1)
|
||
results = append(results,
|
||
fmt.Sprintf("位置: %s, 值: %s", cellName, cell))
|
||
}
|
||
}
|
||
}
|
||
|
||
return results, nil
|
||
}
|
||
|
||
// SearchRowData 搜索整行包含关键字的行
|
||
func (em *ExcelManager) SearchRowData(filename, sheet, keyword string) ([][]string, error) {
|
||
em.mu.RLock()
|
||
defer em.mu.RUnlock()
|
||
|
||
file, err := excelize.OpenFile(filename)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
defer file.Close()
|
||
|
||
var results [][]string
|
||
rows, err := file.GetRows(sheet)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
for _, row := range rows {
|
||
for _, cell := range row {
|
||
if strings.Contains(cell, keyword) {
|
||
results = append(results, row)
|
||
break
|
||
}
|
||
}
|
||
}
|
||
|
||
return results, nil
|
||
}
|
||
|
||
// CreateAndWrite 创建新文件并写入数据
|
||
func (em *ExcelManager) CreateAndWrite(filename, sheet string, data [][]string) error {
|
||
em.mu.Lock()
|
||
defer em.mu.Unlock()
|
||
|
||
// 创建新文件
|
||
file := excelize.NewFile()
|
||
defer file.Close()
|
||
|
||
// 如果指定sheet不是"Sheet1",则重命名默认sheet
|
||
if sheet != "Sheet1" {
|
||
file.SetSheetName("Sheet1", sheet)
|
||
}
|
||
|
||
// 写入数据
|
||
for rowIndex, row := range data {
|
||
for colIndex, value := range row {
|
||
cell, _ := excelize.CoordinatesToCellName(colIndex+1, rowIndex+1)
|
||
file.SetCellValue(sheet, cell, value)
|
||
}
|
||
}
|
||
|
||
// 保存文件
|
||
return file.SaveAs(filename)
|
||
}
|
||
|
||
// CloseAll 关闭所有文件
|
||
func (em *ExcelManager) CloseAll() error {
|
||
em.mu.Lock()
|
||
defer em.mu.Unlock()
|
||
|
||
var lastErr error
|
||
for filename, file := range em.fileMap {
|
||
if err := file.Close(); err != nil {
|
||
lastErr = err
|
||
}
|
||
delete(em.fileMap, filename)
|
||
}
|
||
return lastErr
|
||
}
|
||
|
||
// ============ Excel合并功能 ============
|
||
|
||
// MergeExcelFiles 合并多个Excel文件(批次合并)
|
||
func (em *ExcelManager) MergeExcelFiles(config MergeConfig) error {
|
||
em.mu.Lock()
|
||
defer em.mu.Unlock()
|
||
|
||
// 获取要合并的文件列表
|
||
filesToMerge, err := em.getFilesToMerge(config)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
fmt.Printf("开始合并 %d 个文件:\n", len(filesToMerge))
|
||
for i, file := range filesToMerge {
|
||
fmt.Printf(" %d. %s\n", i+1, filepath.Base(file))
|
||
}
|
||
|
||
// 创建新的输出文件
|
||
outputFile := excelize.NewFile()
|
||
defer outputFile.Close()
|
||
|
||
// 删除默认的Sheet1
|
||
outputFile.DeleteSheet("Sheet1")
|
||
|
||
// 创建目标sheet并设置为活动sheet
|
||
sheetIndex, err := outputFile.NewSheet(config.SheetName)
|
||
if err != nil {
|
||
return fmt.Errorf("创建sheet失败: %v", err)
|
||
}
|
||
outputFile.SetActiveSheet(sheetIndex)
|
||
|
||
currentRow := 1
|
||
totalRowsMerged := 0
|
||
fileCount := len(filesToMerge)
|
||
|
||
// 写入表头(如果需要)
|
||
if config.IncludeHeaders && fileCount > 0 {
|
||
headers, err := em.readFileHeaders(filesToMerge[0], config.SourceSheet)
|
||
if err != nil {
|
||
fmt.Printf("警告: 无法读取第一个文件的表头: %v\n", err)
|
||
} else {
|
||
// 添加额外的列(如果需要)
|
||
colOffset := 0
|
||
if config.AddIndexColumn {
|
||
headers = append([]string{"序号"}, headers...)
|
||
colOffset++
|
||
}
|
||
if config.AddSourceColumn {
|
||
headers = append([]string{"源文件"}, headers...)
|
||
colOffset++
|
||
}
|
||
|
||
// 写入表头
|
||
for colIndex, header := range headers {
|
||
cellName, _ := excelize.CoordinatesToCellName(colIndex+1, currentRow)
|
||
outputFile.SetCellValue(config.SheetName, cellName, header)
|
||
}
|
||
currentRow++
|
||
fmt.Printf("写入表头: %v\n", headers)
|
||
}
|
||
}
|
||
|
||
// 合并所有文件
|
||
for fileIndex, sourceFile := range filesToMerge {
|
||
rowsMerged, err := em.mergeSingleFile(outputFile, config, sourceFile, fileIndex, fileCount, ¤tRow)
|
||
if err != nil {
|
||
fmt.Printf("警告: 合并文件 %s 时出错: %v,跳过\n", sourceFile, err)
|
||
continue
|
||
}
|
||
totalRowsMerged += rowsMerged
|
||
}
|
||
|
||
// 保存输出文件
|
||
if err := outputFile.SaveAs(config.OutputFile); err != nil {
|
||
return fmt.Errorf("保存合并文件失败: %v", err)
|
||
}
|
||
|
||
fmt.Printf("合并完成!结果保存在: %s\n", config.OutputFile)
|
||
fmt.Printf("统计信息:\n")
|
||
fmt.Printf(" - 合并文件数: %d\n", fileCount)
|
||
fmt.Printf(" - 合并数据行数: %d\n", totalRowsMerged)
|
||
fmt.Printf(" - 输出文件总行数: %d\n", currentRow-1)
|
||
|
||
// 验证文件是否正确保存
|
||
if err := em.verifyMergeResult(config.OutputFile, config.SheetName); err != nil {
|
||
return fmt.Errorf("合并结果验证失败: %v", err)
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// getFilesToMerge 获取要合并的文件列表
|
||
func (em *ExcelManager) getFilesToMerge(config MergeConfig) ([]string, error) {
|
||
var filesToMerge []string
|
||
|
||
if len(config.SpecificFiles) > 0 {
|
||
filesToMerge = config.SpecificFiles
|
||
} else if config.SourceDir != "" {
|
||
pattern := "*.xlsx"
|
||
if config.FilePattern != "" {
|
||
pattern = config.FilePattern
|
||
}
|
||
|
||
files, err := filepath.Glob(filepath.Join(config.SourceDir, pattern))
|
||
if err != nil {
|
||
return nil, fmt.Errorf("查找文件失败: %v", err)
|
||
}
|
||
filesToMerge = files
|
||
} else {
|
||
return nil, fmt.Errorf("必须指定SourceDir或SpecificFiles")
|
||
}
|
||
|
||
if len(filesToMerge) == 0 {
|
||
return nil, fmt.Errorf("没有找到要合并的Excel文件")
|
||
}
|
||
|
||
// 按文件名排序
|
||
sort.Strings(filesToMerge)
|
||
return filesToMerge, nil
|
||
}
|
||
|
||
// readFileHeaders 读取文件的表头
|
||
func (em *ExcelManager) readFileHeaders(filename, sheetName string) ([]string, error) {
|
||
srcFile, err := excelize.OpenFile(filename)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
defer srcFile.Close()
|
||
|
||
sourceSheet := em.getSourceSheet(srcFile, sheetName)
|
||
if sourceSheet == "" {
|
||
return nil, fmt.Errorf("未找到可用的sheet")
|
||
}
|
||
|
||
rows, err := srcFile.GetRows(sourceSheet)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
if len(rows) == 0 {
|
||
return nil, fmt.Errorf("文件没有数据")
|
||
}
|
||
|
||
return rows[0], nil
|
||
}
|
||
|
||
// mergeSingleFile 合并单个文件
|
||
func (em *ExcelManager) mergeSingleFile(outputFile *excelize.File, config MergeConfig,
|
||
sourceFile string, fileIndex, fileCount int, currentRow *int) (int, error) {
|
||
|
||
fmt.Printf("\n[%d/%d] 正在处理文件: %s\n", fileIndex+1, fileCount, filepath.Base(sourceFile))
|
||
|
||
// 打开源文件
|
||
srcFile, err := excelize.OpenFile(sourceFile)
|
||
if err != nil {
|
||
return 0, fmt.Errorf("打开文件失败: %v", err)
|
||
}
|
||
defer srcFile.Close()
|
||
|
||
// 确定要读取的sheet
|
||
sourceSheet := em.getSourceSheet(srcFile, config.SourceSheet)
|
||
if sourceSheet == "" {
|
||
return 0, fmt.Errorf("未找到可用的sheet")
|
||
}
|
||
|
||
// 读取源文件数据
|
||
rows, err := srcFile.GetRows(sourceSheet)
|
||
if err != nil {
|
||
return 0, fmt.Errorf("读取数据失败: %v", err)
|
||
}
|
||
|
||
if len(rows) == 0 {
|
||
fmt.Printf(" 警告: 文件没有数据,跳过\n")
|
||
return 0, nil
|
||
}
|
||
|
||
fmt.Printf(" 读取到 %d 行数据\n", len(rows))
|
||
|
||
rowsMerged := 0
|
||
|
||
// 处理数据行
|
||
for rowIndex, row := range rows {
|
||
// 跳过空行
|
||
if config.SkipEmptyRows && em.isRowEmpty(row) {
|
||
continue
|
||
}
|
||
fmt.Println(rowIndex)
|
||
// 处理表头(如果是第一个文件且包含表头,第一行已经处理过了)
|
||
if config.IncludeHeaders && rowIndex == 0 {
|
||
if fileIndex == 0 {
|
||
// 第一个文件的表头已处理,跳过
|
||
continue
|
||
} else {
|
||
// 后续文件的表头跳过
|
||
continue
|
||
}
|
||
}
|
||
|
||
// 写入数据行
|
||
colOffset := 0
|
||
|
||
// 添加序号列
|
||
if config.AddIndexColumn {
|
||
cellName, _ := excelize.CoordinatesToCellName(1, *currentRow)
|
||
outputFile.SetCellValue(config.SheetName, cellName, rowsMerged+1)
|
||
colOffset++
|
||
}
|
||
|
||
// 添加源文件列
|
||
if config.AddSourceColumn {
|
||
cellName, _ := excelize.CoordinatesToCellName(1+colOffset, *currentRow)
|
||
outputFile.SetCellValue(config.SheetName, cellName, filepath.Base(sourceFile))
|
||
colOffset++
|
||
}
|
||
|
||
// 写入原始数据
|
||
for colIndex, cell := range row {
|
||
cellName, _ := excelize.CoordinatesToCellName(colIndex+1+colOffset, *currentRow)
|
||
outputFile.SetCellValue(config.SheetName, cellName, cell)
|
||
}
|
||
|
||
rowsMerged++
|
||
*currentRow++
|
||
}
|
||
|
||
//// 添加文件分隔信息(如果有数据被合并)
|
||
//if rowsMerged > 0 {
|
||
// // 添加分隔行
|
||
// separatorCell, _ := excelize.CoordinatesToCellName(1, *currentRow)
|
||
// outputFile.SetCellValue(config.SheetName, separatorCell,
|
||
// fmt.Sprintf("=== 文件 %d/%d: %s (合并了 %d 行) ===",
|
||
// fileIndex+1, fileCount, filepath.Base(sourceFile), rowsMerged))
|
||
// *currentRow++
|
||
//}
|
||
|
||
fmt.Printf(" 文件处理完成,合并了 %d 行数据\n", rowsMerged)
|
||
return rowsMerged, nil
|
||
}
|
||
|
||
// getSourceSheet 获取源sheet名称
|
||
func (em *ExcelManager) getSourceSheet(file *excelize.File, preferredSheet string) string {
|
||
if preferredSheet != "" {
|
||
if index, _ := file.GetSheetIndex(preferredSheet); index != -1 {
|
||
return preferredSheet
|
||
}
|
||
}
|
||
|
||
// 返回第一个sheet
|
||
sheets := file.GetSheetList()
|
||
if len(sheets) > 0 {
|
||
return sheets[0]
|
||
}
|
||
return ""
|
||
}
|
||
|
||
// verifyMergeResult 验证合并结果
|
||
func (em *ExcelManager) verifyMergeResult(filename, sheet string) error {
|
||
file, err := excelize.OpenFile(filename)
|
||
if err != nil {
|
||
return fmt.Errorf("打开验证文件失败: %v", err)
|
||
}
|
||
defer file.Close()
|
||
|
||
rows, err := file.GetRows(sheet)
|
||
if err != nil {
|
||
return fmt.Errorf("读取sheet失败: %v", err)
|
||
}
|
||
|
||
fmt.Printf("\n验证结果:\n")
|
||
fmt.Printf(" 文件: %s\n", filename)
|
||
fmt.Printf(" Sheet: %s\n", sheet)
|
||
fmt.Printf(" 总行数: %d\n", len(rows))
|
||
|
||
if len(rows) == 0 {
|
||
return fmt.Errorf("警告: 合并后的文件没有数据!")
|
||
}
|
||
|
||
// 显示前5行数据
|
||
limit := 5
|
||
if len(rows) < limit {
|
||
limit = len(rows)
|
||
}
|
||
|
||
fmt.Printf(" 前%d行数据预览:\n", limit)
|
||
for i := 0; i < limit; i++ {
|
||
// 只显示前5列(避免输出过长)
|
||
previewCols := 5
|
||
if len(rows[i]) < previewCols {
|
||
previewCols = len(rows[i])
|
||
}
|
||
fmt.Printf(" 第%d行: %v", i+1, rows[i][:previewCols])
|
||
if len(rows[i]) > previewCols {
|
||
fmt.Printf(" ... (+%d列)", len(rows[i])-previewCols)
|
||
}
|
||
fmt.Println()
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// MergeExcelFilesParallel 并行合并Excel文件(高性能)
|
||
func (em *ExcelManager) MergeExcelFilesParallel(config MergeConfig, workers int) error {
|
||
em.mu.Lock()
|
||
defer em.mu.Unlock()
|
||
|
||
// 获取要合并的文件列表
|
||
filesToMerge, err := em.getFilesToMerge(config)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
fmt.Printf("开始并行合并 %d 个文件,使用 %d 个worker:\n", len(filesToMerge), workers)
|
||
|
||
// 创建通道和等待组
|
||
fileChan := make(chan fileTask, len(filesToMerge))
|
||
resultChan := make(chan fileResult, len(filesToMerge))
|
||
var wg sync.WaitGroup
|
||
|
||
// 启动worker goroutines
|
||
for i := 0; i < workers; i++ {
|
||
wg.Add(1)
|
||
go func(workerID int) {
|
||
defer wg.Done()
|
||
for task := range fileChan {
|
||
fmt.Printf("Worker %d 正在处理: %s (文件 %d/%d)\n",
|
||
workerID, filepath.Base(task.filename), task.index+1, task.total)
|
||
data := em.readExcelFileParallel(task.filename, config.SourceSheet)
|
||
resultChan <- fileResult{
|
||
filename: task.filename,
|
||
index: task.index,
|
||
data: data,
|
||
}
|
||
}
|
||
}(i)
|
||
}
|
||
|
||
// 发送文件任务到通道
|
||
for i, file := range filesToMerge {
|
||
fileChan <- fileTask{
|
||
filename: file,
|
||
index: i,
|
||
total: len(filesToMerge),
|
||
}
|
||
}
|
||
close(fileChan)
|
||
|
||
// 等待所有worker完成
|
||
go func() {
|
||
wg.Wait()
|
||
close(resultChan)
|
||
}()
|
||
|
||
// 收集结果
|
||
var results []fileResult
|
||
for result := range resultChan {
|
||
results = append(results, result)
|
||
}
|
||
|
||
// 按文件索引排序
|
||
sort.Slice(results, func(i, j int) bool {
|
||
return results[i].index < results[j].index
|
||
})
|
||
|
||
// 创建新的输出文件
|
||
outputFile := excelize.NewFile()
|
||
defer outputFile.Close()
|
||
|
||
// 删除默认的Sheet1
|
||
outputFile.DeleteSheet("Sheet1")
|
||
|
||
// 创建目标sheet
|
||
sheetIndex, err := outputFile.NewSheet(config.SheetName)
|
||
if err != nil {
|
||
return fmt.Errorf("创建sheet失败: %v", err)
|
||
}
|
||
outputFile.SetActiveSheet(sheetIndex)
|
||
|
||
// 合并数据
|
||
currentRow := 1
|
||
totalRowsMerged := 0
|
||
|
||
// 写入表头(如果需要)
|
||
headersWritten := false
|
||
if config.IncludeHeaders && len(results) > 0 {
|
||
for _, result := range results {
|
||
if result.data.err == nil && len(result.data.rows) > 0 {
|
||
headers := result.data.rows[0]
|
||
// 添加额外的列
|
||
colOffset := 0
|
||
if config.AddIndexColumn {
|
||
headers = append([]string{"序号"}, headers...)
|
||
colOffset++
|
||
}
|
||
if config.AddSourceColumn {
|
||
headers = append([]string{"源文件"}, headers...)
|
||
colOffset++
|
||
}
|
||
|
||
// 写入表头
|
||
for colIndex, header := range headers {
|
||
cellName, _ := excelize.CoordinatesToCellName(colIndex+1, currentRow)
|
||
outputFile.SetCellValue(config.SheetName, cellName, header)
|
||
}
|
||
currentRow++
|
||
headersWritten = true
|
||
fmt.Printf("写入表头: %v\n", headers)
|
||
break // 只取第一个有效文件的表头
|
||
}
|
||
}
|
||
}
|
||
|
||
// 写入数据
|
||
for _, result := range results {
|
||
if result.data.err != nil {
|
||
fmt.Printf("警告: 文件 %s 处理失败: %v,跳过\n", result.filename, result.data.err)
|
||
continue
|
||
}
|
||
|
||
if len(result.data.rows) == 0 {
|
||
fmt.Printf("警告: 文件 %s 没有数据,跳过\n", result.filename)
|
||
continue
|
||
}
|
||
|
||
rowsMerged := 0
|
||
for rowIndex, row := range result.data.rows {
|
||
// 跳过表头(如果已经处理过)
|
||
if config.IncludeHeaders && rowIndex == 0 && headersWritten {
|
||
continue
|
||
}
|
||
|
||
// 跳过空行
|
||
if config.SkipEmptyRows && em.isRowEmpty(row) {
|
||
continue
|
||
}
|
||
|
||
// 写入数据行
|
||
colOffset := 0
|
||
|
||
// 添加序号列
|
||
if config.AddIndexColumn {
|
||
cellName, _ := excelize.CoordinatesToCellName(1, currentRow)
|
||
outputFile.SetCellValue(config.SheetName, cellName, totalRowsMerged+1)
|
||
colOffset++
|
||
}
|
||
|
||
// 添加源文件列
|
||
if config.AddSourceColumn {
|
||
cellName, _ := excelize.CoordinatesToCellName(1+colOffset, currentRow)
|
||
outputFile.SetCellValue(config.SheetName, cellName, filepath.Base(result.filename))
|
||
colOffset++
|
||
}
|
||
|
||
// 写入原始数据
|
||
for colIndex, cell := range row {
|
||
cellName, _ := excelize.CoordinatesToCellName(colIndex+1+colOffset, currentRow)
|
||
outputFile.SetCellValue(config.SheetName, cellName, cell)
|
||
}
|
||
|
||
currentRow++
|
||
rowsMerged++
|
||
totalRowsMerged++
|
||
}
|
||
|
||
//// 添加分隔行
|
||
//if rowsMerged > 0 {
|
||
// separatorCell, _ := excelize.CoordinatesToCellName(1, currentRow)
|
||
// outputFile.SetCellValue(config.SheetName, separatorCell,
|
||
// fmt.Sprintf("=== 文件 %d/%d: %s ===",
|
||
// result.index+1, len(results), filepath.Base(result.filename)))
|
||
// currentRow++
|
||
//}
|
||
}
|
||
|
||
// 保存输出文件
|
||
if err := outputFile.SaveAs(config.OutputFile); err != nil {
|
||
return fmt.Errorf("保存合并文件失败: %v", err)
|
||
}
|
||
|
||
fmt.Printf("并行合并完成!结果保存在: %s\n", config.OutputFile)
|
||
fmt.Printf("统计信息:\n")
|
||
fmt.Printf(" - 合并文件数: %d\n", len(results))
|
||
fmt.Printf(" - 合并数据行数: %d\n", totalRowsMerged)
|
||
|
||
return em.verifyMergeResult(config.OutputFile, config.SheetName)
|
||
}
|
||
|
||
// MergeByColumn 按列合并Excel文件
|
||
func (em *ExcelManager) MergeByColumn(config MergeConfig) error {
|
||
em.mu.Lock()
|
||
defer em.mu.Unlock()
|
||
|
||
// 获取要合并的文件列表
|
||
filesToMerge, err := em.getFilesToMerge(config)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
fmt.Printf("开始按列合并 %d 个文件:\n", len(filesToMerge))
|
||
|
||
// 创建新的输出文件
|
||
outputFile := excelize.NewFile()
|
||
defer outputFile.Close()
|
||
|
||
// 删除默认的Sheet1
|
||
outputFile.DeleteSheet("Sheet1")
|
||
|
||
// 创建目标sheet
|
||
sheetIndex, err := outputFile.NewSheet(config.SheetName)
|
||
if err != nil {
|
||
return fmt.Errorf("创建sheet失败: %v", err)
|
||
}
|
||
outputFile.SetActiveSheet(sheetIndex)
|
||
|
||
currentCol := 1
|
||
|
||
// 合并所有文件
|
||
for fileIndex, sourceFile := range filesToMerge {
|
||
fmt.Printf("[%d/%d] 正在处理文件: %s\n", fileIndex+1, len(filesToMerge), filepath.Base(sourceFile))
|
||
|
||
// 打开源文件
|
||
srcFile, err := excelize.OpenFile(sourceFile)
|
||
if err != nil {
|
||
return fmt.Errorf("打开文件 %s 失败: %v", sourceFile, err)
|
||
}
|
||
defer srcFile.Close()
|
||
|
||
// 确定要读取的sheet
|
||
sourceSheet := em.getSourceSheet(srcFile, config.SourceSheet)
|
||
if sourceSheet == "" {
|
||
return fmt.Errorf("未找到可用的sheet")
|
||
}
|
||
|
||
// 读取源文件数据
|
||
rows, err := srcFile.GetRows(sourceSheet)
|
||
if err != nil {
|
||
return fmt.Errorf("读取文件 %s 失败: %v,跳过\n", sourceFile, err)
|
||
}
|
||
|
||
if len(rows) == 0 {
|
||
fmt.Printf("警告: 文件 %s 的sheet '%s' 没有数据,跳过\n", sourceFile, sourceSheet)
|
||
continue
|
||
}
|
||
|
||
// 按列写入数据
|
||
for rowIndex, row := range rows {
|
||
// 写入当前列的所有行
|
||
for colIndex, cell := range row {
|
||
targetRow := rowIndex + 1
|
||
cellName, _ := excelize.CoordinatesToCellName(currentCol+colIndex, targetRow)
|
||
outputFile.SetCellValue(config.SheetName, cellName, cell)
|
||
}
|
||
}
|
||
|
||
// 添加文件名称到第一行(作为列标题)
|
||
titleCell, _ := excelize.CoordinatesToCellName(currentCol, 1)
|
||
outputFile.SetCellValue(config.SheetName, titleCell, filepath.Base(sourceFile))
|
||
|
||
// 移动到下一组列
|
||
if len(rows) > 0 && len(rows[0]) > 0 {
|
||
currentCol += len(rows[0])
|
||
} else {
|
||
currentCol += 1
|
||
}
|
||
}
|
||
|
||
// 保存输出文件
|
||
if err := outputFile.SaveAs(config.OutputFile); err != nil {
|
||
return fmt.Errorf("保存合并文件失败: %v", err)
|
||
}
|
||
|
||
fmt.Printf("按列合并完成!结果保存在: %s\n", config.OutputFile)
|
||
return nil
|
||
}
|
||
|
||
// MergeSheetsInSameFile 合并同一文件中的多个sheet
|
||
func (em *ExcelManager) MergeSheetsInSameFile(filename, outputFile string, targetSheetName string) error {
|
||
em.mu.RLock()
|
||
defer em.mu.RUnlock()
|
||
|
||
file, err := excelize.OpenFile(filename)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
defer file.Close()
|
||
|
||
// 获取所有sheet
|
||
sheets := file.GetSheetList()
|
||
if len(sheets) == 0 {
|
||
return fmt.Errorf("文件 %s 中没有sheet", filename)
|
||
}
|
||
|
||
// 创建新的输出文件
|
||
output := excelize.NewFile()
|
||
defer output.Close()
|
||
|
||
// 删除默认的Sheet1
|
||
output.DeleteSheet("Sheet1")
|
||
|
||
// 创建目标sheet
|
||
sheetIndex, err := output.NewSheet(targetSheetName)
|
||
if err != nil {
|
||
return fmt.Errorf("创建sheet失败: %v", err)
|
||
}
|
||
output.SetActiveSheet(sheetIndex)
|
||
|
||
currentRow := 1
|
||
|
||
for _, sheet := range sheets {
|
||
// 读取sheet数据
|
||
rows, err := file.GetRows(sheet)
|
||
if err != nil {
|
||
fmt.Printf("警告: 读取sheet %s 失败: %v,跳过\n", sheet, err)
|
||
continue
|
||
}
|
||
|
||
if len(rows) == 0 {
|
||
continue
|
||
}
|
||
|
||
// 添加sheet名称作为标题
|
||
titleCell, _ := excelize.CoordinatesToCellName(1, currentRow)
|
||
output.SetCellValue(targetSheetName, titleCell, fmt.Sprintf("=== Sheet: %s ===", sheet))
|
||
currentRow++
|
||
|
||
// 写入sheet数据
|
||
for _, row := range rows {
|
||
for colIndex, cell := range row {
|
||
cellName, _ := excelize.CoordinatesToCellName(colIndex+1, currentRow)
|
||
output.SetCellValue(targetSheetName, cellName, cell)
|
||
}
|
||
currentRow++
|
||
}
|
||
|
||
// 添加空行分隔
|
||
currentRow++
|
||
}
|
||
|
||
// 保存输出文件
|
||
return output.SaveAs(outputFile)
|
||
}
|
||
|
||
// ============ 辅助结构和方法 ============
|
||
|
||
// 文件任务结构
|
||
type fileTask struct {
|
||
filename string
|
||
index int
|
||
total int
|
||
}
|
||
|
||
// 文件结果结构
|
||
type fileResult struct {
|
||
filename string
|
||
index int
|
||
data fileData
|
||
}
|
||
|
||
// 文件数据结构
|
||
type fileData struct {
|
||
rows [][]string
|
||
err error
|
||
}
|
||
|
||
// 并行读取Excel文件
|
||
func (em *ExcelManager) readExcelFileParallel(filename, sheetName string) fileData {
|
||
srcFile, err := excelize.OpenFile(filename)
|
||
if err != nil {
|
||
return fileData{err: err}
|
||
}
|
||
defer srcFile.Close()
|
||
|
||
sourceSheet := em.getSourceSheet(srcFile, sheetName)
|
||
if sourceSheet == "" {
|
||
return fileData{err: fmt.Errorf("未找到可用的sheet")}
|
||
}
|
||
|
||
rows, err := srcFile.GetRows(sourceSheet)
|
||
return fileData{
|
||
rows: rows,
|
||
err: err,
|
||
}
|
||
}
|
||
|
||
// 检查行是否为空
|
||
func (em *ExcelManager) isRowEmpty(row []string) bool {
|
||
for _, cell := range row {
|
||
if strings.TrimSpace(cell) != "" {
|
||
return false
|
||
}
|
||
}
|
||
return true
|
||
}
|
||
|
||
// 清理测试文件
|
||
func cleanupFiles(file string) {
|
||
if _, err := os.Stat(file); err == nil {
|
||
os.Remove(file)
|
||
fmt.Printf("删除: %s\n", file)
|
||
}
|
||
}
|
||
|
||
// 辅助函数:将数据转换为JSON字符串
|
||
func convertToJSON(data interface{}) string {
|
||
// 这里使用简单的转换,实际可以使用 encoding/json 包
|
||
switch v := data.(type) {
|
||
case [][]string:
|
||
return convertRowsToJSON(v)
|
||
case []string:
|
||
return strings.Join(v, "\n")
|
||
default:
|
||
return fmt.Sprintf("%v", data)
|
||
}
|
||
}
|
||
|
||
// 将行数据转换为 JSON 字符串
|
||
func convertRowsToJSON(rows [][]string) string {
|
||
if len(rows) == 0 {
|
||
return "[]"
|
||
}
|
||
|
||
// 如果有表头,将数据转换为对象数组
|
||
if len(rows) > 1 {
|
||
headers := rows[0]
|
||
var result []map[string]string
|
||
|
||
// 从第二行开始(跳过表头)
|
||
for i := 1; i < len(rows); i++ {
|
||
rowData := make(map[string]string)
|
||
for j := 0; j < len(headers) && j < len(rows[i]); j++ {
|
||
rowData[headers[j]] = rows[i][j]
|
||
}
|
||
result = append(result, rowData)
|
||
}
|
||
|
||
jsonBytes, err := json.Marshal(result)
|
||
if err != nil {
|
||
// 如果序列化失败,返回简单格式
|
||
return fmt.Sprintf("%v", rows)
|
||
}
|
||
return string(jsonBytes)
|
||
}
|
||
|
||
// 如果没有表头,返回二维数组
|
||
jsonBytes, err := json.Marshal(rows)
|
||
if err != nil {
|
||
return fmt.Sprintf("%v", rows)
|
||
}
|
||
return string(jsonBytes)
|
||
}
|
||
|
||
// 将C字符串数组转换为Go字符串切片
|
||
func cStringArrayToStringSlice(cArray **C.char, length int) []string {
|
||
if cArray == nil || length == 0 {
|
||
return nil
|
||
}
|
||
|
||
var goStrs []string
|
||
for i := 0; i < length; i++ {
|
||
ptr := (**C.char)(unsafe.Pointer(uintptr(unsafe.Pointer(cArray)) + uintptr(i)*unsafe.Sizeof(uintptr(0))))
|
||
if *ptr != nil {
|
||
goStrs = append(goStrs, C.GoString(*ptr))
|
||
}
|
||
}
|
||
|
||
return goStrs
|
||
}
|
||
|
||
// 辅助函数:设置错误信息
|
||
func setError(err string) {
|
||
// 这里可以添加日志记录等操作
|
||
fmt.Printf("Excel操作错误: %s\n", err)
|
||
}
|
||
|
||
// ============ CGO 导出函数 ============
|
||
|
||
var (
|
||
managerStore sync.Map
|
||
nextHandle int64 = 1
|
||
)
|
||
|
||
// 创建新的Excel管理器并返回指针
|
||
//
|
||
//export NewExcelManagerInstance
|
||
func NewExcelManagerInstance() C.longlong {
|
||
manager := NewExcelManager()
|
||
handle := nextHandle
|
||
nextHandle++
|
||
|
||
// 将Go对象存储在map中
|
||
managerStore.Store(handle, manager)
|
||
|
||
return C.longlong(handle)
|
||
}
|
||
|
||
// 释放Excel管理器
|
||
//
|
||
//export FreeExcelManager
|
||
func FreeExcelManager(handle C.longlong) {
|
||
h := int64(handle)
|
||
|
||
// 从map中删除并清理
|
||
if mgr, ok := managerStore.Load(h); ok {
|
||
if manager, ok := mgr.(*ExcelManager); ok {
|
||
manager.CloseAll()
|
||
}
|
||
managerStore.Delete(h)
|
||
}
|
||
}
|
||
|
||
// 读取Excel数据
|
||
//
|
||
//export ReadExcelData
|
||
func ReadExcelData(handle C.longlong, filename *C.char, sheet *C.char, result **C.char) C.int {
|
||
h := int64(handle)
|
||
mgr, ok := managerStore.Load(h)
|
||
if !ok {
|
||
return C.int(-1)
|
||
}
|
||
manager, ok := mgr.(*ExcelManager)
|
||
if !ok {
|
||
return C.int(-1)
|
||
}
|
||
goFilename := C.GoString(filename)
|
||
goSheet := C.GoString(sheet)
|
||
data, err := manager.ReadData(goFilename, goSheet)
|
||
if err != nil {
|
||
return C.int(-1)
|
||
}
|
||
|
||
jsonStr := convertRowsToJSON(data)
|
||
// 将数据转换为JSON字符串
|
||
//jsonStr := convertToJSON(data)
|
||
|
||
//// 将结果转换为JSON格式
|
||
//jsonData, err := ParseExcelDataToJSON(jsonStr)
|
||
//var datas string
|
||
//if err != nil {
|
||
// fmt.Printf("转换JSON失败: %v\n", err)
|
||
//} else {
|
||
// // 将JSON数据格式化输出
|
||
// jsonBytes, err := json.Marshal(jsonData)
|
||
// if err != nil {
|
||
// fmt.Printf("格式化JSON失败: %v\n", err)
|
||
// } else {
|
||
// datas = string(jsonBytes)
|
||
// }
|
||
//}
|
||
*result = C.CString(jsonStr)
|
||
return C.int(0)
|
||
}
|
||
|
||
func ParseExcelDataToJSON(excelData string) ([]map[string]string, error) {
|
||
if excelData == "" {
|
||
return nil, fmt.Errorf("Excel数据为空")
|
||
}
|
||
|
||
// 使用csv解析器解析数据
|
||
reader := csv.NewReader(strings.NewReader(excelData))
|
||
records, err := reader.ReadAll()
|
||
if err != nil {
|
||
return nil, fmt.Errorf("解析Excel数据失败: %v", err)
|
||
}
|
||
|
||
if len(records) < 2 {
|
||
return nil, fmt.Errorf("数据行数不足")
|
||
}
|
||
|
||
// 提取表头(第一行)
|
||
headers := records[0]
|
||
|
||
// 将数据转换为JSON格式
|
||
var jsonData []map[string]string
|
||
|
||
// 从第二行开始(跳过表头)
|
||
for i := 1; i < len(records); i++ {
|
||
row := records[i]
|
||
rowData := make(map[string]string)
|
||
|
||
// 确保每行的列数与表头一致
|
||
for j := 0; j < len(headers) && j < len(row); j++ {
|
||
rowData[headers[j]] = row[j]
|
||
}
|
||
jsonData = append(jsonData, rowData)
|
||
}
|
||
|
||
return jsonData, nil
|
||
}
|
||
|
||
// 批量写入数据到Excel文件
|
||
//
|
||
//export WriteBatchData
|
||
func WriteBatchData(handle C.longlong, filename *C.char, sheet *C.char, cells *C.char, values *C.char, count C.int) C.int {
|
||
h := int64(handle)
|
||
mgr, ok := managerStore.Load(h)
|
||
if !ok {
|
||
return C.int(-1)
|
||
}
|
||
manager, ok := mgr.(*ExcelManager)
|
||
if !ok {
|
||
return C.int(-1)
|
||
}
|
||
goFilename := C.GoString(filename)
|
||
goSheet := C.GoString(sheet)
|
||
goCells := C.GoString(cells)
|
||
goValues := C.GoString(values)
|
||
|
||
var cell []string
|
||
var value []string
|
||
err := json.Unmarshal([]byte(goCells), &cell)
|
||
if err != nil {
|
||
setError(fmt.Sprintf("解析数据失败: %v,数据: %s", err, goCells))
|
||
return C.int(-1)
|
||
}
|
||
err = json.Unmarshal([]byte(goValues), &value)
|
||
if err != nil {
|
||
setError(fmt.Sprintf("解析数据失败: %v,数据: %s", err, goValues))
|
||
return C.int(-1)
|
||
}
|
||
// 构建数据映射
|
||
data := make(map[string]interface{})
|
||
for i, c := range cell {
|
||
data[c] = value[i]
|
||
}
|
||
err = manager.WriteData(goFilename, goSheet, data)
|
||
if err != nil {
|
||
fmt.Printf("批量写入数据失败: %v\n", err)
|
||
return C.int(-1)
|
||
}
|
||
return C.int(0)
|
||
}
|
||
|
||
// 追加数据到Excel文件末尾
|
||
//
|
||
//export AppendDataToExcel
|
||
func AppendDataToExcel(handle C.longlong, filename *C.char, sheet *C.char, values *C.char, count C.int) C.int {
|
||
h := int64(handle)
|
||
mgr, ok := managerStore.Load(h)
|
||
if !ok {
|
||
return C.int(-1)
|
||
}
|
||
manager, ok := mgr.(*ExcelManager)
|
||
if !ok {
|
||
return C.int(-1)
|
||
}
|
||
goFilename := C.GoString(filename)
|
||
goSheet := C.GoString(sheet)
|
||
goValues := C.GoString(values)
|
||
var value []string
|
||
err := json.Unmarshal([]byte(goValues), &value)
|
||
if err != nil {
|
||
setError(fmt.Sprintf("解析数据失败: %v,数据: %s", err, goValues))
|
||
return C.int(-1)
|
||
}
|
||
// 转换为interface{}切片
|
||
var rowData []interface{}
|
||
for _, val := range value {
|
||
rowData = append(rowData, val)
|
||
}
|
||
err = manager.AppendData(goFilename, goSheet, rowData)
|
||
if err != nil {
|
||
fmt.Printf("追加数据失败: %v\n", err)
|
||
return C.int(-1)
|
||
}
|
||
return C.int(0)
|
||
}
|
||
|
||
// 搜索包含关键字的单元格
|
||
//
|
||
//export SearchByKeyword
|
||
func SearchByKeyword(handle C.longlong, filename *C.char, sheet *C.char, keyword *C.char, result **C.char) C.int {
|
||
h := int64(handle)
|
||
mgr, ok := managerStore.Load(h)
|
||
if !ok {
|
||
return C.int(-1)
|
||
}
|
||
manager, ok := mgr.(*ExcelManager)
|
||
if !ok {
|
||
return C.int(-1)
|
||
}
|
||
goFilename := C.GoString(filename)
|
||
goSheet := C.GoString(sheet)
|
||
goKeyword := C.GoString(keyword)
|
||
results, err := manager.SearchByKeyword(goFilename, goSheet, goKeyword)
|
||
if err != nil {
|
||
return C.int(-1)
|
||
}
|
||
// 将结果转换为JSON字符串
|
||
jsonStr := convertToJSON(results)
|
||
*result = C.CString(jsonStr)
|
||
return C.int(0)
|
||
}
|
||
|
||
// 搜索整行包含关键字的行
|
||
//
|
||
//export SearchRowsByKeyword
|
||
func SearchRowsByKeyword(handle C.longlong,
|
||
filename *C.char,
|
||
sheet *C.char,
|
||
keyword *C.char,
|
||
result **C.char) C.int {
|
||
h := int64(handle)
|
||
mgr, ok := managerStore.Load(h)
|
||
if !ok {
|
||
return C.int(-1)
|
||
}
|
||
manager, ok := mgr.(*ExcelManager)
|
||
if !ok {
|
||
return C.int(-1)
|
||
}
|
||
goFilename := C.GoString(filename)
|
||
goSheet := C.GoString(sheet)
|
||
goKeyword := C.GoString(keyword)
|
||
results, err := manager.SearchRowData(goFilename, goSheet, goKeyword)
|
||
if err != nil {
|
||
return C.int(-1)
|
||
}
|
||
// 将结果转换为JSON字符串
|
||
jsonStr := convertToJSON(results)
|
||
*result = C.CString(jsonStr)
|
||
return C.int(0)
|
||
}
|
||
|
||
// 创建新文件并写入数据
|
||
//
|
||
//export CreateAndWriteExcel
|
||
func CreateAndWriteExcel(handle C.longlong, filename *C.char, sheet *C.char, rowsData *C.char) C.int {
|
||
h := int64(handle)
|
||
|
||
fmt.Println("managerStore:", managerStore)
|
||
|
||
// 获取管理器
|
||
mgr, ok := managerStore.Load(h)
|
||
if !ok {
|
||
setError("Invalid handle")
|
||
return C.int(-1)
|
||
}
|
||
|
||
manager, ok := mgr.(*ExcelManager)
|
||
if !ok {
|
||
setError("Invalid manager type")
|
||
return C.int(-1)
|
||
}
|
||
goFilename := C.GoString(filename)
|
||
goSheet := C.GoString(sheet)
|
||
goRowsData := C.GoString(rowsData)
|
||
var data [][]string
|
||
err2 := json.Unmarshal([]byte(goRowsData), &data)
|
||
if err2 != nil {
|
||
// 更详细的错误信息
|
||
setError(fmt.Sprintf("解析数据失败: %v, 数据: %s", err2, goRowsData))
|
||
return C.int(-1)
|
||
}
|
||
// 调用管理器方法
|
||
err := manager.CreateAndWrite(goFilename, goSheet, data)
|
||
if err != nil {
|
||
setError(fmt.Sprintf("创建并写入文件失败: %v", err))
|
||
return C.int(-1)
|
||
}
|
||
fmt.Printf("文件创建成功: %s\n", goFilename)
|
||
return C.int(0)
|
||
}
|
||
|
||
// 增强版合并Excel文件(支持指定文件列表)
|
||
//
|
||
//export MergeExcelFilesEx
|
||
func MergeExcelFilesEx(handle C.longlong,
|
||
sourceDir *C.char,
|
||
specificFiles *C.char,
|
||
outputFile *C.char,
|
||
sheetName *C.char,
|
||
mergeByColumn C.int,
|
||
includeHeaders C.int,
|
||
skipEmptyRows C.int,
|
||
filePattern *C.char,
|
||
sourceSheet *C.char,
|
||
addSourceColumn C.int,
|
||
addIndexColumn C.int) C.int {
|
||
h := int64(handle)
|
||
mgr, ok := managerStore.Load(h)
|
||
if !ok {
|
||
return C.int(-1)
|
||
}
|
||
manager, ok := mgr.(*ExcelManager)
|
||
if !ok {
|
||
return C.int(-1)
|
||
}
|
||
|
||
goSpecificFiles := C.GoString(specificFiles)
|
||
var data []string
|
||
err2 := json.Unmarshal([]byte(goSpecificFiles), &data)
|
||
if err2 != nil {
|
||
// 更详细的错误信息
|
||
setError(fmt.Sprintf("解析数据失败: %v, 数据: %s", err2, goSpecificFiles))
|
||
return C.int(-1)
|
||
}
|
||
// 构建配置
|
||
config := MergeConfig{
|
||
SourceDir: C.GoString(sourceDir),
|
||
SpecificFiles: data,
|
||
OutputFile: C.GoString(outputFile),
|
||
SheetName: C.GoString(sheetName),
|
||
MergeByColumn: mergeByColumn != 0,
|
||
IncludeHeaders: includeHeaders != 0,
|
||
SkipEmptyRows: skipEmptyRows != 0,
|
||
FilePattern: C.GoString(filePattern),
|
||
AddSourceColumn: addSourceColumn != 0,
|
||
AddIndexColumn: addIndexColumn != 0,
|
||
SourceSheet: C.GoString(sourceSheet),
|
||
}
|
||
// 根据合并模式调用不同的方法
|
||
var err error
|
||
if config.MergeByColumn {
|
||
err = manager.MergeByColumn(config)
|
||
} else {
|
||
err = manager.MergeExcelFiles(config)
|
||
}
|
||
if err != nil {
|
||
fmt.Printf("合并Excel文件失败: %v\n", err)
|
||
return C.int(-1)
|
||
}
|
||
return C.int(0)
|
||
}
|
||
|
||
// 并行合并Excel文件(增强版)
|
||
//
|
||
//export MergeExcelFilesParallelEx
|
||
func MergeExcelFilesParallelEx(handle C.longlong,
|
||
sourceDir *C.char,
|
||
specificFiles **C.char,
|
||
fileCount C.int,
|
||
outputFile *C.char,
|
||
sheetName *C.char,
|
||
includeHeaders C.int,
|
||
skipEmptyRows C.int,
|
||
filePattern *C.char,
|
||
sourceSheet *C.char,
|
||
addSourceColumn C.int,
|
||
addIndexColumn C.int,
|
||
workers C.int) C.int {
|
||
h := int64(handle)
|
||
mgr, ok := managerStore.Load(h)
|
||
if !ok {
|
||
return C.int(-1)
|
||
}
|
||
manager, ok := mgr.(*ExcelManager)
|
||
if !ok {
|
||
return C.int(-1)
|
||
}
|
||
// 构建配置
|
||
config := MergeConfig{
|
||
SourceDir: C.GoString(sourceDir),
|
||
OutputFile: C.GoString(outputFile),
|
||
SheetName: C.GoString(sheetName),
|
||
IncludeHeaders: includeHeaders != 0,
|
||
SkipEmptyRows: skipEmptyRows != 0,
|
||
FilePattern: C.GoString(filePattern),
|
||
AddSourceColumn: addSourceColumn != 0,
|
||
AddIndexColumn: addIndexColumn != 0,
|
||
SourceSheet: C.GoString(sourceSheet),
|
||
}
|
||
|
||
// 如果有指定的文件列表,则添加到配置中
|
||
if fileCount > 0 && specificFiles != nil {
|
||
config.SpecificFiles = cStringArrayToStringSlice(specificFiles, int(fileCount))
|
||
}
|
||
|
||
err := manager.MergeExcelFilesParallel(config, int(workers))
|
||
if err != nil {
|
||
fmt.Printf("并行合并Excel文件失败: %v\n", err)
|
||
return C.int(-1)
|
||
}
|
||
|
||
return C.int(0)
|
||
}
|
||
|
||
// 合并同一文件中的多个sheet
|
||
//
|
||
//export MergeSheetsInFile
|
||
func MergeSheetsInFile(handle C.longlong,
|
||
filename *C.char,
|
||
outputFile *C.char,
|
||
targetSheetName *C.char) C.int {
|
||
h := int64(handle)
|
||
mgr, ok := managerStore.Load(h)
|
||
if !ok {
|
||
return C.int(-1)
|
||
}
|
||
manager, ok := mgr.(*ExcelManager)
|
||
if !ok {
|
||
return C.int(-1)
|
||
}
|
||
goFilename := C.GoString(filename)
|
||
goOutputFile := C.GoString(outputFile)
|
||
goTargetSheet := C.GoString(targetSheetName)
|
||
|
||
err := manager.MergeSheetsInSameFile(goFilename, goOutputFile, goTargetSheet)
|
||
if err != nil {
|
||
fmt.Printf("合并sheet失败: %v\n", err)
|
||
return C.int(-1)
|
||
}
|
||
|
||
return C.int(0)
|
||
}
|
||
|
||
// 释放C字符串
|
||
//
|
||
//export FreeCString
|
||
func FreeCString(str *C.char) {
|
||
if str != nil {
|
||
C.free(unsafe.Pointer(str))
|
||
}
|
||
}
|
||
|
||
//func main() {
|
||
//
|
||
//}
|