Format a text in GO better than fmt
Tags: go, opensource, programming, tutorial
Published: 2024-01-14 18:47:23

## Format a text in GO better then fmt

Looking at the article title, we should clarify what means **better** and what is **text formatting**.  Lets start from the last one from these these theses. Text formatting is an important part of programming, prepared text is using in a various tasks:
  * description/result of some operations;
  * detailed log;
  * as a query for data selection in other systems;
  * and in many others fields.
Better means that `sf` ([wissance.StringFormatter](https://github.com/Wissance/stringFormatter)) has features that `fmt` has't (see chapter 1 to see our text formatting approach).

## 1. What can do sf aka wissance.stringFormatter
In our earlier [article](https://dev.to/evillord666/string-formatting-in-golang-3l7h) we were writing about `sf` **_convinience_** (convenience is a thing that is subjective to humans; here, I mean convenience based on my own background). But briefly it is more covnenient to format text like:
```go
userNews = stringFormatter.Format("Hi \"{0}\", see latest news: {1}", "john doe", "1. You won 1M$ in a lottery, please give us your VISA/MS card data to receive money.")
```
then like:
```go
userNews = fmt.Sprintf("Hi \"%s\", see latest news: %s", "john doe", "1. You won 1M$ in a lottery, please give us your VISA/MS card data to receive money.")
```
**Until** version `1.2.0` `sf` was unable to make more precise argument formatting (i.e., use different number notation: `bin`, `hex`), **starting with 1.2.0 we could do almost all that `fmt` supports**:

1. Bin number formatting:
  * `{0:B}`, 15 outputs -> `1111`
  * `{0:B8}`, 15 outputs -> `00001111`
2. Hex number formatting
  * `{0:X}`, 250 outputs -> `fa`
  * `{0:X4}`, 250 outputs -> `00fa`
3. Oct number formatting
  * `{0:o}`, 11 outputs -> `14`
4. Float point number formatting
  * `{0:E2}`, 191.0478 outputs -> `1.91e+02`
  * `{0:F}`, 10.4567890 outputs ->  `10.456789`
  * `{0:F4}`, 10.4567890 outputs -> `10.4568`
  * `{0:F8}`, 10.4567890 outputs -> `10.45678900`
5. Percentage output
  * `{0:P100}`, 12 outputs -> `12%`

`sf` has 2 string format methods - `Format` and `FormatComplex`. Latter also allows passing argument formatting, like for `Format` method.

Let's consider a minimal example:
1. We should build text using the following format `"Today tempearture is {temp}, humidity is {hum}` where `{temp}` and `{hum}` should be replaced with an actual sensor values.
2. We would like to specify `{temp}` and `{hum}` output, i.e., `{temp}` should have 4 digits after the dot, and `{hum}` must be outputted in percents. After analyzing these requirements, we modified our template as follows: `"Today tempearture is {temp:F4}, humidity is {hum:P100}"`.
3. Passing `12.3456` and `60` like this:
```go
   sf.FormatComplex("Today tempearture is {temp:F4}, humidity is {hum:P100}", map[string]any {"temp":12.3456, "hum":60})
```

More examples could be found in a `FormatComplex` unit test, see in repo and below:

```go
func TestFormatComplexWithArgFormatting(t *testing.T) {
	for name, test := range map[string]struct {
		template string
		args     map[string]any
		expected string
	}{
		"numeric_test_1": {
			template: "This is the text with an only number formatting: scientific - {mass} / {mass : e2}",
			args:     map[string]any{"mass": 191.0784},
			expected: "This is the text with an only number formatting: scientific - 191.0784 / 1.91e+02",
		},
		"numeric_test_2": {
			template: "This is the text with an only number formatting: binary - {bin:B} / {bin : B8}, hexadecimal - {hex:X} / {hex : X4}",
			args:     map[string]any{"bin": 15, "hex": 250},
			expected: "This is the text with an only number formatting: binary - 1111 / 00001111, hexadecimal - fa / 00fa",
		},
		"numeric_test_3": {
			template: "This is the text with an only number formatting: decimal - {float:F} / {float : F4} / {float:F8}",
			args:     map[string]any{"float": 10.5467890},
			expected: "This is the text with an only number formatting: decimal - 10.546789 / 10.5468 / 10.54678900",
		},
	} {
		t.Run(name, func(t *testing.T) {
			assert.Equal(t, test.expected, stringFormatter.FormatComplex(test.template, test.args))
		})
	}
}
```

It is clear that **we've got convenient** (for people with `C#`, `Python` background) **library for text formatting**. But we have another advantage - **_performance_**, **`sf` makes formatting **FASTER** than `fmt`**.

## 2. Perforamnce Benefits
There is one more important thing that could distinguish a `sf` library: it has performance advances, both in formatting cases with argument format specifications and without them. But `FormatComplex` is twice faster than `fmt`, see picture below with the results:

![sf vs fmt performance comparison](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y8byhrmyz81n7jcb4la7.png)

## 3. Conclusion
Today we could say that there is [one more text formatting utility/lib](https://github.com/Wissance/stringFormatter) that has all `fmt` features, and this library processes text faster, which in some cases could be important. Please give us a star on GitHub and subscribe.