Golang : Warp text string by number of characters or runes example


Tags : golang line-break character-wrap rune-slice slice-character

Problem:

You have a long text string and you want to wrap the text according to a given number of characters per line instead of number of words. This is to make the output fit nicely to display on terminal or web page. How to do that?

Solution:

This is a simple and primitive wrap by number of characters per line solution that should work for most cases.

  • convert the text string into a slice of characters.

  • push out the characters into a line according to the given limit/length and append each line with \r\n

Here you go!

 package main

 import (
 "fmt"
 "github.com/buger/goterm"
 )

 // Limits a string to X number of characters.

 func char_wrap(s string, limit int) string {

 var charSlice []rune

 // push characters to slice
 for _, char := range s {
 charSlice = append(charSlice, char)
 }

 var result string = ""

 for len(charSlice) >= 1 {
 // convert slice/array back to string
 // but insert \r\n at specified limit

 result = result + string(charSlice[:limit]) + "\r\n"

 // discard the elements that were copied over to result
 charSlice = charSlice[limit:]

 // change the limit
 // to cater for the last few words in
 //
 if len(charSlice) < limit {
 limit = len(charSlice)
 }

 }

 return result

 }

 func main() {

 str := "ReadAtLeast reads from r into buf until it has read at least min bytes. It returns the number of bytes copied and an error if fewer bytes were read. The error is EOF only if no bytes were read. If an EOF happens after reading fewer than min bytes, ReadAtLeast returns ErrUnexpectedEOF. If min is greater than the length of buf, ReadAtLeast returns ErrShortBuffer. On return, n >= min if and only if err == nil."

 fmt.Printf("Original : \n[%s]\n\n", str)

 // wrap at 80 characters
 fmt.Printf("80 characters : \n%s\n", char_wrap(str, 80))

 // wrap according to terminal width
 terminalWidth := goterm.Width()

 fmt.Println("Terminal width is : ", terminalWidth)

 fmt.Printf("%d characters : \n%s\n", terminalWidth, char_wrap(str, terminalWidth))

 japaneseStr := "お客様からお預かりする貴重な金型を共有の財産と考え、金型の生産性向上や長寿命化にも重
 点をおいています。 最新技術・機器を導入し、より品質の高いものを提供します。また古い金型に新しく息を吹き込む努力も惜しみません。 経年劣化よる摩耗やひび割れ・酸化した金型もメンテナンスします。"

 fmt.Printf("Original : \n[%s]\n\n", japaneseStr)

 // wrap at 20 runes
 fmt.Printf("20 characters : \n%s\n", char_wrap(japaneseStr, 20))

 fmt.Println("Terminal width is : ", terminalWidth)

 fmt.Printf("%d characters : \n%s\n", terminalWidth, char_wrap(japaneseStr, terminalWidth))

 }

Output :

Original : [ReadAtLeast reads from r into buf until it has read at least min bytes. It returns the number of bytes copied and an error if fewer bytes were read. The error is EOF only if no bytes were read. If an EOF happens after reading fewer than min bytes, ReadAtLeast returns ErrUnexpectedEOF. If min is greater than the length of buf, ReadAtLeast returns ErrShortBuffer. On return, n >= min if and only if err == nil.]


80 characters :

ReadAtLeast reads from r into buf until it has read at least min bytes. It retur

ns the number of bytes copied and an error if fewer bytes were read. The error i

s EOF only if no bytes were read. If an EOF happens after reading fewer than min

bytes, ReadAtLeast returns ErrUnexpectedEOF. If min is greater than the length

of buf, ReadAtLeast returns ErrShortBuffer. On return, n >= min if and only if e

rr == nil.


Terminal width is : 109

109 characters :

ReadAtLeast reads from r into buf until it has read at least min bytes. It returns the number of bytes copied

and an error if fewer bytes were read. The error is EOF only if no bytes were read. If an EOF happens after

reading fewer than min bytes, ReadAtLeast returns ErrUnexpectedEOF. If min is greater than the length of buf,

ReadAtLeast returns ErrShortBuffer. On return, n >= min if and only if err == nil.


Original :

[お客様からお預かりする貴重な金型を共有の財産と考え、金型の生産性向上や長寿命化にも重点をおいています。 最新技術・機器を導入し、より品質の高いものを提供します。また古い金型に新しく息を吹き込む努力も惜しみません。 経年劣化よる摩耗やひび割れ・酸化した金型もメンテナンスします。]


20 characters :

お客様からお預かりする貴重な金型を共有の

財産と考え、金型の生産性向上や長寿命化に

も重点をおいています。 最新技術・機器を

導入し、より品質の高いものを提供します。

また古い金型に新しく息を吹き込む努力も惜

しみません。 経年劣化よる摩耗やひび割れ

・酸化した金型もメンテナンスします。


Terminal width is : 109

109 characters :

お客様からお預かりする貴重な金型を共有の財産と考え、金型の生産性向上や長寿命化にも重点をおいています。 最新技術・機器を導入し、より品質の高いも

のを提供します。また古い金型に新しく息を吹き込む努力も惜しみません。 経年

劣化よる摩耗やひび割れ・酸化した金型もメンテナンスします。

References:

https://www.socketloop.com/tutorials/golang-count-number-of-rune-in-string

https://www.socketloop.com/tutorials/golang-get-terminal-width-and-height-example

  See also : Golang : Simple word wrap or line breaking example



Tags : golang line-break character-wrap rune-slice slice-character

By Adam Ng

IF you gain some knowledge or the information here solved your programming problem. Please consider donating to the less fortunate or some charities that you like. Apart from donation, planting trees, volunteering or reducing your carbon footprint will be great too.


Advertisement