Advanced Go Features(Error handling & Pattern matching and regular expressions)

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.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值