瀏覽代碼

Add alpha cell ID to cell in output.

Geoffrey J. Teale 11 年之前
父節點
當前提交
881fcc3ee2
共有 3 個文件被更改,包括 72 次插入26 次删除
  1. 44 25
      lib.go
  2. 25 0
      lib_test.go
  3. 3 1
      sheet.go

+ 44 - 25
lib.go

@@ -78,32 +78,11 @@ func getLargestDenominator(numerator, multiple, baseDenominator, power int) (int
 	return multiple, power
 }
 
-// numericToLetters is used to convert a zero based, numeric column
-// indentifier into a character code.
-func numericToLetters(colRef int) string {
-	b26Denominator, _ := getLargestDenominator(colRef, 1, 26, 0)
-	parts := []int{}
-	x := colRef
-	for d := b26Denominator; d > 0; d = d / 26 {
-		value := x / d
-		remainder := x % d
-		parts = append(parts, value)
-		x = remainder
-	}
-	lastPart := len(parts) - 1
-	for i := lastPart -1; i > 0; i-- {
-		part := parts[i]
-		if part == 0 {
-			greaterPart := parts[i-1]
-			if greaterPart > 0 {
-				parts[i-1] = greaterPart - 1
-				parts[i] = 26
-			}
-		}
-	}
+func formatColumnName(colId []int) string {
+	lastPart := len(colId) - 1
+
 	result := ""
-	for n, part := range(parts) {
-		fmt.Printf("c = %d, n = %d, p = %d, l = %d\n", colRef, n, part, lastPart)
+	for n, part := range(colId) {
 		if n == lastPart {
 			result += string(part + 65)
 		} else {
@@ -115,6 +94,46 @@ func numericToLetters(colRef int) string {
 	return result
 }
 
+func smooshBase26Slice(b26 []int) []int {
+	// Smoosh values together, eliminating 0s from all but the
+	// least significant part.
+	lastButOnePart := len(b26) - 2
+	for i := lastButOnePart; i > 0; i-- {
+		part := b26[i]
+		if part == 0 {
+			greaterPart := b26[i-1]
+			if greaterPart > 0 {
+				b26[i-1] = greaterPart - 1
+				b26[i] = 26
+			}
+		}
+	}
+	return b26
+}
+
+func intToBase26(x int) (parts []int) {
+	// Excel column codes are pure evil - in essence they're just
+	// base26, but they don't represent the number 0.
+	b26Denominator, _ := getLargestDenominator(x, 1, 26, 0)
+
+	// This loop terminates because integer division of 1 / 26
+	// returns 0.
+	for d := b26Denominator; d > 0; d = d / 26 {
+		value := x / d
+		remainder := x % d
+		parts = append(parts, value)
+		x = remainder
+	}
+	return parts
+}
+
+// numericToLetters is used to convert a zero based, numeric column
+// indentifier into a character code.
+func numericToLetters(colRef int) string {
+	parts := intToBase26(colRef)
+	return formatColumnName(smooshBase26Slice(parts))
+}
+
 // letterOnlyMapF is used in conjunction with strings.Map to return
 // only the characters A-Z and a-z in a string
 func letterOnlyMapF(rune rune) rune {

+ 25 - 0
lib_test.go

@@ -137,6 +137,31 @@ func (l *LibSuite) TestReadWorkbookRelationsFromZipFileWithFunnyNames(c *C) {
 	c.Assert(cell1.String(), Equals, "I am Bob")
 }
 
+
+// Excel column codes are a special form of base26 that doesn't allow
+// zeros, except in the least significant part of the code.  Test we
+// can smoosh the numbers in a normal base26 representation (presented
+// as a slice of integers) down to this form.
+func (l *LibSuite) TestSmooshBase26Slice(c *C) {
+	input := []int{20, 0, 1}
+	expected := []int{19, 26, 1}
+	c.Assert(smooshBase26Slice(input), DeepEquals, expected)
+}
+
+// formatColumnName converts slices of base26 integers to alphabetical
+// column names.  Note that the least signifcant character has a
+// different numeric offset (Yuck!)
+func (l *LibSuite) TestFormatColumnName(c *C) {
+	c.Assert(formatColumnName([]int{0}), Equals, "A")
+	c.Assert(formatColumnName([]int{25}), Equals, "Z")
+	c.Assert(formatColumnName([]int{1, 25}), Equals, "AZ")
+	c.Assert(formatColumnName([]int{26, 25}), Equals, "ZZ")
+	c.Assert(formatColumnName([]int{26, 26, 25}), Equals, "ZZZ")
+}
+
+
+// getLargestDenominator returns the largest power of a provided value
+// that can fit within a given value.
 func (l *LibSuite) TestGetLargestDenominator(c *C) {
 	d, p := getLargestDenominator(0, 1, 2, 0)
 	c.Assert(d, Equals, 1)

+ 3 - 1
sheet.go

@@ -2,6 +2,7 @@ package xlsx
 
 import (
 	"encoding/xml"
+	"fmt"
 )
 
 // Sheet is a high level structure intended to provide user access to
@@ -28,8 +29,9 @@ func (s *Sheet) makeXLSXSheet() ([]byte, error) {
 	for r, row := range s.Rows {
 		xRow := xlsxRow{}
 		xRow.R = r + 1
-		for _, cell := range row.Cells {
+		for c, cell := range row.Cells {
 			xC := xlsxC{}
+			xC.R = fmt.Sprintf("%s%d", numericToLetters(c), r + 1)
 			xC.V = cell.Value
 			xC.T = "s" // Hardcode string type, for now.
 			xRow.C = append(xRow.C, xC)