使用Golang解析xlsx文件的方法与实践

使用Golang解析xlsx文件的方法与实践 大家好 我找到了用于处理xlsx文件的excelize包 现在我正尝试将我的文件解析到结构体字段中 我的代码:

type Test struct {
	field1 string
	field2 string
	field2 string
}

func main() {

var allTestInXlsx []Test
xlsx, err := excelize.OpenFile("./test.xlsx")
if err != nil {
	log.Println(err)
	return
}

sheetsXLSX := xlsx.GetSheetMap()
rows, _  := xlsx.Rows(sheetsXLSX[2])
for rows.Next() {
	s := rows.Columns()
	var tD Test
	tD.field1 = s[0]
	tD.field2 = s[1]
	tD.field3 = s[2]
	allTestInXlsx = append(allTestInXlsx, tD)
}
	log.Println(len(allData))
}

当我在控制台运行程序时,出现错误 - index out of range 我不明白为什么? 因为我的test.xlsx文件中确实存在值 有人用过这个包吗 - 我哪里做错了? 谢谢!


更多关于使用Golang解析xlsx文件的方法与实践的实战教程也可以访问 https://www.itying.com/category-94-b0.html

12 回复

问题是什么?

更多关于使用Golang解析xlsx文件的方法与实践的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


一切正常,是我的错 谢谢!

你能把你正在使用的代码发出来吗……

你尝试过打印从 rows.Columns() 获取的切片长度吗?你不应该假设切片中一定包含元素。

你能执行 fmt.Printf("%+v\n", rows.Columns()) 并粘贴结果吗?这样我们就能看到从 XSLX 解析器获取的内容了。

fmt.Printf("%+v\n", rows.Columns())

YongGopher:

allRowsFromSheet, _ := xlsx.Rows(sheetsXLSX[2])

尝试

allRowsFromSheet, _ := xlsx.Rows(sheetsXLSX[2])
for _, row := range allRowsFromSheet {
	for _, colCell := range row {
		fmt.Print(colCell, "\t")
	}
	fmt.Println()
}

是的,当然 rows.Columns() 不会给出空结果 在 xlsx 文件中有多个工作表,我只尝试解析其中一个 在这个工作表中,某些单元格包含带有公式值的数据 公式是正确的,在 xlsx 中显示正确的结果 也许我遗漏了从带有公式的单元格中获取数据的某些逻辑? 像这样获取数据 s[cell] 是错误的吗? 但我不想在 Golang 端重新计算从解析工作表获取的数据

非常奇怪它返回了 [] 但在同一时间 xlsx.GetSheetMap() 却返回了正确的映射

sheetsXLSX := xlsx.GetSheetMap()
log.Println(sheetsXLSX[2])

allRowsFromSheet, _ := xlsx.Rows(sheetsXLSX[2])
log.Printf("%+v\n", allRowsFromSheet.Columns())

sheetOne

在xlsx工作表中 - 46列 第一种情况

for _, v := range file.Sheet["Name"].Rows {
	var d = MyType{}
	for i, v := range v.Cells {
		d[i] = v
	}
}

返回错误:索引超出范围 - 我不明白为什么 但第二种情况

for _, v := range file.Sheet["Name"].Rows {
	var d = MyType{}
    if len(v.Cells) == 46 {
	for i, v := range v.Cells {
		d[i] = v
	 }
   }
 }

运行良好

感谢 在这种情况下一切正常 但现在我正在使用另一个包

GitHub - tealeg/xlsx: 用于读写XLSX文件的Go库

因为这个包有几个很酷的功能,比如

cell.FormattedValue()

同时我遇到了新问题 当我解析文件(仅sheet[2])时,它返回的值如下

cellOne - 正常 cellTwo - 正常
cellThree - 来自sheet[1]的单元格值

我查看了文件,发现sheet[1]中的某些单元格包含计算公式,这些公式会计算sheet[2]中的值 但是在sheet[2]中并没有指向sheet[1]的链接 我不明白为什么会发生这种情况((((

嗯…确实,如果你不把xlsx文件也发到某个地方的话,我们理解起来有点困难。你是想将xlsx文件中的值解析到Go对象/类型中吗?

看起来这个包已经有了相应的函数:https://godoc.org/github.com/tealeg/xlsx#example-Row-ReadStruct

如果我对你的代码示例理解正确的话,for _, v := range file.Sheet["Name"].Rows { 中的 v 就是行,对吗?

那么你可以这样做:

for _, v := range file.Sheet["Name"].Rows {
    var d = MyType{}
    err := row.ReadStruct(d)
    if err != nil {
        panic(err)
    }
    // 在这里对d做任何你想做的操作,比如添加到所有已读行的切片中
    fmt.Println(d)
}

这样写会更符合Go语言的惯用风格!

在解析xlsx文件时出现"index out of range"错误通常是由于行中的列数少于预期导致的。你的代码假设每行至少有3列,但实际数据可能某些行的列数较少。

以下是修正后的代码:

type Test struct {
	field1 string
	field2 string
	field3 string // 修正了重复的field2
}

func main() {
	var allTestInXlsx []Test
	xlsx, err := excelize.OpenFile("./test.xlsx")
	if err != nil {
		log.Println(err)
		return
	}

	sheetsXLSX := xlsx.GetSheetMap()
	rows, err := xlsx.Rows(sheetsXLSX[1]) // 注意:索引从1开始
	if err != nil {
		log.Println(err)
		return
	}

	for rows.Next() {
		cols, err := rows.Columns()
		if err != nil {
			log.Println(err)
			continue
		}
		
		// 检查列数是否足够
		if len(cols) < 3 {
			log.Printf("行数据不足3列,实际列数: %d\n", len(cols))
			continue
		}
		
		var tD Test
		tD.field1 = cols[0]
		tD.field2 = cols[1]
		tD.field3 = cols[2]
		allTestInXlsx = append(allTestInXlsx, tD)
	}
	
	log.Println(len(allTestInXlsx))
}

主要修正点:

  1. 结构体字段名修正:field2重复,改为field3
  2. 添加错误处理:检查Rows()Columns()的返回值
  3. 列数检查:在访问数组索引前验证len(cols) >= 3
  4. 工作表索引修正:GetSheetMap()返回的映射中索引从1开始

更健壮的版本可以处理空行和列数不足的情况:

for rows.Next() {
	cols, err := rows.Columns()
	if err != nil {
		log.Println(err)
		continue
	}
	
	// 跳过空行
	if len(cols) == 0 {
		continue
	}
	
	var tD Test
	// 安全地赋值,避免索引越界
	if len(cols) > 0 {
		tD.field1 = cols[0]
	}
	if len(cols) > 1 {
		tD.field2 = cols[1]
	}
	if len(cols) > 2 {
		tD.field3 = cols[2]
	}
	allTestInXlsx = append(allTestInXlsx, tD)
}

这样可以确保即使某些行的列数不足,程序也不会崩溃。

回到顶部