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

1452 lines
35 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 (
"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, &currentRow)
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() {
//
//}