Skip to content

Instantly share code, notes, and snippets.

@hartfordfive
Last active July 3, 2024 21:11
Show Gist options
  • Save hartfordfive/69f84657267c2327e484 to your computer and use it in GitHub Desktop.
Save hartfordfive/69f84657267c2327e484 to your computer and use it in GitHub Desktop.
Go function to center a string with whitespace padding
package main
import (
"bytes"
"fmt"
)
// Example: http://play.golang.org/p/Q34HEmWMXh
func main() {
fmt.Printf("|%s|%s|%s|%s|\n",
centerString("First Name", 20),
centerString("Last Name", 20),
centerString("DOB", 20),
centerString("Country", 22))
fmt.Println("----------------------------------------------------------------------------------------")
fmt.Printf("|%s|%s|%s|%s|\n",
centerString("John", 20),
centerString("Doe", 20),
centerString("July 15, 1975", 20),
centerString("United States", 22))
}
func centerString(str string, total_field_width int) string {
str_len := len(str)
spaces_to_pad := total_field_width - str_len
var tmp_spaces float64
var lr_spaces int
tmp_spaces = float64(spaces_to_pad) / 2
lr_spaces = int(tmp_spaces)
buffer := bytes.NewBufferString("")
spaces_remaining := total_field_width
for i := 0; i < lr_spaces; i++ {
buffer.WriteString(" ")
spaces_remaining = spaces_remaining - 1
}
buffer.WriteString(str)
spaces_remaining = spaces_remaining - str_len
for i := spaces_remaining; i > 0; i-- {
buffer.WriteString(" ")
}
return buffer.String()
}
@tanema
Copy link

tanema commented Jun 7, 2022

This could be simplified a lot: https://go.dev/play/p/ptjC-dj6iMQ

package main

import (
	"fmt"
	"strings"
)

func main() {
	fmt.Printf("|%s|%s|%s|%s|\n",
		centerString("First Name", 20),
		centerString("Last Name", 20),
		centerString("DOB", 20),
		centerString("Country", 22))
	fmt.Println("---------------------------------------------------------------------------------------")
	fmt.Printf("|%s|%s|%s|%s|\n",
		centerString("John", 20),
		centerString("Doe", 20),
		centerString("July 15, 1975", 20),
		centerString("United States", 22))
}

func centerString(str string, width int) string {
	spaces := int(float64(width-len(str)) / 2)
	return strings.Repeat(" ", spaces) + str + strings.Repeat(" ", width-(spaces+len(str)))
}

@renatoathaydes
Copy link

renatoathaydes commented Jul 3, 2024

I initially thought @tanema 's solution would be slower, but then I checked it and it's actually much faster for "medium" widths, though a bit slower for very small ones!

I came up with a solution that works using just a format string, but that's a bit slower as well:

func centerString(s string, width int) string {
	a := (width - len(s)) / 2
	b := a + len(s)
	if width % 2 != 0 {
		a++
	}
	return fmt.Sprintf("%[1]*[3]s%[2]*[4]s", b, a, s, "")
}

As I will use this function on a logger handler, I wanted it to be fast... also, I was just curious, so I wrote a quick benchmark:

package test

import (
	"bytes"
	"fmt"
	"strings"
	"testing"
)

var result interface{}

func centerString1(str string, total_field_width int) string {
	str_len := len(str)
	spaces_to_pad := total_field_width - str_len
	var tmp_spaces float64
	var lr_spaces int

	tmp_spaces = float64(spaces_to_pad) / 2
	lr_spaces = int(tmp_spaces)

	buffer := bytes.NewBufferString("")

	spaces_remaining := total_field_width

	for i := 0; i < lr_spaces; i++ {
		buffer.WriteString(" ")
		spaces_remaining = spaces_remaining - 1
	}
	buffer.WriteString(str)
	spaces_remaining = spaces_remaining - str_len
	for i := spaces_remaining; i > 0; i-- {
		buffer.WriteString(" ")
	}

	return buffer.String()
}

func centerString2(str string, width int) string {
	spaces := int(float64(width-len(str)) / 2)
	return strings.Repeat(" ", spaces) + str + strings.Repeat(" ", width-(spaces+len(str)))
}

func centerString3(s string, width int) string {
	a := (width - len(s)) / 2
	b := a + len(s)
	if width % 2 != 0 {
		a++
	}
	return fmt.Sprintf("%[1]*[3]s%[2]*[4]s", b, a, s, "")
}

func benchmarkCenterString1(b *testing.B, w int, s string) {
        var r interface{}
        for n := 0; n < b.N; n++ {
                r = centerString1(s, w)
        }
        result = r
}

func benchmarkCenterString2(b *testing.B, w int, s string) {
	var r interface{}
	for n := 0; n < b.N; n++ {
			r = centerString2(s, w)
	}
	result = r
}

func benchmarkCenterString3(b *testing.B, w int, s string) {
	var r interface{}
	
	for n := 0; n < b.N; n++ {
			r = centerString3(s, w)
	}
	result = r
}

func Benchmark1CenterString2(b *testing.B)  { benchmarkCenterString1(b, 2, "a") }
func Benchmark1CenterString10(b *testing.B)  { benchmarkCenterString1(b, 10, "abc") }
func Benchmark1CenterString100(b *testing.B)  { benchmarkCenterString1(b, 100, "abc") }
func Benchmark1CenterString1000(b *testing.B) { benchmarkCenterString1(b, 1000, "abc") }
func Benchmark1CenterString1000L(b *testing.B) { benchmarkCenterString1(b, 1000, "abcdefghijklmnopqrstuvxzwyabcdefghijklmnopqrstuvxzwyabcdefghijklmnopqrstuvxzwy") }

func Benchmark2CenterString2(b *testing.B)  { benchmarkCenterString2(b, 2, "a") }
func Benchmark2CenterString10(b *testing.B)  { benchmarkCenterString2(b, 10, "abc") }
func Benchmark2CenterString100(b *testing.B)  { benchmarkCenterString2(b, 100, "abc") }
func Benchmark2CenterString1000(b *testing.B) { benchmarkCenterString2(b, 1000, "abc") }
func Benchmark2CenterString1000L(b *testing.B) { benchmarkCenterString2(b, 1000, "abcdefghijklmnopqrstuvxzwyabcdefghijklmnopqrstuvxzwyabcdefghijklmnopqrstuvxzwy") }

func Benchmark3CenterString2(b *testing.B)  { benchmarkCenterString3(b, 2, "a") }
func Benchmark3CenterString10(b *testing.B)  { benchmarkCenterString3(b, 10, "abc") }
func Benchmark3CenterString100(b *testing.B)  { benchmarkCenterString3(b, 100, "abc") }
func Benchmark3CenterString1000(b *testing.B) { benchmarkCenterString3(b, 1000, "abc") }
func Benchmark3CenterString1000L(b *testing.B) { benchmarkCenterString3(b, 1000, "abcdefghijklmnopqrstuvxzwyabcdefghijklmnopqrstuvxzwyabcdefghijklmnopqrstuvxzwy") }

Results on my Macbook Air:

goos: darwin
goarch: amd64
pkg: server/test
cpu: Intel(R) Core(TM) i5-1030NG7 CPU @ 1.10GHz

# Original Solution
Benchmark1CenterString2-8               18358189                59.52 ns/op
Benchmark1CenterString10-8              12077696               100.3 ns/op
Benchmark1CenterString100-8              1727518               682.5 ns/op
Benchmark1CenterString1000-8              196567              5699 ns/op
Benchmark1CenterString1000L-8             215280              5540 ns/op

# Tanema's Solution
Benchmark2CenterString2-8               16654598                70.01 ns/op
Benchmark2CenterString10-8               8091330               153.4 ns/op
Benchmark2CenterString100-8              4972375               255.4 ns/op
Benchmark2CenterString1000-8             1410862               820.8 ns/op
Benchmark2CenterString1000L-8            1358682               812.8 ns/op

# My Solution
Benchmark3CenterString2-8                4882995               238.0 ns/op
Benchmark3CenterString10-8               4467850               286.1 ns/op
Benchmark3CenterString100-8              2866339               404.3 ns/op
Benchmark3CenterString1000-8             1000000              1109 ns/op
Benchmark3CenterString1000L-8            1215786               956.0 ns/op
PASS
ok      server/test      24.125s

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment