About advanced Go features, including:
- Error handling
- Error logging
- Pattern matching and regular expressions
- Reflection
- How to user strace(1) and dtrace(1) tools to watch the system calls of Go executable files.
- How to detect unreachable Go code
- How to avoid various common Go mistakes.
Error handling in Go
Go has a special data type called error that helps signify errorneous states; if an error variable has a nil value, then there is no error situation.
you can ignore the error vairable that is returned by most Go functions using the _ character:

File Input and Output , even End of File(EOF) is a type of error that is returned when there is nothing left to read from a file. As EOF is defined in the io package, you can handle it as follow
if err == io.EOF {
// Do something
}
Functions can return error variables
An error condition can be handled inside a function,outside of a function, or both inside and outside the function.
The relevant Go code can be found in funErr.go :
[maxwell@MaxwellDBA AdvancedGoFeatures]$ vim funErr.go
[maxwell@MaxwellDBA AdvancedGoFeatures]$ go run funErr.go
The result is 1
The result is 2
There is a remainder!
2022/12/11 20:44:44 Cannot divide by zero!
exit status 1
[maxwell@MaxwellDBA AdvancedGoFeatures]$ cat funErr.go
package main
import (
"errors"
"fmt"
"log"
)
func division(x, y int) (int, error, error){
if y == 0 {
return 0, nil, errors.New("Cannot divide by zero!")
}
if x%y != 0 {
remainder := errors.New("There is a remainder!")
return x / y, remainder,nil
} else {
return x / y, nil, nil
}
}
func main() {
result, rem, err := division(2, 2)
if err != nil {
log.Fatal(err)
} else {
fmt.Println("The result is", result)
}
if rem != nil {
fmt.Println(rem)
}
result, rem, err = division(12, 5)
if err != nil {
log.Fatal(err)
} else {
fmt.Println("The result is", result)
}
if rem != nil {
fmt.Println(rem)
}
result, rem, err = division(2, 0)
if err != nil {
log.Fatal(err)
} else {
fmt.Println("The result is", result)
}
if rem != nil {
fmt.Println(rem)
}
}
[maxwell@MaxwellDBA AdvancedGoFeatures]$
About error logging
Go offers functions that can help you log your error messages in various ways.
Generally speaking, log.Fatal() should be used instead of the os.Exit() function because it allows you to print an error message and exit your program using just one function call.
Go offers additional error logging functions in the log standard package that behave more gently depending on the situation, which includes :
- log.Printf()
- log.Print()
- log.Println()
- log.Fatalf()
- log.Fatalln()
- log.Panic()
- log.Panicln()
- log.Panicf()
Please note that logging functions can be handy for debugging purposes so do not underestimate their power.
[maxwell@MaxwellDBA AdvancedGoFeatures]$ vim logging.go
[maxwell@MaxwellDBA AdvancedGoFeatures]$ go run logging.go
2022/12/11 21:02:25 log.Print() function: 1
2022/12/11 21:02:25 log.Print() function: 2
2022/12/11 21:02:25 log.Panicf function: 3
panic: log.Panicf function: 3
goroutine 1 [running]:
panic
:0
log.Panicf
:0
main.main
/home/maxwell/goproject/AdvancedGoFeatures/logging.go:14
__start_context
:0
exit status 2
[maxwell@MaxwellDBA AdvancedGoFeatures]$ cat logging.go
package main
import (
"log"
)
func main() {
x := 1
log.Printf("log.Print() function: %d", x)
x = x + 1
log.Printf("log.Print() function: %d", x)
x = x + 1
log.Panicf("log.Panicf function: %d", x)
x = x + 1
log.Printf("log.Print() function: %d", x)
}
[maxwell@MaxwellDBA AdvancedGoFeatures]$
Although the log.Printf() function works in the same way as fmt.Printf(), it automatically prints the date and time the log message was printed, just like the log.Fatal() function did in funErr.go. Additionally, the log.Panicf()function works in a similar way to log.Fatal()--they both terminate the current program. However, log.Panicf() prints some additional information,useful for debugging purposes.
Go also offers the log/syslog package that is a simple interface to the system log service running on your Unix machine.
The addCLA.go program revisited
[maxwell@MaxwellDBA goproject]$ vim addCLAImproved.go
[maxwell@MaxwellDBA goproject]$ diff addCLAImproved.go addCLA.go
13,14c13
< temp, err := strconv.Atoi(arguments[i])
< if err == nil {
---
> temp,_ := strconv.Atoi(arguments[i])
16,18d14
< } else {
< fmt.Println("Ignoring", arguments[i])
< }
[maxwell@MaxwellDBA goproject]$ vim addCLAImproved.go
[maxwell@MaxwellDBA goproject]$ diff addCLAImproved.go addCLA.go
13,18c13,14
< temp, err := strconv.Atoi(arguments[i])
< if err == nil {
< sum = sum + temp
< } else {
< fmt.Println("Ignoring", arguments[i])
< }
---
> temp,_ := strconv.Atoi(arguments[i])
> sum = sum + temp
[maxwell@MaxwellDBA goproject]$ cat addCLAImproved.go
package main
import (
"fmt"
"os"
"strconv"
)
func main() {
arguments := os.Args
sum := 0
for i := 1; i < len(arguments); i++ {
temp, err := strconv.Atoi(arguments[i])
if err == nil {
sum = sum + temp
} else {
fmt.Println("Ignoring", arguments[i])
}
}
fmt.Println("Sum:", sum)
}
[maxwell@MaxwellDBA goproject]$ go run addCLAImproved.go
Sum: 0
[maxwell@MaxwellDBA goproject]$ go run addCLAImproved.go 1 2 -3
Sum: 0
[maxwell@MaxwellDBA goproject]$ go run addCLAImproved.go 1 a 2 b 3.2 @
Ignoring a
Ignoring b
Ignoring 3.2
Ignoring @
Sum: 3
[maxwell@MaxwellDBA goproject]$
The new and improved version works as expected , behave reliably, and allows us to differentiate between valid and invalid input.
Pattern matching and regular expressions
Pattern matching , which plays a key role in Go, is a technique for searching a string for a set of characters based on a specific search pattern that is based on regular expressions.
Once pattern matching is successful ,it allows you to extract the desired data from the string or replace or delete it.
Regular expressions and pattern matching are not a panacea, so you should not try to solve every problem using regular expressions since they are not suitable for every kind of problem you may come up against. Furthermore, they might introduce unnecessary complexity to your software.
The Go package responsible for the pattern matching capabilities of Go is called regexp. which you can see in action in regExp.go
[maxwell@MaxwellDBA Patternmatchingandregualrexpressions]$ vim regExp.go
[maxwell@MaxwellDBA Patternmatchingandregualrexpressions]$ go run regExp.go
true
false
true
true
false
MIHALIS MIHALIS
[maxwell@MaxwellDBA Patternmatchingandregualrexpressions]$ cat regExp.go
package main
import (
"fmt"
"regexp"
)
func main() {
match,_ := regexp.MatchString("Mihalis", "Mihalis Tsoukalos")
fmt.Println(match)
match,_ = regexp.MatchString("Tsoukalos", "Mihalis tsoukalos")
fmt.Println(match)
parse, err := regexp.Compile("[Mm]ihalis")
if err != nil {
fmt.Printf("Error compiling RE: %s\n", err)
} else {
fmt.Println(parse.MatchString("Mihalis Tsoukalos"))
fmt.Println(parse.MatchString("mihalis Tsoukalos"))
fmt.Println(parse.MatchString("M ihalis Tsoukalos"))
fmt.Println(parse.ReplaceAllString("mihalis Mihalis","MIHALIS"))
}
}
[maxwell@MaxwellDBA Patternmatchingandregualrexpressions]$
the first call to regexp.MatchString() was a match, but the second was not because pattern matching is case-sensitive and Tsoukalos does not match tsoukalos. The parse.ReplaceAllString() function at the end searches the string that is given as an input ("mihalis Mihalis") and replaces each match with the string that is given as its second parameter ("MIHALIS").
Printing all the values from a given column of a line
[maxwell@MaxwellDBA Patternmatchingandregualrexpressions]$ vim readColumn.go
[maxwell@MaxwellDBA Patternmatchingandregualrexpressions]$ go run readColumn.go
2
12
2
[maxwell@MaxwellDBA Patternmatchingandregualrexpressions]$ cat readColumn.go
package main
import (
"fmt"
"strings"
)
func main() {
var s [3]string
s[0] = "1 2 3"
s[1] = "11 12 13 14 15 16"
s[2] = "-1 2 -3 -4 -5 6"
column := 2
for i := 0; i < len(s); i++ {
data := strings.Fields(s[i])
if len(data) >= column {
fmt.Println((data[column-1]))
}
}
}
[maxwell@MaxwellDBA Patternmatchingandregualrexpressions]$
An important thing to remember is that you should never trust your data.Put simply, always verify that the data you expect to grab is there.
Creating summaries
[maxwell@MaxwellDBA creatingsummaries]$ vim summary.go
[maxwell@MaxwellDBA creatingsummaries]$ go run summary.go 0
Invalid column
exit status 1
[maxwell@MaxwellDBA creatingsummaries]$ go run summary.go 2
Invalid argument: b
Invalid argument: a
Sum: 2
[maxwell@MaxwellDBA creatingsummaries]$ go run summary.go 1
Sum: 11
[maxwell@MaxwellDBA creatingsummaries]$ cat summary.go
package main
import (
"fmt"
"os"
"strconv"
"strings"
)
func main() {
var s [3]string
s[0] = "1 b 3"
s[1] = "11 a 1 14 1 1"
s[2] = "-1 2 -3 -4 -5"
arguments := os.Args
column, err := strconv.Atoi(arguments[1])
if err != nil {
fmt.Println("Error reading argument")
os.Exit(-1)
}
if column == 0 {
fmt.Println("Invalid column")
os.Exit(1)
}
sum := 0
for i := 0; i < len(s); i++ {
data := strings.Fields(s[i])
if len(data) >= column {
temp, err := strconv.Atoi(data[column-1])
if err == nil {
sum = sum + temp
} else {
fmt.Printf("Invalid argument: %s\n",data[column-1])
}
} else {
fmt.Println("Invalid column!")
}
}
fmt.Printf("Sum: %d\n", sum)
}
[maxwell@MaxwellDBA creatingsummaries]$
Finding the number of occurrences
below code show how to finding out the number of times an IP address appears in a log file using a handy map structure.
[maxwell@MaxwellDBA findingthenumberofoccurrences]$ vim occurrences.go
[maxwell@MaxwellDBA findingthenumberofoccurrences]$ go run occurrences.go
1 -> 8
b -> 3
3 -> 1
a -> 6
11 -> 1
-1 -> 1
-4 -> 1
[maxwell@MaxwellDBA findingthenumberofoccurrences]$ go run occurrences.go | sort -n -r -t\ -k3,3
1 -> 8
a -> 6
b -> 3
-4 -> 1
3 -> 1
11 -> 1
-1 -> 1
[maxwell@MaxwellDBA findingthenumberofoccurrences]$ cat occurrences.go
package main
import (
"fmt"
"strings"
)
func main() {
var s [3]string
s[0] = "1 b 3 1 a a b"
s[1] = "11 a 1 1 1 1 a a"
s[2] = "-1 b 1 -4 a 1"
counts := make(map[string]int)
for i := 0; i < len(s); i++ {
data := strings.Fields(s[i])
for _, word := range data {
_, ok := counts[word]
if ok {
counts[word] = counts[word] + 1
} else {
counts[word] = 1
}
}
}
for key, _ := range counts {
fmt.Printf("%s -> %d \n", key, counts[key])
}
}
[maxwell@MaxwellDBA findingthenumberofoccurrences]$
Find and replace
The example below will search the provided text for two variations of a given string and replace it with another string.
[maxwell@MaxwellDBA findandreplace]$
[maxwell@MaxwellDBA findandreplace]$ vim findReplace.go
[maxwell@MaxwellDBA findandreplace]$ go run findReplace.go
1 C 3
11 a C 14 1 1
C 2 -3 C -5
[maxwell@MaxwellDBA findandreplace]$ cat findReplace.go
package main
import (
"fmt"
"os"
"regexp"
)
func main() {
var s [3]string
s[0] = "1 b 3"
s[1] = "11 a B 14 1 1"
s[2] = "b 2 -3 B -5"
parse, err := regexp.Compile("[bB]")
if err != nil {
fmt.Printf("Error compiling RE: %s\n", err)
os.Exit(-1)
}
for i := 0; i < len(s); i++ {
temp := parse.ReplaceAllString(s[i], "C")
fmt.Println(temp)
}
}
[maxwell@MaxwellDBA findandreplace]$
The awk(1) and sed(1) command-line tools can do most of the previous tasks more easily, but sed(1) and awk(1) are not general-purpose programming languages.

被折叠的 条评论
为什么被折叠?



