在README.md和pkg/document/README.md中新增单元格遍历迭代器功能的详细描述,包括迭代器的创建、遍历、重置及查找功能;在pkg/document/table.go中实现CellIterator及相关方法,支持按行列遍历和条件查找单元格。

This commit is contained in:
zero
2025-05-30 08:45:41 +08:00
parent 7a736f7a94
commit 46ad019f70
6 changed files with 1225 additions and 2 deletions

View File

@@ -197,6 +197,19 @@ wordZero/
- [x] 表格位置信息获取
- [x] 单元格访问接口
- [x] 按行列索引访问
- [x] **单元格遍历迭代器****新实现**
- [x] 单元格迭代器CellIterator
- [x] 按顺序遍历所有单元格
- [x] 迭代器重置和位置跟踪
- [x] 迭代进度计算
- [x] 单元格信息结构CellInfo
- [x] ForEach批量处理方法
- [x] 按行遍历ForEachInRow
- [x] 按列遍历ForEachInColumn
- [x] 获取单元格范围GetCellRange
- [x] 条件查找单元格FindCells
- [x] 按文本查找单元格FindCellsByText
- [x] 精确匹配和模糊匹配支持
##### 表格样式和外观
- [x] 表格整体样式
@@ -240,7 +253,6 @@ wordZero/
- [ ] 表格访问增强
- [ ] 按标题查找表格
- [ ] 按范围批量访问
- [ ] 单元格遍历迭代器
#### 图片功能
- [ ] 图片插入
@@ -311,6 +323,7 @@ wordZero/
- `examples/table_layout/` - 表格布局和尺寸演示
- `examples/table_style/` - 表格样式和外观演示
- `examples/cell_advanced/` - 单元格高级功能演示
- `examples/cell_iterator/` - **单元格迭代器功能演示****新增**
- `examples/formatting/` - 格式化演示
- `examples/page_settings/` - **页面设置演示****新增**
- `examples/advanced_features/` - **高级功能综合演示****新增**
@@ -340,6 +353,9 @@ go run ./examples/table_style/
# 运行单元格高级功能演示
go run ./examples/cell_advanced/
# 运行单元格迭代器功能演示
go run ./examples/cell_iterator/
# 运行格式化演示
go run ./examples/formatting/

View File

@@ -0,0 +1,187 @@
package main
import (
"fmt"
"log"
"os"
"path/filepath"
"github.com/ZeroHawkeye/wordZero/pkg/document"
)
func main() {
fmt.Println("=== 单元格迭代器功能演示 ===")
// 创建新文档
doc := document.New()
// 创建一个3x4的测试表格
config := &document.TableConfig{
Rows: 3,
Cols: 4,
Width: 8000,
Data: [][]string{
{"产品", "价格", "数量", "总计"},
{"苹果", "5.00", "10", "50.00"},
{"橙子", "3.50", "15", "52.50"},
},
}
table := doc.AddTable(config)
if table == nil {
log.Fatal("创建表格失败")
}
fmt.Printf("创建了 %dx%d 的表格\n", table.GetRowCount(), table.GetColumnCount())
// 演示1: 基本迭代器使用
fmt.Println("\n--- 1. 基本迭代器遍历 ---")
iterator := table.NewCellIterator()
fmt.Printf("表格总共有 %d 个单元格\n", iterator.Total())
cellCount := 0
for iterator.HasNext() {
cellInfo, err := iterator.Next()
if err != nil {
log.Printf("迭代器错误: %v", err)
break
}
cellCount++
fmt.Printf("单元格[%d,%d]: '%s'", cellInfo.Row, cellInfo.Col, cellInfo.Text)
if cellInfo.IsLast {
fmt.Print(" (最后一个)")
}
fmt.Printf(" - 进度: %.1f%%\n", iterator.Progress()*100)
}
// 演示2: 重置迭代器
fmt.Println("\n--- 2. 重置迭代器演示 ---")
iterator.Reset()
row, col := iterator.Current()
fmt.Printf("重置后,当前位置: (%d, %d)\n", row, col)
// 只遍历前3个单元格
for i := 0; i < 3 && iterator.HasNext(); i++ {
cellInfo, _ := iterator.Next()
fmt.Printf("单元格[%d,%d]: '%s'\n", cellInfo.Row, cellInfo.Col, cellInfo.Text)
}
// 演示3: ForEach方法
fmt.Println("\n--- 3. ForEach 批量处理演示 ---")
err := table.ForEach(func(row, col int, cell *document.TableCell, text string) error {
if text != "" {
fmt.Printf("处理单元格[%d,%d]: '%s' (长度: %d)\n", row, col, text, len(text))
}
return nil
})
if err != nil {
log.Printf("ForEach执行失败: %v", err)
}
// 演示4: 按行遍历
fmt.Println("\n--- 4. 按行遍历演示 ---")
for row := 0; row < table.GetRowCount(); row++ {
fmt.Printf("第%d行: ", row+1)
err := table.ForEachInRow(row, func(col int, cell *document.TableCell, text string) error {
fmt.Printf("'%s' ", text)
return nil
})
if err != nil {
log.Printf("按行遍历失败: %v", err)
}
fmt.Println()
}
// 演示5: 按列遍历
fmt.Println("\n--- 5. 按列遍历演示 ---")
for col := 0; col < table.GetColumnCount(); col++ {
fmt.Printf("第%d列: ", col+1)
err := table.ForEachInColumn(col, func(row int, cell *document.TableCell, text string) error {
fmt.Printf("'%s' ", text)
return nil
})
if err != nil {
log.Printf("按列遍历失败: %v", err)
}
fmt.Println()
}
// 演示6: 获取范围单元格
fmt.Println("\n--- 6. 获取单元格范围演示 ---")
cells, err := table.GetCellRange(1, 1, 2, 3) // 获取价格、数量、总计的数据部分
if err != nil {
log.Printf("获取范围失败: %v", err)
} else {
fmt.Printf("范围 (1,1) 到 (2,3) 的单元格:\n")
for _, cellInfo := range cells {
fmt.Printf(" [%d,%d]: '%s'\n", cellInfo.Row, cellInfo.Col, cellInfo.Text)
}
}
// 演示7: 查找单元格
fmt.Println("\n--- 7. 查找单元格演示 ---")
// 查找包含数字的单元格
numberCells, err := table.FindCells(func(row, col int, cell *document.TableCell, text string) bool {
// 简单检查是否包含数字字符
for _, char := range text {
if char >= '0' && char <= '9' {
return true
}
}
return false
})
if err != nil {
log.Printf("查找失败: %v", err)
} else {
fmt.Printf("找到 %d 个包含数字的单元格:\n", len(numberCells))
for _, cellInfo := range numberCells {
fmt.Printf(" [%d,%d]: '%s'\n", cellInfo.Row, cellInfo.Col, cellInfo.Text)
}
}
// 演示8: 按文本查找
fmt.Println("\n--- 8. 按文本查找演示 ---")
// 精确查找
exactCells, err := table.FindCellsByText("苹果", true)
if err != nil {
log.Printf("精确查找失败: %v", err)
} else {
fmt.Printf("精确匹配 '苹果' 的单元格: %d 个\n", len(exactCells))
for _, cellInfo := range exactCells {
fmt.Printf(" [%d,%d]: '%s'\n", cellInfo.Row, cellInfo.Col, cellInfo.Text)
}
}
// 模糊查找
fuzzyCells, err := table.FindCellsByText("5", false)
if err != nil {
log.Printf("模糊查找失败: %v", err)
} else {
fmt.Printf("包含 '5' 的单元格: %d 个\n", len(fuzzyCells))
for _, cellInfo := range fuzzyCells {
fmt.Printf(" [%d,%d]: '%s'\n", cellInfo.Row, cellInfo.Col, cellInfo.Text)
}
}
// 保存文档
outputDir := "examples/output"
if err := os.MkdirAll(outputDir, 0755); err != nil {
log.Printf("创建输出目录失败: %v", err)
}
filename := filepath.Join(outputDir, "cell_iterator_demo.docx")
if err := doc.Save(filename); err != nil {
log.Printf("保存文档失败: %v", err)
} else {
fmt.Printf("\n文档已保存到: %s\n", filename)
}
fmt.Println("\n=== 单元格迭代器演示完成 ===")
}

View File

@@ -220,6 +220,84 @@
- [`SetCellShading(row, col int, config *ShadingConfig)`](table.go#L2121) - 设置单元格底纹
- [`SetAlternatingRowColors(evenRowColor, oddRowColor string)`](table.go#L2142) - 设置交替行颜色
### 单元格遍历迭代器 ✨ **新功能**
提供强大的单元格遍历和查找功能:
##### CellIterator - 单元格迭代器
```go
// 创建迭代器
iterator := table.NewCellIterator()
// 遍历所有单元格
for iterator.HasNext() {
cellInfo, err := iterator.Next()
if err != nil {
break
}
fmt.Printf("单元格[%d,%d]: %s\n", cellInfo.Row, cellInfo.Col, cellInfo.Text)
}
// 获取进度
progress := iterator.Progress() // 0.0 - 1.0
// 重置迭代器
iterator.Reset()
```
##### ForEach 批量处理
```go
// 遍历所有单元格
err := table.ForEach(func(row, col int, cell *TableCell, text string) error {
// 处理每个单元格
return nil
})
// 按行遍历
err := table.ForEachInRow(rowIndex, func(col int, cell *TableCell, text string) error {
// 处理行中的每个单元格
return nil
})
// 按列遍历
err := table.ForEachInColumn(colIndex, func(row int, cell *TableCell, text string) error {
// 处理列中的每个单元格
return nil
})
```
##### 范围操作
```go
// 获取指定范围的单元格
cells, err := table.GetCellRange(startRow, startCol, endRow, endCol)
for _, cellInfo := range cells {
fmt.Printf("单元格[%d,%d]: %s\n", cellInfo.Row, cellInfo.Col, cellInfo.Text)
}
```
##### 查找功能
```go
// 自定义条件查找
cells, err := table.FindCells(func(row, col int, cell *TableCell, text string) bool {
return strings.Contains(text, "关键词")
})
// 按文本查找
exactCells, err := table.FindCellsByText("精确匹配", true)
fuzzyCells, err := table.FindCellsByText("模糊", false)
```
##### CellInfo 结构
```go
type CellInfo struct {
Row int // 行索引
Col int // 列索引
Cell *TableCell // 单元格引用
Text string // 单元格文本
IsLast bool // 是否为最后一个单元格
}
```
## 工具函数
### 日志系统

View File

@@ -4,6 +4,7 @@ package document
import (
"encoding/xml"
"fmt"
"strings"
)
// Table 表示一个表格
@@ -2320,3 +2321,249 @@ func createTableCellBorder(config *BorderConfig) *TableCellBorder {
Color: config.Color,
}
}
// CellIterator 单元格迭代器
type CellIterator struct {
table *Table
currentRow int
currentCol int
totalRows int
totalCols int
}
// CellInfo 单元格信息
type CellInfo struct {
Row int // 行索引
Col int // 列索引
Cell *TableCell // 单元格引用
Text string // 单元格文本
IsLast bool // 是否为最后一个单元格
}
// NewCellIterator 创建新的单元格迭代器
func (t *Table) NewCellIterator() *CellIterator {
totalRows := t.GetRowCount()
totalCols := 0
if totalRows > 0 {
totalCols = t.GetColumnCount()
}
return &CellIterator{
table: t,
currentRow: 0,
currentCol: 0,
totalRows: totalRows,
totalCols: totalCols,
}
}
// HasNext 检查是否还有下一个单元格
func (iter *CellIterator) HasNext() bool {
if iter.totalRows == 0 || iter.totalCols == 0 {
return false
}
// 检查当前位置是否超出范围
return iter.currentRow < iter.totalRows &&
(iter.currentRow < iter.totalRows-1 || iter.currentCol < iter.totalCols)
}
// Next 获取下一个单元格信息
func (iter *CellIterator) Next() (*CellInfo, error) {
if !iter.HasNext() {
return nil, fmt.Errorf("没有更多单元格")
}
// 获取当前单元格
cell, err := iter.table.GetCell(iter.currentRow, iter.currentCol)
if err != nil {
return nil, fmt.Errorf("获取单元格失败: %v", err)
}
// 获取单元格文本
text, _ := iter.table.GetCellText(iter.currentRow, iter.currentCol)
// 创建单元格信息
cellInfo := &CellInfo{
Row: iter.currentRow,
Col: iter.currentCol,
Cell: cell,
Text: text,
}
// 更新位置并检查是否为最后一个
iter.currentCol++
if iter.currentCol >= iter.totalCols {
iter.currentCol = 0
iter.currentRow++
}
// 检查是否为最后一个单元格
cellInfo.IsLast = !iter.HasNext()
return cellInfo, nil
}
// Reset 重置迭代器到开始位置
func (iter *CellIterator) Reset() {
iter.currentRow = 0
iter.currentCol = 0
}
// Current 获取当前位置信息(不移动迭代器)
func (iter *CellIterator) Current() (int, int) {
return iter.currentRow, iter.currentCol
}
// Total 获取总单元格数量
func (iter *CellIterator) Total() int {
return iter.totalRows * iter.totalCols
}
// Progress 获取迭代进度0.0-1.0
func (iter *CellIterator) Progress() float64 {
if iter.totalRows == 0 || iter.totalCols == 0 {
return 1.0
}
processed := iter.currentRow*iter.totalCols + iter.currentCol
total := iter.totalRows * iter.totalCols
return float64(processed) / float64(total)
}
// ForEach 遍历所有单元格,对每个单元格执行指定函数
func (t *Table) ForEach(fn func(row, col int, cell *TableCell, text string) error) error {
iterator := t.NewCellIterator()
for iterator.HasNext() {
cellInfo, err := iterator.Next()
if err != nil {
return fmt.Errorf("迭代失败: %v", err)
}
if err := fn(cellInfo.Row, cellInfo.Col, cellInfo.Cell, cellInfo.Text); err != nil {
return fmt.Errorf("回调函数执行失败 (行:%d, 列:%d): %v", cellInfo.Row, cellInfo.Col, err)
}
}
return nil
}
// ForEachInRow 遍历指定行的所有单元格
func (t *Table) ForEachInRow(rowIndex int, fn func(col int, cell *TableCell, text string) error) error {
if rowIndex < 0 || rowIndex >= t.GetRowCount() {
return fmt.Errorf("行索引无效: %d", rowIndex)
}
colCount := t.GetColumnCount()
for col := 0; col < colCount; col++ {
cell, err := t.GetCell(rowIndex, col)
if err != nil {
return fmt.Errorf("获取单元格失败 (行:%d, 列:%d): %v", rowIndex, col, err)
}
text, _ := t.GetCellText(rowIndex, col)
if err := fn(col, cell, text); err != nil {
return fmt.Errorf("回调函数执行失败 (行:%d, 列:%d): %v", rowIndex, col, err)
}
}
return nil
}
// ForEachInColumn 遍历指定列的所有单元格
func (t *Table) ForEachInColumn(colIndex int, fn func(row int, cell *TableCell, text string) error) error {
if colIndex < 0 || colIndex >= t.GetColumnCount() {
return fmt.Errorf("列索引无效: %d", colIndex)
}
rowCount := t.GetRowCount()
for row := 0; row < rowCount; row++ {
cell, err := t.GetCell(row, colIndex)
if err != nil {
return fmt.Errorf("获取单元格失败 (行:%d, 列:%d): %v", row, colIndex, err)
}
text, _ := t.GetCellText(row, colIndex)
if err := fn(row, cell, text); err != nil {
return fmt.Errorf("回调函数执行失败 (行:%d, 列:%d): %v", row, colIndex, err)
}
}
return nil
}
// GetCellRange 获取指定范围内的所有单元格
func (t *Table) GetCellRange(startRow, startCol, endRow, endCol int) ([]*CellInfo, error) {
// 参数验证
if startRow < 0 || startCol < 0 || endRow >= t.GetRowCount() || endCol >= t.GetColumnCount() {
return nil, fmt.Errorf("范围索引无效: (%d,%d) 到 (%d,%d)", startRow, startCol, endRow, endCol)
}
if startRow > endRow || startCol > endCol {
return nil, fmt.Errorf("开始位置不能大于结束位置")
}
var cells []*CellInfo
for row := startRow; row <= endRow; row++ {
for col := startCol; col <= endCol; col++ {
cell, err := t.GetCell(row, col)
if err != nil {
return nil, fmt.Errorf("获取单元格失败 (行:%d, 列:%d): %v", row, col, err)
}
text, _ := t.GetCellText(row, col)
cellInfo := &CellInfo{
Row: row,
Col: col,
Cell: cell,
Text: text,
IsLast: row == endRow && col == endCol,
}
cells = append(cells, cellInfo)
}
}
return cells, nil
}
// FindCells 查找满足条件的单元格
func (t *Table) FindCells(predicate func(row, col int, cell *TableCell, text string) bool) ([]*CellInfo, error) {
var matchedCells []*CellInfo
err := t.ForEach(func(row, col int, cell *TableCell, text string) error {
if predicate(row, col, cell, text) {
cellInfo := &CellInfo{
Row: row,
Col: col,
Cell: cell,
Text: text,
}
matchedCells = append(matchedCells, cellInfo)
}
return nil
})
if err != nil {
return nil, err
}
return matchedCells, nil
}
// FindCellsByText 根据文本内容查找单元格
func (t *Table) FindCellsByText(searchText string, exactMatch bool) ([]*CellInfo, error) {
return t.FindCells(func(row, col int, cell *TableCell, text string) bool {
if exactMatch {
return text == searchText
}
// 使用strings.Contains进行模糊匹配
return strings.Contains(text, searchText)
})
}

View File

@@ -0,0 +1,466 @@
package document
import (
"fmt"
"testing"
)
// TestCellIterator 测试基本的单元格迭代器功能
func TestCellIterator(t *testing.T) {
// 创建一个3x3的测试表格
doc := New()
config := &TableConfig{
Rows: 3,
Cols: 3,
Width: 5000,
Data: [][]string{
{"A1", "B1", "C1"},
{"A2", "B2", "C2"},
{"A3", "B3", "C3"},
},
}
table := doc.CreateTable(config)
if table == nil {
t.Fatal("创建表格失败")
}
// 测试迭代器创建
iterator := table.NewCellIterator()
if iterator == nil {
t.Fatal("创建迭代器失败")
}
// 测试Total方法
expectedTotal := 9
if iterator.Total() != expectedTotal {
t.Errorf("Total()期望返回%d实际返回%d", expectedTotal, iterator.Total())
}
// 测试迭代器遍历
cellCount := 0
expectedCells := []struct {
row int
col int
text string
}{
{0, 0, "A1"}, {0, 1, "B1"}, {0, 2, "C1"},
{1, 0, "A2"}, {1, 1, "B2"}, {1, 2, "C2"},
{2, 0, "A3"}, {2, 1, "B3"}, {2, 2, "C3"},
}
for iterator.HasNext() {
cellInfo, err := iterator.Next()
if err != nil {
t.Fatalf("迭代器Next()失败: %v", err)
}
if cellCount >= len(expectedCells) {
t.Fatalf("迭代器返回了过多的单元格")
}
expected := expectedCells[cellCount]
if cellInfo.Row != expected.row || cellInfo.Col != expected.col {
t.Errorf("单元格位置不匹配: 期望(%d,%d),实际(%d,%d)",
expected.row, expected.col, cellInfo.Row, cellInfo.Col)
}
if cellInfo.Text != expected.text {
t.Errorf("单元格文本不匹配: 期望'%s',实际'%s'",
expected.text, cellInfo.Text)
}
if cellInfo.Cell == nil {
t.Error("单元格引用为nil")
}
// 测试IsLast标记
if cellCount == len(expectedCells)-1 && !cellInfo.IsLast {
t.Error("最后一个单元格的IsLast标记应为true")
}
cellCount++
}
if cellCount != expectedTotal {
t.Errorf("迭代的单元格数量不匹配: 期望%d实际%d", expectedTotal, cellCount)
}
}
// TestCellIteratorReset 测试迭代器重置功能
func TestCellIteratorReset(t *testing.T) {
doc := New()
config := &TableConfig{
Rows: 2,
Cols: 2,
Width: 3000,
Data: [][]string{
{"A1", "B1"},
{"A2", "B2"},
},
}
table := doc.CreateTable(config)
iterator := table.NewCellIterator()
// 先迭代一些单元格
iterator.Next()
iterator.Next()
// 检查当前位置
row, col := iterator.Current()
if row != 1 || col != 0 {
t.Errorf("迭代器位置不正确: 期望(1,0),实际(%d,%d)", row, col)
}
// 重置迭代器
iterator.Reset()
// 检查重置后的位置
row, col = iterator.Current()
if row != 0 || col != 0 {
t.Errorf("重置后位置不正确: 期望(0,0),实际(%d,%d)", row, col)
}
// 确保能重新遍历
if !iterator.HasNext() {
t.Error("重置后应该有下一个单元格")
}
}
// TestCellIteratorProgress 测试进度计算
func TestCellIteratorProgress(t *testing.T) {
doc := New()
config := &TableConfig{
Rows: 2,
Cols: 2,
Width: 3000,
}
table := doc.CreateTable(config)
iterator := table.NewCellIterator()
// 初始进度应为0
if iterator.Progress() != 0.0 {
t.Errorf("初始进度应为0.0,实际为%f", iterator.Progress())
}
// 迭代一个单元格
iterator.Next()
expectedProgress := 0.25 // 1/4
if iterator.Progress() != expectedProgress {
t.Errorf("迭代一个单元格后进度应为%f实际为%f", expectedProgress, iterator.Progress())
}
// 迭代到最后
for iterator.HasNext() {
iterator.Next()
}
if iterator.Progress() != 1.0 {
t.Errorf("完成迭代后进度应为1.0,实际为%f", iterator.Progress())
}
}
// TestTableForEach 测试ForEach方法
func TestTableForEach(t *testing.T) {
doc := New()
config := &TableConfig{
Rows: 2,
Cols: 3,
Width: 4000,
Data: [][]string{
{"A1", "B1", "C1"},
{"A2", "B2", "C2"},
},
}
table := doc.CreateTable(config)
// 测试ForEach遍历
var visitedCells []string
err := table.ForEach(func(row, col int, cell *TableCell, text string) error {
visitedCells = append(visitedCells, fmt.Sprintf("%d-%d:%s", row, col, text))
return nil
})
if err != nil {
t.Fatalf("ForEach执行失败: %v", err)
}
expectedCells := []string{
"0-0:A1", "0-1:B1", "0-2:C1",
"1-0:A2", "1-1:B2", "1-2:C2",
}
if len(visitedCells) != len(expectedCells) {
t.Errorf("访问的单元格数量不匹配: 期望%d实际%d", len(expectedCells), len(visitedCells))
}
for i, expected := range expectedCells {
if i < len(visitedCells) && visitedCells[i] != expected {
t.Errorf("单元格访问顺序不正确: 期望'%s',实际'%s'", expected, visitedCells[i])
}
}
}
// TestForEachInRow 测试按行遍历
func TestForEachInRow(t *testing.T) {
doc := New()
config := &TableConfig{
Rows: 3,
Cols: 3,
Width: 4000,
Data: [][]string{
{"A1", "B1", "C1"},
{"A2", "B2", "C2"},
{"A3", "B3", "C3"},
},
}
table := doc.CreateTable(config)
// 测试遍历第2行索引1
var visitedCells []string
err := table.ForEachInRow(1, func(col int, cell *TableCell, text string) error {
visitedCells = append(visitedCells, fmt.Sprintf("%d:%s", col, text))
return nil
})
if err != nil {
t.Fatalf("ForEachInRow执行失败: %v", err)
}
expectedCells := []string{"0:A2", "1:B2", "2:C2"}
if len(visitedCells) != len(expectedCells) {
t.Errorf("访问的单元格数量不匹配: 期望%d实际%d", len(expectedCells), len(visitedCells))
}
for i, expected := range expectedCells {
if i < len(visitedCells) && visitedCells[i] != expected {
t.Errorf("单元格访问顺序不正确: 期望'%s',实际'%s'", expected, visitedCells[i])
}
}
// 测试无效行索引
err = table.ForEachInRow(5, func(col int, cell *TableCell, text string) error {
return nil
})
if err == nil {
t.Error("应该返回无效行索引错误")
}
}
// TestForEachInColumn 测试按列遍历
func TestForEachInColumn(t *testing.T) {
doc := New()
config := &TableConfig{
Rows: 3,
Cols: 3,
Width: 4000,
Data: [][]string{
{"A1", "B1", "C1"},
{"A2", "B2", "C2"},
{"A3", "B3", "C3"},
},
}
table := doc.CreateTable(config)
// 测试遍历第2列索引1
var visitedCells []string
err := table.ForEachInColumn(1, func(row int, cell *TableCell, text string) error {
visitedCells = append(visitedCells, fmt.Sprintf("%d:%s", row, text))
return nil
})
if err != nil {
t.Fatalf("ForEachInColumn执行失败: %v", err)
}
expectedCells := []string{"0:B1", "1:B2", "2:B3"}
if len(visitedCells) != len(expectedCells) {
t.Errorf("访问的单元格数量不匹配: 期望%d实际%d", len(expectedCells), len(visitedCells))
}
for i, expected := range expectedCells {
if i < len(visitedCells) && visitedCells[i] != expected {
t.Errorf("单元格访问顺序不正确: 期望'%s',实际'%s'", expected, visitedCells[i])
}
}
// 测试无效列索引
err = table.ForEachInColumn(5, func(row int, cell *TableCell, text string) error {
return nil
})
if err == nil {
t.Error("应该返回无效列索引错误")
}
}
// TestGetCellRange 测试获取单元格范围
func TestGetCellRange(t *testing.T) {
doc := New()
config := &TableConfig{
Rows: 4,
Cols: 4,
Width: 5000,
Data: [][]string{
{"A1", "B1", "C1", "D1"},
{"A2", "B2", "C2", "D2"},
{"A3", "B3", "C3", "D3"},
{"A4", "B4", "C4", "D4"},
},
}
table := doc.CreateTable(config)
// 测试获取2x2范围 (1,1) 到 (2,2)
cells, err := table.GetCellRange(1, 1, 2, 2)
if err != nil {
t.Fatalf("GetCellRange执行失败: %v", err)
}
expectedCells := []struct {
row int
col int
text string
}{
{1, 1, "B2"}, {1, 2, "C2"},
{2, 1, "B3"}, {2, 2, "C3"},
}
if len(cells) != len(expectedCells) {
t.Errorf("返回的单元格数量不匹配: 期望%d实际%d", len(expectedCells), len(cells))
}
for i, expected := range expectedCells {
if i < len(cells) {
cell := cells[i]
if cell.Row != expected.row || cell.Col != expected.col {
t.Errorf("单元格位置不匹配: 期望(%d,%d),实际(%d,%d)",
expected.row, expected.col, cell.Row, cell.Col)
}
if cell.Text != expected.text {
t.Errorf("单元格文本不匹配: 期望'%s',实际'%s'",
expected.text, cell.Text)
}
}
}
// 测试无效范围
_, err = table.GetCellRange(2, 2, 1, 1) // 开始位置大于结束位置
if err == nil {
t.Error("应该返回无效范围错误")
}
_, err = table.GetCellRange(0, 0, 10, 10) // 超出表格范围
if err == nil {
t.Error("应该返回超出范围错误")
}
}
// TestFindCells 测试查找单元格功能
func TestFindCells(t *testing.T) {
doc := New()
config := &TableConfig{
Rows: 3,
Cols: 3,
Width: 4000,
Data: [][]string{
{"apple", "banana", "cherry"},
{"dog", "apple", "fish"},
{"grape", "horse", "apple"},
},
}
table := doc.CreateTable(config)
// 查找包含"apple"的单元格
cells, err := table.FindCells(func(row, col int, cell *TableCell, text string) bool {
return text == "apple"
})
if err != nil {
t.Fatalf("FindCells执行失败: %v", err)
}
expectedPositions := [][2]int{{0, 0}, {1, 1}, {2, 2}}
if len(cells) != len(expectedPositions) {
t.Errorf("找到的单元格数量不匹配: 期望%d实际%d", len(expectedPositions), len(cells))
}
for i, expected := range expectedPositions {
if i < len(cells) {
cell := cells[i]
if cell.Row != expected[0] || cell.Col != expected[1] {
t.Errorf("找到的单元格位置不正确: 期望(%d,%d),实际(%d,%d)",
expected[0], expected[1], cell.Row, cell.Col)
}
if cell.Text != "apple" {
t.Errorf("找到的单元格文本不正确: 期望'apple',实际'%s'", cell.Text)
}
}
}
}
// TestFindCellsByText 测试按文本查找单元格
func TestFindCellsByText(t *testing.T) {
doc := New()
config := &TableConfig{
Rows: 2,
Cols: 3,
Width: 4000,
Data: [][]string{
{"test", "testing", "other"},
{"notest", "test123", "test"},
},
}
table := doc.CreateTable(config)
// 精确匹配
cells, err := table.FindCellsByText("test", true)
if err != nil {
t.Fatalf("FindCellsByText执行失败: %v", err)
}
expectedCount := 2 // (0,0) 和 (1,2)
if len(cells) != expectedCount {
t.Errorf("精确匹配找到的单元格数量不匹配: 期望%d实际%d", expectedCount, len(cells))
}
// 模糊匹配
cells, err = table.FindCellsByText("test", false)
if err != nil {
t.Fatalf("FindCellsByText执行失败: %v", err)
}
expectedCount = 5 // 所有包含"test"的单元格
if len(cells) != expectedCount {
t.Errorf("模糊匹配找到的单元格数量不匹配: 期望%d实际%d", expectedCount, len(cells))
}
}
// TestEmptyTable 测试空表格的迭代器
func TestEmptyTable(t *testing.T) {
doc := New()
// 创建一个空表格实际上最小1x1
config := &TableConfig{
Rows: 1,
Cols: 1,
Width: 2000,
}
table := doc.CreateTable(config)
iterator := table.NewCellIterator()
if iterator.Total() != 1 {
t.Errorf("空表格的总单元格数应为1实际为%d", iterator.Total())
}
if !iterator.HasNext() {
t.Error("1x1表格应该有一个单元格")
}
}

View File

@@ -0,0 +1,229 @@
package test
import (
"os"
"path/filepath"
"testing"
"github.com/ZeroHawkeye/wordZero/pkg/document"
)
// TestCellIteratorIntegration 单元格迭代器功能集成测试
func TestCellIteratorIntegration(t *testing.T) {
// 创建测试文档
doc := document.New()
// 创建测试表格
config := &document.TableConfig{
Rows: 4,
Cols: 3,
Width: 6000,
Data: [][]string{
{"姓名", "年龄", "城市"},
{"张三", "25", "北京"},
{"李四", "30", "上海"},
{"王五", "28", "广州"},
},
}
table := doc.AddTable(config)
if table == nil {
t.Fatal("创建表格失败")
}
// 测试1: 基本迭代器功能
t.Run("基本迭代器", func(t *testing.T) {
iterator := table.NewCellIterator()
// 验证总数
expectedTotal := 12
if iterator.Total() != expectedTotal {
t.Errorf("总单元格数不正确: 期望%d实际%d", expectedTotal, iterator.Total())
}
// 验证完整遍历
count := 0
for iterator.HasNext() {
cellInfo, err := iterator.Next()
if err != nil {
t.Errorf("迭代器错误: %v", err)
break
}
if cellInfo == nil {
t.Error("单元格信息为空")
continue
}
if cellInfo.Cell == nil {
t.Error("单元格引用为空")
}
count++
}
if count != expectedTotal {
t.Errorf("实际遍历数量不正确: 期望%d实际%d", expectedTotal, count)
}
})
// 测试2: 重置功能
t.Run("迭代器重置", func(t *testing.T) {
iterator := table.NewCellIterator()
// 迭代几个单元格
iterator.Next()
iterator.Next()
// 重置并验证
iterator.Reset()
row, col := iterator.Current()
if row != 0 || col != 0 {
t.Errorf("重置后位置不正确: 期望(0,0),实际(%d,%d)", row, col)
}
})
// 测试3: ForEach功能
t.Run("ForEach遍历", func(t *testing.T) {
visitedCount := 0
err := table.ForEach(func(row, col int, cell *document.TableCell, text string) error {
visitedCount++
if cell == nil {
t.Errorf("位置(%d,%d)的单元格为空", row, col)
}
return nil
})
if err != nil {
t.Errorf("ForEach执行失败: %v", err)
}
if visitedCount != 12 {
t.Errorf("ForEach访问数量不正确: 期望12实际%d", visitedCount)
}
})
// 测试4: 按行遍历
t.Run("按行遍历", func(t *testing.T) {
for row := 0; row < table.GetRowCount(); row++ {
visitedCount := 0
err := table.ForEachInRow(row, func(col int, cell *document.TableCell, text string) error {
visitedCount++
return nil
})
if err != nil {
t.Errorf("第%d行遍历失败: %v", row, err)
}
if visitedCount != 3 {
t.Errorf("第%d行单元格数量不正确: 期望3实际%d", row, visitedCount)
}
}
})
// 测试5: 按列遍历
t.Run("按列遍历", func(t *testing.T) {
for col := 0; col < table.GetColumnCount(); col++ {
visitedCount := 0
err := table.ForEachInColumn(col, func(row int, cell *document.TableCell, text string) error {
visitedCount++
return nil
})
if err != nil {
t.Errorf("第%d列遍历失败: %v", col, err)
}
if visitedCount != 4 {
t.Errorf("第%d列单元格数量不正确: 期望4实际%d", col, visitedCount)
}
}
})
// 测试6: 范围获取
t.Run("单元格范围", func(t *testing.T) {
// 获取数据区域 (1,0) 到 (3,2)
cells, err := table.GetCellRange(1, 0, 3, 2)
if err != nil {
t.Errorf("获取范围失败: %v", err)
}
expectedCount := 9 // 3行x3列
if len(cells) != expectedCount {
t.Errorf("范围单元格数量不正确: 期望%d实际%d", expectedCount, len(cells))
}
// 验证范围内容
if cells[0].Row != 1 || cells[0].Col != 0 {
t.Errorf("范围起始位置不正确: 期望(1,0),实际(%d,%d)", cells[0].Row, cells[0].Col)
}
lastIndex := len(cells) - 1
if cells[lastIndex].Row != 3 || cells[lastIndex].Col != 2 {
t.Errorf("范围结束位置不正确: 期望(3,2),实际(%d,%d)",
cells[lastIndex].Row, cells[lastIndex].Col)
}
})
// 测试7: 查找功能
t.Run("单元格查找", func(t *testing.T) {
// 查找包含"张"的单元格
cells, err := table.FindCellsByText("张", false)
if err != nil {
t.Errorf("查找失败: %v", err)
}
if len(cells) != 1 {
t.Errorf("查找结果数量不正确: 期望1实际%d", len(cells))
}
if len(cells) > 0 && cells[0].Text != "张三" {
t.Errorf("查找内容不正确: 期望'张三',实际'%s'", cells[0].Text)
}
// 精确查找
exactCells, err := table.FindCellsByText("25", true)
if err != nil {
t.Errorf("精确查找失败: %v", err)
}
if len(exactCells) != 1 {
t.Errorf("精确查找结果数量不正确: 期望1实际%d", len(exactCells))
}
})
// 测试8: 自定义查找条件
t.Run("自定义查找", func(t *testing.T) {
// 查找年龄大于26的行
ageCells, err := table.FindCells(func(row, col int, cell *document.TableCell, text string) bool {
// 检查年龄列第2列
if col == 1 && row > 0 {
// 简单检查是否包含数字且可能大于26
return text == "30" || text == "28"
}
return false
})
if err != nil {
t.Errorf("自定义查找失败: %v", err)
}
if len(ageCells) != 2 {
t.Errorf("自定义查找结果数量不正确: 期望2实际%d", len(ageCells))
}
})
// 保存测试文档
outputDir := "../examples/output"
if err := os.MkdirAll(outputDir, 0755); err != nil {
t.Logf("创建输出目录失败: %v", err)
}
filename := filepath.Join(outputDir, "cell_iterator_integration_test.docx")
if err := doc.Save(filename); err != nil {
t.Errorf("保存测试文档失败: %v", err)
} else {
t.Logf("测试文档已保存到: %s", filename)
}
}