Skip to content

Commit c12dca0

Browse files
authored
This related to #2080, support blank padding in number formatting (#2216)
1 parent a44e440 commit c12dca0

File tree

3 files changed

+90
-21
lines changed

3 files changed

+90
-21
lines changed

excelize_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -779,10 +779,10 @@ func TestSetCellStyleNumberFormat(t *testing.T) {
779779
idxTbl := []int{0, 1, 2, 3, 4, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49}
780780
value := []string{"37947.7500001", "-37947.7500001", "0.007", "2.1", "String"}
781781
expected := [][]string{
782-
{"37947.75", "37948", "37947.75", "37,948", "37,947.75", "3794775%", "3794775.00%", "3.79E+04", "37947 3/4", "37947 3/4", "11-22-03", "22-Nov-03", "22-Nov", "Nov-03", "6:00 PM", "6:00:00 PM", "18:00", "18:00:00", "11/22/03 18:00", "37,948 ", "37,948 ", "37,947.75 ", "37,947.75 ", " 37,948 ", " $37,948 ", " 37,947.75 ", " $37,947.75 ", "00:00", "910746:00:00", "00:00.0", "37947.7500001", "37947.7500001"},
783-
{"-37947.75", "-37948", "-37947.75", "-37,948", "-37,947.75", "-3794775%", "-3794775.00%", "-3.79E+04", "-37947 3/4", "-37947 3/4", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "(37,948)", "(37,948)", "(37,947.75)", "(37,947.75)", " (37,948)", " $(37,948)", " (37,947.75)", " $(37,947.75)", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001"},
784-
{"0.007", "0", "0.01", "0", "0.01", "1%", "0.70%", "7.00E-03", "0 ", "0 ", "01-00-00", "0-Jan-00", "0-Jan", "Jan-00", "12:10 AM", "12:10:05 AM", "00:10", "00:10:05", "1/0/00 00:10", "0 ", "0 ", "0.01 ", "0.01 ", " 0 ", " $0 ", " 0.01 ", " $0.01 ", "10:05", "0:10:05", "10:04.8", "0.007", "0.007"},
785-
{"2.1", "2", "2.10", "2", "2.10", "210%", "210.00%", "2.10E+00", "2 1/9", "2 1/10", "01-02-00", "2-Jan-00", "2-Jan", "Jan-00", "2:24 AM", "2:24:00 AM", "02:24", "02:24:00", "1/2/00 02:24", "2 ", "2 ", "2.10 ", "2.10 ", " 2 ", " $2 ", " 2.10 ", " $2.10 ", "24:00", "50:24:00", "24:00.0", "2.1", "2.1"},
782+
{"37947.75", "37948", "37947.75", "37,948", "37,947.75", "3794775%", "3794775.00%", "3.79E+04", "37947 3/4", "37947 3/4 ", "11-22-03", "22-Nov-03", "22-Nov", "Nov-03", "6:00 PM", "6:00:00 PM", "18:00", "18:00:00", "11/22/03 18:00", "37,948 ", "37,948 ", "37,947.75 ", "37,947.75 ", " 37,948 ", " $37,948 ", " 37,947.75 ", " $37,947.75 ", "00:00", "910746:00:00", "00:00.0", "37947.7500001", "37947.7500001"},
783+
{"-37947.75", "-37948", "-37947.75", "-37,948", "-37,947.75", "-3794775%", "-3794775.00%", "-3.79E+04", "-37947 3/4", "-37947 3/4 ", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "(37,948)", "(37,948)", "(37,947.75)", "(37,947.75)", " (37,948)", " $(37,948)", " (37,947.75)", " $(37,947.75)", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001", "-37947.7500001"},
784+
{"0.007", "0", "0.01", "0", "0.01", "1%", "0.70%", "7.00E-03", "0 ", "0 ", "01-00-00", "0-Jan-00", "0-Jan", "Jan-00", "12:10 AM", "12:10:05 AM", "00:10", "00:10:05", "1/0/00 00:10", "0 ", "0 ", "0.01 ", "0.01 ", " 0 ", " $0 ", " 0.01 ", " $0.01 ", "10:05", "0:10:05", "10:04.8", "0.007", "0.007"},
785+
{"2.1", "2", "2.10", "2", "2.10", "210%", "210.00%", "2.10E+00", "2 1/9", "2 1/10", "01-02-00", "2-Jan-00", "2-Jan", "Jan-00", "2:24 AM", "2:24:00 AM", "02:24", "02:24:00", "1/2/00 02:24", "2 ", "2 ", "2.10 ", "2.10 ", " 2 ", " $2 ", " 2.10 ", " $2.10 ", "24:00", "50:24:00", "24:00.0", "2.1", "2.1"},
786786
{"String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", "String", " String ", " String ", " String ", " String ", "String", "String", "String", "String", "String"},
787787
}
788788

numfmt.go

Lines changed: 50 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5351,9 +5351,8 @@ func (nf *numberFormat) getNumberFmtConf() {
53515351
}
53525352
}
53535353

5354-
// handleDigitsLiteral apply hash and zero place holder tokens for the number
5355-
// literal.
5356-
func handleDigitsLiteral(text string, tokenValueLen, intPartLen, hashZeroPartLen int) (int, string) {
5354+
// handleDigitsLiteral apply digit placeholder tokens for the number literal.
5355+
func handleDigitsLiteral(text string, tokenValueLen, intPartLen, hashZeroPartLen int, fill bool) (int, string) {
53575356
var result string
53585357
l := tokenValueLen
53595358
if intPartLen == 0 && len(text) > hashZeroPartLen {
@@ -5366,6 +5365,8 @@ func handleDigitsLiteral(text string, tokenValueLen, intPartLen, hashZeroPartLen
53665365
j := i + intPartLen
53675366
if 0 <= j && j < len([]rune(text)) {
53685367
result += string([]rune(text)[j])
5368+
} else if fill {
5369+
result += " "
53695370
}
53705371
}
53715372
return l, result
@@ -5382,21 +5383,44 @@ func (nf *numberFormat) printNumberLiteral(text string) string {
53825383
if nf.usePositive {
53835384
result += "-"
53845385
}
5385-
for _, token := range nf.section[nf.sectionIdx].Items {
5386-
if token.TType == nfp.TokenTypeHashPlaceHolder || token.TType == nfp.TokenTypeZeroPlaceHolder {
5386+
lastNonFractionPartDigital := math.MaxInt
5387+
appearedFraction := false
5388+
numeratorPlaceHolder := 0
5389+
for idx := len(nf.section[nf.sectionIdx].Items) - 1; idx >= 0; idx-- {
5390+
token := nf.section[nf.sectionIdx].Items[idx]
5391+
if token.TType == nfp.TokenTypeFraction {
5392+
appearedFraction = true
5393+
} else if appearedFraction && (token.TType == nfp.TokenTypeHashPlaceHolder || token.TType == nfp.TokenTypeDigitalPlaceHolder) {
5394+
lastNonFractionPartDigital = idx - 1 // current idx belongs to the fraction part (numerator)
5395+
break
5396+
}
5397+
}
5398+
for idx, token := range nf.section[nf.sectionIdx].Items {
5399+
switch token.TType {
5400+
case nfp.TokenTypeHashPlaceHolder, nfp.TokenTypeZeroPlaceHolder:
53875401
hashZeroPartLen += len(token.TValue)
5402+
case nfp.TokenTypeDigitalPlaceHolder:
5403+
if lastNonFractionPartDigital >= 0 && idx < lastNonFractionPartDigital {
5404+
hashZeroPartLen += len(token.TValue)
5405+
}
53885406
}
53895407
}
5390-
for _, token := range nf.section[nf.sectionIdx].Items {
5408+
for idx, token := range nf.section[nf.sectionIdx].Items {
53915409
if token.TType == nfp.TokenTypeCurrencyLanguage {
53925410
_, _ = nf.currencyLanguageHandler(token)
53935411
result += nf.currencyString
53945412
}
53955413
if token.TType == nfp.TokenTypeLiteral {
53965414
result += token.TValue
53975415
}
5398-
if token.TType == nfp.TokenTypeHashPlaceHolder || token.TType == nfp.TokenTypeZeroPlaceHolder || token.TType == nfp.TokenTypeDigitalPlaceHolder {
5399-
digits, str := handleDigitsLiteral(text, len(token.TValue), intPartLen, hashZeroPartLen)
5416+
if token.TType == nfp.TokenTypeDigitalPlaceHolder && idx > lastNonFractionPartDigital {
5417+
// If it is a fraction part, it will be filled in by the fractalHandler()
5418+
// If not, fill placeHolder in directly here
5419+
if !useFraction { // numerator part
5420+
numeratorPlaceHolder = len(token.TValue)
5421+
}
5422+
} else if token.TType == nfp.TokenTypeHashPlaceHolder || token.TType == nfp.TokenTypeZeroPlaceHolder || token.TType == nfp.TokenTypeDigitalPlaceHolder {
5423+
digits, str := handleDigitsLiteral(text, len(token.TValue), intPartLen, hashZeroPartLen, token.TType == nfp.TokenTypeDigitalPlaceHolder)
54005424
intPartLen += digits
54015425
result += str
54025426
}
@@ -5405,27 +5429,37 @@ func (nf *numberFormat) printNumberLiteral(text string) string {
54055429
frac, useFraction = math.Abs(frac), true
54065430
}
54075431
if useFraction {
5408-
result += nf.fractionHandler(frac, token)
5432+
result += nf.fractionHandler(frac, token, numeratorPlaceHolder)
54095433
}
54105434
}
54115435
return nf.printSwitchArgument(result)
54125436
}
54135437

54145438
// fractionHandler handling fraction number format expression for positive and
54155439
// negative numeric.
5416-
func (nf *numberFormat) fractionHandler(frac float64, token nfp.Token) string {
5440+
func (nf *numberFormat) fractionHandler(frac float64, token nfp.Token, numeratorPlaceHolder int) string {
54175441
var rat, result string
5442+
var lastRat *big.Rat
54185443
if token.TType == nfp.TokenTypeDigitalPlaceHolder {
5419-
fracPlaceHolder := len(token.TValue)
5420-
for i := 0; i < 5000; i++ {
5421-
if r := newRat(frac, int64(i), 0); len(r.Denom().String()) <= fracPlaceHolder {
5422-
if rat = r.String(); strings.HasPrefix(rat, "0/") {
5423-
rat = strings.Repeat(" ", 3)
5424-
}
5444+
denominatorPlaceHolder := len(token.TValue)
5445+
for i := range 5000 {
5446+
if r := newRat(frac, int64(i), 0); len(r.Denom().String()) <= denominatorPlaceHolder {
5447+
lastRat = r // record the last valid ratio, and delay conversion to string
54255448
continue
54265449
}
54275450
break
54285451
}
5452+
if lastRat != nil {
5453+
if lastRat.Num().Int64() == 0 {
5454+
rat = strings.Repeat(" ", numeratorPlaceHolder+denominatorPlaceHolder+1)
5455+
} else {
5456+
num := lastRat.Num().String()
5457+
den := lastRat.Denom().String()
5458+
numeratorPlaceHolder = max(numeratorPlaceHolder-len(num), 0)
5459+
denominatorPlaceHolder = max(denominatorPlaceHolder-len(den), 0)
5460+
rat = fmt.Sprintf("%s%s/%s%s", strings.Repeat(" ", numeratorPlaceHolder), num, den, strings.Repeat(" ", denominatorPlaceHolder))
5461+
}
5462+
}
54295463
result += rat
54305464
}
54315465
if token.TType == nfp.TokenTypeDenominator {

numfmt_test.go

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4255,7 +4255,7 @@ func TestNumFmt(t *testing.T) {
42554255
{"1.234E-16", "0.000000000000000000%%%%", "0.000000000000012340%"},
42564256
{"-123.4567", "# ?/?", "-123 1/2"},
42574257
{"123.4567", "# ??/??", "123 37/81"},
4258-
{"123.4567", "#\\ ???/???", "123 58/127"},
4258+
{"123.4567", "#\\ ???/???", "123 58/127"},
42594259
{"123.4567", "#\\ ?/2", "123 1/2"},
42604260
{"123.4567", "#\\ ?/4", "123 2/4"},
42614261
{"123.4567", "#\\ ?/8", "123 4/8"},
@@ -4269,6 +4269,18 @@ func TestNumFmt(t *testing.T) {
42694269
{"1234.5678", "0.0xxx00", "1234.5xxx68"},
42704270
{"80145.899999999994", "[$¥-8004]\" \"#\" \"####\"\"", "¥ 8 0146"},
42714271
{"1", "?", "1"},
4272+
{"1", "???", " 1"},
4273+
{"0", "# ?/?", "0 "},
4274+
{"0", "# ??/??", "0 "},
4275+
{"0", "# ??/???", "0 "},
4276+
{"31.69", "?#???", " 32"},
4277+
{"314159.26535", "#,### ??/??", "314,159 13/49"},
4278+
{"314159.26535", "# ??? ????/??", "314 159 13/49"},
4279+
{"-3.14159265358979323", "# ?/?", "-3 1/7"},
4280+
{"-3.14159265358979323", "# ??/??", "-3 1/7 "},
4281+
{"-3.14159265358979323", "# ???/???", "-3 16/113"},
4282+
{"5.81999", `???? ????`, " 6"},
4283+
{"4.6", `# #### ####`, " 5"},
42724284
// Unsupported number format
42734285
{"37947.7500001", "0.00000000E+000", "37947.7500001"},
42744286
{"123", "[DBNum4][$-804]yyyy\"\"m\"\";@", "123"},
@@ -4320,3 +4332,26 @@ func TestNumFmt(t *testing.T) {
43204332
assert.Equal(t, ErrUnsupportedNumberFormat, err)
43214333
assert.False(t, changeNumFmtCode)
43224334
}
4335+
4336+
func BenchmarkNumFmtPlaceHolder(b *testing.B) {
4337+
items := [][]string{
4338+
{"1", "?", "1"},
4339+
{"1", "???", " 1"},
4340+
{"0", "# ?/?", "0 "},
4341+
{"0", "# ??/??", "0 "},
4342+
{"0", "# ??/???", "0 "},
4343+
{"31.69", "?#???", " 32"},
4344+
{"314159.26535", "#,### ??/??", "314,159 13/49"},
4345+
{"314159.26535", "# ??? ????/??", "314 159 13/49"},
4346+
{"-3.14159265358979323", "# ?/?", "-3 1/7"},
4347+
{"-3.14159265358979323", "# ??/??", "-3 1/7 "},
4348+
{"123.4567", "#\\ ?/10", "123 5/10"},
4349+
}
4350+
b.ReportAllocs()
4351+
for i := 0; i < b.N; i++ {
4352+
for _, item := range items {
4353+
result := format(item[0], item[1], false, CellTypeNumber, nil)
4354+
_ = result
4355+
}
4356+
}
4357+
}

0 commit comments

Comments
 (0)