Assuming that your input string is valid UTF-8, this thread (Golang - ToUpper() on a single byte?) is close enough, though not quite a perfect duplicate. We can build on that to come to an acceptable solution using unicode.ToUpper
on the first rune of the string.
r := []rune(s)
r[0] = unicode.ToUpper(r[0])
s := string(r)
Or with a "clever" one-liner:
s := string(append([]rune{unicode.ToUpper(r[0])}, r[1:]...))
Unlike strings, rune slices are not immutable, so you can replace the first rune with ToUpper
, which will take care of non-ASCII and/or multi-byte code points that do have upper cases (e.g. Russian) and leave alone those that don't (e.g. Asian scripts)
NOTE: there is a difference between UPPER case and TITLE case, which is simply explained here. In short, digraph characters like DŽ will have different title case (Dž, only first grapheme capitalized) and upper cases (DŽ, both graphemes capitalized). If you actually need titlecase, use unicode.ToTitle
.
NOTE 2: converting to/from string
to []rune
involves copying, because you get a mutable slice from an immutable string. Do profile your application if you expect to use it in performance-sensitive code.
Playground: https://go.dev/play/p/HpCBM7cRflZ
If you have a sizeable input string where a full rune slice conversion becomes too expensive, you can work around this using a capped strings.SplitN
on some separator, essentially to extract the first word of the text and use only that in the conversion:
sep := " "
ss := strings.SplitN(s, sep, 2)
r := []rune(ss[0])
r[0] = unicode.ToUpper(r[0])
s = string(r) + sep + ss[1])
Benchmarking with a ~30K input string shows a significant difference:
go test -v -bench=. -benchmem
goos: darwin
goarch: arm64
pkg: example.com
BenchmarkRuneConv-10 6376 183691 ns/op 258049 B/op 3 allocs/op
BenchmarkSplitN-10 1709989 706.1 ns/op 4152 B/op 3 allocs/op
PASS
ok example.com 3.477s