Efficient GO programming - printing

Posted by dessolator on Tue, 11 Jan 2022 03:50:02 +0100

Welcome to GolangRoadmap, a young GO developer community https://www.golangroadmap.com/ At present, it is an invitation system registration, with registration code: Gopher-1035-0722. It has opened the columns of GO push, GO interview, GO Baodian, GO rebate and so on

Go's format printing style is similar to C's printf family, but it is richer and more general. These functions are in the FMT package, and the function names are capitalized: for example, FMT Printf,fmt.Fprintf,fmt.Sprintf et al. String functions (sprintf, etc.) return a string instead of filling a given buffer.

You do not need to provide a format string. Each Printf, Fprintf and Sprintf corresponds to another function, such as Print and Println. Instead of accepting format strings, these functions generate a default format for each argument. The functions of the Println series also insert spaces in the arguments and append a newline character to the output, while the Print version adds white space only when there is no string on either side of the operand. In the following example, each row produces the same output.

fmt.Printf("Hello %d\n", 23)
fmt.Fprint(os.Stdout, "Hello ", 23, "\n")
fmt.Println("Hello", 23)
fmt.Println(fmt.Sprint("Hello ", 23))

fmt. The format printing function such as fprint can accept any implementation of Io The object of the writer interface is used as the first argument; Variable OS Stdout and OS Stderr is a well-known example.

From here on, it's a little different from C. First, numeric formats such as% d do not accept markers representing symbols or sizes, and the print routine determines these attributes based on the type of arguments.

var x uint64 = 1<<64 - 1
fmt.Printf("%d %x; %d %x\n", x, x, int64(x), int64(x))

Print results

18446744073709551615 ffffffffffffffff; -1 -1

If you only want the default conversion, such as using decimal integers, you can use the general format%v (corresponding to "value"); The result is exactly the same as the output of Print and Println. In addition, this format can Print arbitrary values, even arrays, structures, and mappings. The following is the statement to Print the time zone mapping defined in the previous section.

fmt.Printf("%v\n", timeZone)  // or just fmt.Println(timeZone)

Print results:

map[CST:-21600 EST:-18000 MST:-25200 PST:-28800 UTC:0]

For mappings, Printf automatically sorts the mapping values in key dictionary order.

Of course, the keys in the map can be output in any order. When printing a structure, the improved format% + V will add a field name to each field of the structure, while the other format%#v will print the value exactly according to the syntax of Go.

type T struct {
    a int
    b float64
    c string
}
t := &T{ 7, -2.35, "abc\tdef" }
fmt.Printf("%v\n", t)
fmt.Printf("%+v\n", t)
fmt.Printf("%#v\n", t)
fmt.Printf("%#v\n", timeZone)

Will print

&{7 -2.35 abc   def}
&{a:7 b:-2.35 c:abc     def}
&main.T{a:7, b:-2.35, c:"abc\tdef"}
map[string]int{"CST":-21600, "EST":-18000, "MST":-25200, "PST":-28800, "UTC":0}

(please note the & sign in it) when a string or [] byte value is encountered, you can use% Q to generate a quoted string; The format%#q will use backquotes whenever possible. (%q format can also be used for integers and runes, which produces a single quoted Rune constant.) In addition,% X can be used for strings, byte arrays, and integers to generate a long hexadecimal string, while the format with spaces (% x) also inserts spaces between bytes.

Another useful format is% T, which prints the type of a value.

fmt.Printf("%T\n", timeZone)

Can print

map[string]int

If you want to control the default format of a custom type, just define a method with String() string signature for the type. For our simple type T, you can do the following.

func (t *T) String() string {
    return fmt.Sprintf("%d/%g/%q", t.a, t.b, t.c)
}
fmt.Printf("%v\n", t)

The following format will be printed:

7/-2.35/"abc\tdef"

(if you need to print the value of type T like a pointer to T, the receiver of String must be of value type; in the above example, the receiver is a pointer, because it is more efficient and general for structure. See the section pointer vs. value receiver for more details.)

Our String method can also call Sprintf because the print routine can be completely reentrant and encapsulated in this way. However, there is one important detail you need to know: do not call Sprintf to construct a String method, because it will recurse your String method infinitely. This happens if the Sprintf call attempts to print the receiver directly as a String that will call the method again. This is a common error, as shown in this example.

type MyString string

func (m MyString) String() string {
    return fmt.Sprintf("MyString=%s", m) // Error: infinite recursion
}

The solution to this problem is also simple: convert the argument to a basic string type, which does not have this method.

type MyString string
func (m MyString) String() string {
    return fmt.Sprintf("MyString=%s", string(m)) // Yes: note the conversion
}

In the initialization section, we'll see another technique to avoid this recursion.

Another printing technique is to pass the arguments of a printing routine directly into another such routine. Printf's signature uses the... interface {} type for its last argument, so that any number and type of formal parameters can appear after the format.

func Printf(format string, v ...interface{}) (n int, err error) {

In the Printf function, v looks more like a variable of type [] interface {}, but if it is passed to another variable parameter function, it is like a regular argument list. The following is the log we used before Implementation of println. It passes its arguments directly to FMT Sprintln does the actual formatting.

// Println via FMT Print the log to the standard recorder by println
func Println(v ...interface{}) {
    std.Output(2, fmt.Sprintln(v...))  // Output takes parameters (int, string)
}

In this Sprintln nested call, we write... After v to tell the compiler to treat v as a list of arguments, otherwise it will pass v as a single slice argument.

There are many knowledge points about printing that are not mentioned. For details, please refer to godoc's documentation on fmt package.

Incidentally, the... Parameter can specify a specific type, for example, select the function min with the minimum value from the integer list, and its formal parameter can be of type... int.

func Min(a ...int) int {
    min := int(^uint(0) >> 1)  // Maximum int
    for _, i := range a {
        if i < min {
            min = i
        }
    }
    return min
}

Topics: Go