21
0

try to use a new formatter for date and decimal

This commit is contained in:
Liu Ming 2017-07-26 17:56:49 +08:00
parent ce73093973
commit d3f14f70d8
9 changed files with 408 additions and 12 deletions

8
col.go
View File

@ -7,7 +7,7 @@ import (
"time" "time"
"github.com/extrame/goyymmdd" internalFmt "github.com/extrame/xls/format"
) )
//content type //content type
@ -57,8 +57,8 @@ func (xf *XfRk) String(wb *WorkBook) string {
if !isFloat { if !isFloat {
f = float64(i) f = float64(i)
} }
t := timeFromExcelTime(f, wb.dateMode == 1) // t := timeFromExcelTime(f, wb.dateMode == 1)
return yymmdd.Format(t, formatter.str) return formatter.Format(f, wb.dateMode == 1)
} }
// see http://www.openoffice.org/sc/excelfileformat.pdf Page #174 // see http://www.openoffice.org/sc/excelfileformat.pdf Page #174
} else if 14 <= fNo && fNo <= 17 || fNo == 22 || 27 <= fNo && fNo <= 36 || 50 <= fNo && fNo <= 58 { // jp. date format } else if 14 <= fNo && fNo <= 17 || fNo == 22 || 27 <= fNo && fNo <= 36 || 50 <= fNo && fNo <= 58 { // jp. date format
@ -66,7 +66,7 @@ func (xf *XfRk) String(wb *WorkBook) string {
if !isFloat { if !isFloat {
f = float64(i) f = float64(i)
} }
t := timeFromExcelTime(f, wb.dateMode == 1) t := internalFmt.TimeFromExcelTime(f, wb.dateMode == 1)
return t.Format(time.RFC3339) //TODO it should be international return t.Format(time.RFC3339) //TODO it should be international
} }
} }

View File

@ -1,9 +1,22 @@
package xls package xls
type Format struct { import (
"fmt"
formatter "github.com/extrame/xls/format"
)
type format struct {
Head struct { Head struct {
Index uint16 Index uint16
Size uint16 Size uint16
} }
str string str string
} }
func (f *format) Format(val float64, date1904 bool) string {
_, tokens := formatter.Lexer(f.str)
ds := formatter.Parse(tokens)
fmt.Println("=>", val)
return ds.Format(val, date1904)
}

View File

@ -1,4 +1,4 @@
package xls package format
import ( import (
"math" "math"
@ -69,7 +69,7 @@ func doTheFliegelAndVanFlandernAlgorithm(jd int) (day, month, year int) {
} }
// Convert an excelTime representation (stored as a floating point number) to a time.Time. // Convert an excelTime representation (stored as a floating point number) to a time.Time.
func timeFromExcelTime(excelTime float64, date1904 bool) time.Time { func TimeFromExcelTime(excelTime float64, date1904 bool) time.Time {
var date time.Time var date time.Time
var intPart int64 = int64(excelTime) var intPart int64 = int64(excelTime)
// Excel uses Julian dates prior to March 1st 1900, and // Excel uses Julian dates prior to March 1st 1900, and

106
format/formatter.go Normal file
View File

@ -0,0 +1,106 @@
package format
import (
"errors"
"fmt"
)
var InvalidTimeFormat = errors.New("invalid time format")
type Formatter struct {
typ int
Items []ItemFormatter
}
func (f *Formatter) Format(val float64, date1904 bool) string {
var gf string
for _, i := range f.Items {
itemFormatString, _ := i.translateToGolangFormat()
gf += itemFormatString
}
fmt.Println(f)
if f.typ == DATEFORMAT {
t := TimeFromExcelTime(val, date1904)
return t.Format(gf)
} else {
return fmt.Sprintf("%f", val)
}
}
type ItemFormatter interface {
translateToGolangFormat() (string, error)
setOriginal(string)
}
type basicFormatter struct {
origin string
}
func (self *basicFormatter) translateToGolangFormat() (string, error) {
return self.origin, nil
}
func (self *basicFormatter) setOriginal(o string) {
self.origin = o
}
func (self *basicFormatter) String() string {
return fmt.Sprintf("basic formatter as (%s)", self.origin)
}
type YearFormatter struct {
basicFormatter
}
func (y *YearFormatter) translateToGolangFormat() (string, error) {
switch len(y.origin) {
case 4:
return "2006", nil
case 2:
return "06", nil
default:
return "", InvalidTimeFormat
}
}
func (y *YearFormatter) String() string {
return fmt.Sprintf("year formatter as (%s)", y.origin)
}
type MonthFormatter struct {
basicFormatter
}
func (self *MonthFormatter) translateToGolangFormat() (string, error) {
switch len(self.origin) {
case 2:
return "01", nil
case 1:
return "1", nil
default:
return "", InvalidTimeFormat
}
}
func (self *MonthFormatter) String() string {
return fmt.Sprintf("month formatter as (%s)", self.origin)
}
type DayFormatter struct {
basicFormatter
}
func (self *DayFormatter) translateToGolangFormat() (string, error) {
switch len(self.origin) {
case 2:
return "02", nil
case 1:
return "2", nil
default:
return "", InvalidTimeFormat
}
}
func (self *DayFormatter) String() string {
return fmt.Sprintf("day formatter as (%s)", self.origin)
}

13
format/lang.go Normal file
View File

@ -0,0 +1,13 @@
package format
const T_YEAR_MARK string = "T_YEAR_MARK"
const T_MONTH_MARK string = "T_MONTH_MARK"
const T_DAY_MARK string = "T_DAY_MARK"
const T_RAW_MARK string = "T_RAW_MARK"
const T_EOF string = "T_EOF"
const T_STRING_MARK string = "T_STRING_MARK"
const T_DECIMAL_MARK string = "T_DECIMAL_MARK"
const T_COMMA_MARK string = "T_COMMA_MARK"

150
format/lexer.go Normal file
View File

@ -0,0 +1,150 @@
package format
import (
"strconv"
"strings"
"unicode/utf8"
)
const (
DATEFORMAT = iota
DECIMALFORMAT = iota
)
// LexToken holds is a (type, value) array.
type LexToken [3]string
// EOF character
var EOF string = "+++EOF+++"
// lexerState represents the state of the scanner
// as a function that returns the next state.
type lexerState func(*lexer) lexerState
// run lexes the input by executing state functions until
// the state is nil.
func (l *lexer) Run() {
for state := l.initialState; state != nil; {
state = state(l)
}
}
// Lexer creates a new scanner for the input string.
func Lexer(input string) (*lexer, []LexToken) {
l := &lexer{
input: input,
tokens: make([]LexToken, 0),
lineno: 1,
}
l.initialState = initLexerState
l.Run()
return l, l.tokens
}
// lexer holds the state of the scanner.
type lexer struct {
input string // the string being scanned.
start int // start position of this item.
pos int // current position in the input.
width int // width of last rune read from input.
tokens []LexToken // scanned items.
initialState lexerState
typ int
lineno int
}
// next returns the next rune in the input.
func (l *lexer) next() string {
var r rune
if l.pos >= len(l.input) {
l.width = 0
return EOF
}
r, l.width = utf8.DecodeRuneInString(l.input[l.pos:])
l.pos += l.width
return string(r)
}
// ignore skips over the pending input before this point.
func (l *lexer) ignore() {
l.start = l.pos
}
// backup steps back one rune.
// Can be called only once per call of next.
func (l *lexer) backup() {
l.pos -= l.width
}
// acceptRun consumes a run of runes from the valid set.
func (l *lexer) acceptRun(valid string) {
for strings.Index(valid, l.next()) >= 0 {
}
l.backup()
}
// acceptRun consumes a run of runes from the valid set.
func (l *lexer) acceptUntil(marker string) {
for r := l.next(); r != EOF && strings.Index(marker, r) < 0; r = l.next() {
}
}
// emit passes an item back to the client.
func (l *lexer) emit(t string) {
l.tokens = append(l.tokens, LexToken{t, l.input[l.start:l.pos], strconv.Itoa(l.lineno)})
l.start = l.pos
}
// emit passes an item back to the client.
func (l *lexer) emitRaw() {
if l.pos-l.start > 1 {
l.tokens = append(l.tokens, LexToken{T_RAW_MARK, l.input[l.start : l.pos-1], strconv.Itoa(l.lineno)})
l.start = l.pos - 1
}
}
// emit passes an item back to the client.
func (l *lexer) emitWithoutEnd(t string) {
if l.pos-l.start > 1 {
l.tokens = append(l.tokens, LexToken{t, l.input[l.start : l.pos-1], strconv.Itoa(l.lineno)})
l.start = l.pos
}
}
// initialState is the starting point for the
// scanner. It scans through each character and decides
// which state to create for the lexer. lexerState == nil
// is exit scanner.
func initLexerState(l *lexer) lexerState {
for r := l.next(); r != EOF; r = l.next() {
if r == "y" {
l.emitRaw()
l.acceptRun("y")
l.emit(T_YEAR_MARK)
} else if r == "m" {
l.emitRaw()
l.acceptRun("m")
l.emit(T_MONTH_MARK)
} else if r == "d" {
l.emitRaw()
l.acceptRun("d")
l.emit(T_DAY_MARK)
} else if r == "\"" {
l.emitRaw()
l.ignore()
l.acceptUntil("\"")
l.emitWithoutEnd(T_STRING_MARK)
} else if r == "#" {
l.emitRaw()
l.acceptRun("#,")
l.emit(T_COMMA_MARK)
} else if r == "0" {
l.emitRaw()
l.acceptRun("0123456789.")
l.emit(T_DECIMAL_MARK)
}
}
l.emit(T_EOF)
return nil
}

36
format/parse_test.go Normal file
View File

@ -0,0 +1,36 @@
package format
import (
"fmt"
"testing"
)
func TestParseYY(t *testing.T) {
_, tokens := Lexer(`yy-`)
ds := Parse(tokens)
fmt.Println(ds.Format(8100, true))
}
func TestParseMM(t *testing.T) {
_, tokens := Lexer(`yyyymm`)
ds := Parse(tokens)
fmt.Println(ds.Format(8100, true))
}
func TestParseMM2(t *testing.T) {
_, tokens := Lexer(`yyyy-mm"fasd65af"----`)
ds := Parse(tokens)
fmt.Println(ds.Format(8800, false))
}
func TestParseDD(t *testing.T) {
_, tokens := Lexer(`yyyy-mm-dd`)
ds := Parse(tokens)
fmt.Println(ds.Format(8100, true))
}
func TestParseNum(t *testing.T) {
_, tokens := Lexer(`"$"#,##0.00`)
ds := Parse(tokens)
fmt.Println(ds.Format(8800, false))
}

78
format/parser.go Normal file
View File

@ -0,0 +1,78 @@
package format
// Parse creates a new parser with the recommended
// parameters.
func Parse(tokens []LexToken) Formatter {
p := &parser{
tokens: tokens,
pos: -1,
}
p.initState = initialParserState
return p.run()
}
// run starts the statemachine
func (p *parser) run() Formatter {
var f Formatter
for state := p.initState; state != nil; {
state = state(p, &f)
}
return f
}
// parserState represents the state of the scanner
// as a function that returns the next state.
type parserState func(*parser, *Formatter) parserState
// nest returns what the next token AND
// advances p.pos.
func (p *parser) next() *LexToken {
if p.pos >= len(p.tokens)-1 {
return nil
}
p.pos += 1
return &p.tokens[p.pos]
}
// the parser type
type parser struct {
tokens []LexToken
pos int
serial int
initState parserState
}
// the starting state for parsing
func initialParserState(p *parser, f *Formatter) parserState {
var t *LexToken
for t = p.next(); t[0] != T_EOF; t = p.next() {
var item ItemFormatter
switch t[0] {
case T_YEAR_MARK:
f.typ = DATEFORMAT
item = new(YearFormatter)
case T_MONTH_MARK:
f.typ = DATEFORMAT
item = new(MonthFormatter)
case T_DAY_MARK:
f.typ = DATEFORMAT
item = new(DayFormatter)
case T_RAW_MARK:
item = new(basicFormatter)
case T_STRING_MARK:
item = new(basicFormatter)
case T_COMMA_MARK, T_DECIMAL_MARK:
f.typ = DECIMALFORMAT
item = new(basicFormatter)
}
item.setOriginal(t[1])
f.Items = append(f.Items, item)
}
if len(t[1]) > 0 {
r := new(basicFormatter)
r.origin = t[1]
f.Items = append(f.Items, r)
}
return nil
}

View File

@ -15,7 +15,7 @@ type WorkBook struct {
Codepage uint16 Codepage uint16
Xfs []st_xf_data Xfs []st_xf_data
Fonts []Font Fonts []Font
Formats map[uint16]*Format Formats map[uint16]*format
//All the sheets from the workbook //All the sheets from the workbook
sheets []*WorkSheet sheets []*WorkSheet
Author string Author string
@ -30,7 +30,7 @@ type WorkBook struct {
//read workbook from ole2 file //read workbook from ole2 file
func newWorkBookFromOle2(rs io.ReadSeeker) *WorkBook { func newWorkBookFromOle2(rs io.ReadSeeker) *WorkBook {
wb := new(WorkBook) wb := new(WorkBook)
wb.Formats = make(map[uint16]*Format) wb.Formats = make(map[uint16]*format)
// wb.bts = bts // wb.bts = bts
wb.rs = rs wb.rs = rs
wb.sheets = make([]*WorkSheet, 0) wb.sheets = make([]*WorkSheet, 0)
@ -61,11 +61,11 @@ func (w *WorkBook) addFont(font *FontInfo, buf io.ReadSeeker) {
w.Fonts = append(w.Fonts, Font{Info: font, Name: name}) w.Fonts = append(w.Fonts, Font{Info: font, Name: name})
} }
func (w *WorkBook) addFormat(format *Format) { func (w *WorkBook) addFormat(fmt *format) {
if w.Formats == nil { if w.Formats == nil {
os.Exit(1) os.Exit(1)
} }
w.Formats[format.Head.Index] = format w.Formats[fmt.Head.Index] = fmt
} }
func (wb *WorkBook) parseBof(buf io.ReadSeeker, b *bof, pre *bof, offset_pre int) (after *bof, after_using *bof, offset int) { func (wb *WorkBook) parseBof(buf io.ReadSeeker, b *bof, pre *bof, offset_pre int) (after *bof, after_using *bof, offset int) {
@ -147,7 +147,7 @@ func (wb *WorkBook) parseBof(buf io.ReadSeeker, b *bof, pre *bof, offset_pre int
binary.Read(buf_item, binary.LittleEndian, f) binary.Read(buf_item, binary.LittleEndian, f)
wb.addFont(f, buf_item) wb.addFont(f, buf_item)
case 0x41E: //FORMAT case 0x41E: //FORMAT
font := new(Format) font := new(format)
binary.Read(buf_item, binary.LittleEndian, &font.Head) binary.Read(buf_item, binary.LittleEndian, &font.Head)
font.str, _ = wb.get_string(buf_item, font.Head.Size) font.str, _ = wb.get_string(buf_item, font.Head.Size)
wb.addFormat(font) wb.addFormat(font)