Skip to content

Latest commit

 

History

History
939 lines (653 loc) · 29.3 KB

File metadata and controls

939 lines (653 loc) · 29.3 KB

三、处理数字

本章中的配方包括:

  • 将字符串转换为数字
  • 比较浮点数
  • 四舍五入浮点数
  • 浮点算法
  • 格式化数字
  • 二进制、八进制、十进制和十六进制之间的转换
  • 使用正确的复数进行格式化
  • 生成随机数
  • 运算复数
  • 在度和弧度之间转换
  • 取对数
  • 生成校验和

介绍

数字通常是每个应用程序打印格式化数字、转换基本表示等过程中不可避免的一部分。本章介绍了许多您通常可以处理的操作。

检查 Go 是否正确安装。第一章与环境互动检索戈朗版配方中的准备部分将帮助您。

将字符串转换为数字

此配方将向您展示如何将包含数字的字符串转换为数字类型(整型或浮点值)。

怎么做。。。

  1. 打开控制台,创建文件夹chapter03/recipe01
  2. 导航到该目录。
  3. 创建具有以下内容的main.go文件:
        package main

        import (
          "fmt"
          "strconv"
        )

        const bin = "00001"
        const hex = "2f"
        const intString = "12"
        const floatString = "12.3"

        func main() {

          // Decimals
          res, err := strconv.Atoi(intString)
          if err != nil {
            panic(err)
          }
          fmt.Printf("Parsed integer: %d\n", res)

          // Parsing hexadecimals
          res64, err := strconv.ParseInt(hex, 16, 32)
          if err != nil {
            panic(err)
          }
          fmt.Printf("Parsed hexadecima: %d\n", res64)

          // Parsing binary values
          resBin, err := strconv.ParseInt(bin, 2, 32)
          if err != nil {
            panic(err)
          }
          fmt.Printf("Parsed bin: %d\n", resBin)

          // Parsing floating-points
          resFloat, err := strconv.ParseFloat(floatString, 32)
          if err != nil {
            panic(err)
          }
          fmt.Printf("Parsed float: %.5f\n", resFloat)

        }
  1. 在终端执行go run main.go命令。
  2. 您将看到以下输出:

它是如何工作的。。。

前面示例代码中的主要函数是包strconvParseInt函数。该函数由三个参数调用:输入、输入基数和位大小。基数决定如何解析数字。请注意,十六进制的基数(第二个参数)为 16,二进制的基数为 2。包strconv的函数Atoi实际上是以 10 为基数的ParseInt函数。

ParseFloat函数将字符串转换为浮点数。第二个参数是bitSize的精度。bitSize = 64将导致float64bitSize = 32将导致float64,但它可以在不改变其值的情况下转换为float32

比较浮点数

由于浮点数的表示方式,在比较两个看似相同的数字时可能会出现不一致。与整数不同,IEEE 浮点数只是近似值。需要将数字转换成计算机可以存储在二进制中的形式,这会导致较小的精度或舍入偏差。例如,值 1.3 可以表示为 1.299999999。比较可以有一定的公差。要以任意精度比较数字,big包在这里。

怎么做。。。

  1. 打开控制台,创建文件夹chapter03/recipe02
  2. 导航到该目录。
  3. 创建具有以下内容的tolerance.go文件:
        package main

        import (
          "fmt"
          "math"
        )

        const da = 0.29999999999999998889776975374843459576368331909180
        const db = 0.3

        func main() {

          daStr := fmt.Sprintf("%.10f", da)
          dbStr := fmt.Sprintf("%.10f", db)

          fmt.Printf("Strings %s = %s equals: %v \n", daStr,
                     dbStr, dbStr == daStr)
          fmt.Printf("Number equals: %v \n", db == da)

          // As the precision of float representation
          // is limited. For the float comparison it is
          // better to use comparison with some tolerance.
          fmt.Printf("Number equals with TOLERANCE: %v \n", 
                     equals(da, db))

        }

        const TOLERANCE = 1e-8
        // Equals compares the floating-point numbers
        // with tolerance 1e-8
        func equals(numA, numB float64) bool {
          delta := math.Abs(numA - numB)
          if delta < TOLERANCE {
            return true
          }
          return false
        }
  1. 在终端执行go run tolerance.go命令。
  2. 您将看到以下输出:

  1. 创建具有以下内容的文件big.go
        package main

        import (
          "fmt"
          "math/big"
        )

        var da float64 = 0.299999992
        var db float64 = 0.299999991

        var prec uint = 32
        var prec2 uint = 16

        func main() {

          fmt.Printf("Comparing float64 with '==' equals: %v\n", da == db)

          daB := big.NewFloat(da).SetPrec(prec)
          dbB := big.NewFloat(db).SetPrec(prec)

          fmt.Printf("A: %v \n", daB)
          fmt.Printf("B: %v \n", dbB)
          fmt.Printf("Comparing big.Float with precision: %d : %v\n",
                     prec, daB.Cmp(dbB) == 0)

          daB = big.NewFloat(da).SetPrec(prec2)
          dbB = big.NewFloat(db).SetPrec(prec2)

          fmt.Printf("A: %v \n", daB)
          fmt.Printf("B: %v \n", dbB)
          fmt.Printf("Comparing big.Float with precision: %d : %v\n",
                     prec2, daB.Cmp(dbB) == 0)

        }
  1. 在终端运行go run big.go执行代码。
  2. 您将看到以下输出:

它是如何工作的。。。

第一种不使用任何内置包(步骤1-5)的浮点数比较方法需要使用所谓的EPSILON常量。这是选择两个数之间的足够小的δ(差)来考虑值相等的值。δ常数可能在 1e-8 量级,这通常足够精确。

第二个选项更复杂,但对于进一步处理浮点数也更有用。软件包math/big提供Float类型,可针对给定精度进行配置。该软件包的优点是,其精度可能远高于float64型的精度。出于说明目的,小精度值用于显示给定精度下的舍入和比较。

请注意,使用 16 位精度时,dadb数字相等,而使用 32 位精度时不相等。可从big.MaxPrec常数获得最大配置精度。

四舍五入浮点数

必须正确地将浮点数舍入为整数或特定精度。最常见的错误是将浮点型 AutoT0Ep 转换为整数类型,并将其视为处理好。

例如,可以将数字 3.9999 转换为整数,并期望它成为值为 4 的整数。实际结果是 3。在编写本书时,Go(1.9.2)的当前版本不包含Round功能。然而,在版本 1.10 中,Round功能已经在math包中实现。

怎么做。。。

  1. 打开控制台,创建文件夹chapter03/recipe03
  2. 导航到该目录。
  3. 创建具有以下内容的round.go文件:
        package main

        import (
          "fmt"
          "math"
        )

        var valA float64 = 3.55554444

        func main() {

          // Bad assumption on rounding
          // the number by casting it to
          // integer.
          intVal := int(valA)
          fmt.Printf("Bad rounding by casting to int: %v\n", intVal)

          fRound := Round(valA)
          fmt.Printf("Rounding by custom function: %v\n", fRound)

        }

        // Round returns the nearest integer.
        func Round(x float64) float64 {
          t := math.Trunc(x)
          if math.Abs(x-t) >= 0.5 {
            return t + math.Copysign(1, x)
          }
          return t
        }
  1. 在终端运行go run round.go执行代码。
  2. 您将看到以下输出:

它是如何工作的。。。

将浮点转换为整数实际上只是截断浮点值。假设值 2 表示为 1.999999;在本例中,输出为 1,这不是您所期望的。

舍入浮点数的正确方法是使用还考虑小数部分的函数。常用的四舍五入方法是从零开始减半(也称为商业四舍五入)。简单来说,如果数字包含小数部分大于或等于 0.5 的绝对值,则该数字向上舍入,否则向下舍入。

**在函数Round中,包math的函数Trunc截断数字的小数部分。然后,提取数字的小数部分。如果该值超过 0.5 的限制,则添加与整数值具有相同符号的值 1。

Go 版本 1.10 使用了示例中提到的功能的更快实现。在 1.10 版中,您只需调用math.Round函数即可获得整数。

浮点算法

正如前面的配方中所述,浮点数的表示也使算法复杂化。一般来说,内置float64上的操作就足够了。如果需要更高的精度,math/big包将发挥作用。这个食谱将告诉你如何处理这个问题。

怎么做。。。

  1. 打开控制台,创建文件夹chapter03/recipe04
  2. 导航到该目录。
  3. 创建具有以下内容的main.go文件:
        package main

        import (
          "fmt"
          "math/big"
        )

        const PI = `3.1415926535897932384626433832795028841971693
                    993751058209749445923078164062862089986280348253
                    421170679821480865132823066470938446095505822317
                    253594081284811174502841027019385211055596446229
                    4895493038196`
        const diameter = 3.0
        const precision = 400

        func main() {

          pi, _ := new(big.Float).SetPrec(precision).SetString(PI)
          d := new(big.Float).SetPrec(precision).SetFloat64(diameter)

          circumference := new(big.Float).Mul(pi, d)

          pi64, _ := pi.Float64()
          fmt.Printf("Circumference big.Float = %.400f\n",
                     circumference)
          fmt.Printf("Circumference float64 = %.400f\n", pi64*diameter)

          sum := new(big.Float).Add(pi, pi)
          fmt.Printf("Sum = %.400f\n", sum)

          diff := new(big.Float).Sub(pi, pi)
          fmt.Printf("Diff = %.400f\n", diff)

          quo := new(big.Float).Quo(pi, pi)
          fmt.Printf("Quocient = %.400f\n", quo)

        }
  1. 在终端运行go run main.go执行代码。
  2. 您将看到以下输出:

它是如何工作的。。。

big软件包支持高精度浮点运算。前面的示例演示了对数字的基本操作。请注意,代码将该操作与float64类型和big.Float类型进行比较。

通过高精度处理数字,使用big.Float类型至关重要。当big.Float转换回内置float64类型时,高精度丢失

还有更多。。。

大的包包含更多的Float类型的操作。参见文件(https://golang.org/pkg/math/big/#Float 有关更多详细信息,请参阅此软件包的

另见

浮点数的比较和舍入在比较浮点数舍入浮点数配方中提到。

格式化数字

如果将数字转换为字符串,则通常需要对其进行合理的格式化。数字的格式表示数字是用给定的数字打印的,由数字和小数组成。也可以选择值的表示形式。然而,与此密切相关的一个问题是数字格式的本地化。例如,某些语言使用逗号分隔的零。

怎么做。。。

  1. 打开控制台,创建文件夹chapter03/recipe05
  2. 导航到该目录。
  3. 创建具有以下内容的format.go文件:
        package main

        import (
          "fmt"
        )

        var integer int64 = 32500
        var floatNum float64 = 22000.456

        func main() {

          // Common way how to print the decimal
          // number
          fmt.Printf("%d \n", integer)

          // Always show the sign
          fmt.Printf("%+d \n", integer)

          // Print in other base X -16, o-8, b -2, d - 10
          fmt.Printf("%X \n", integer)
          fmt.Printf("%#X \n", integer)

          // Padding with leading zeros
          fmt.Printf("%010d \n", integer)

          // Left padding with spaces
          fmt.Printf("% 10d \n", integer)

          // Right padding
          fmt.Printf("% -10d \n", integer)

          // Print floating
          // point number
          fmt.Printf("%f \n", floatNum)

          // Floating-point number
          // with limited precision = 5
          fmt.Printf("%.5f \n", floatNum)

          // Floating-point number
          // in scientific notation
          fmt.Printf("%e \n", floatNum)

          // Floating-point number
          // %e for large exponents
          // or %f otherwise
          fmt.Printf("%g \n", floatNum)

        }
  1. 在主终端运行go run format.go执行代码。
  2. 您将看到以下输出:

  1. 创建具有以下内容的文件localized.go
        package main

        import (
          "golang.org/x/text/language"
          "golang.org/x/text/message"
        )

        const num = 100000.5678

        func main() {
          p := message.NewPrinter(language.English)
          p.Printf(" %.2f \n", num)

          p = message.NewPrinter(language.German)
          p.Printf(" %.2f \n", num)
        }
  1. 在主终端运行go run localized.go执行代码。
  2. 您将看到以下输出:

它是如何工作的。。。

代码示例显示了最常用的整数和浮点数选项。

Go 中的格式来自 C 的printf函数。所谓的verbs用于定义数字的格式。例如,动词可以是%X,它实际上是值的占位符。

除了基本的格式之外,还有一些与当地习俗相关的格式规则。对于格式,根据区域设置,golang.org/x/text/message包可能会有所帮助。请参阅本配方中的第二个代码示例。这样,就可以对数字格式进行本地化。

还有更多。。。

有关所有格式选项,请参阅fmt包。strconv软件包在您希望在不同的基础上格式化数字时也很有用。下面的配方描述了数字转换的可能性,但作为副作用,提供了如何在不同的基础上格式化数字的选项。

二进制、八进制、十进制和十六进制之间的转换

在某些情况下,整数值可以用十进制以外的表示形式表示。使用strconv包可以轻松地完成这些表示之间的转换。

怎么做。。。

  1. 打开控制台,创建文件夹chapter03/recipe06
  2. 导航到该目录。
  3. 创建具有以下内容的convert.go文件:
        package main

        import (
          "fmt"
          "strconv"
        )

        const bin = "10111"
        const hex = "1A"
        const oct = "12"
        const dec = "10"
        const floatNum = 16.123557

        func main() {

          // Converts binary value into hex
          v, _ := ConvertInt(bin, 2, 16)
          fmt.Printf("Binary value %s converted to hex: %s\n", bin, v)

          // Converts hex value into dec
          v, _ = ConvertInt(hex, 16, 10)
          fmt.Printf("Hex value %s converted to dec: %s\n", hex, v)

          // Converts oct value into hex
          v, _ = ConvertInt(oct, 8, 16)
          fmt.Printf("Oct value %s converted to hex: %s\n", oct, v)

          // Converts dec value into oct
          v, _ = ConvertInt(dec, 10, 8)
          fmt.Printf("Dec value %s converted to oct: %s\n", dec, v)

          //... analogically any other conversion
          // could be done.

        }

        // ConvertInt converts the given string value of base
        // to defined toBase.
        func ConvertInt(val string, base, toBase int) (string, error) {
          i, err := strconv.ParseInt(val, base, 64)
          if err != nil {
            return "", err
          }
          return strconv.FormatInt(i, toBase), nil
        }
  1. 在主终端运行go run convert.go执行代码。
  2. 您将看到以下输出:

它是如何工作的。。。

strconv包提供了ParseIntFormatInt两个函数,可以说它们是互补函数。函数ParseInt能够解析任何基表示形式的整数。另一方面,函数FormatInt可以将整数格式化为任何给定的基

最后,可以将整数的字符串表示形式解析为内置的int64类型,然后将解析后的整数字符串格式化为给定的基表示形式。

使用正确的复数进行格式化

当为用户显示消息时,如果句子更人性化,交互会更愉快。Go 包golang.org/x/text是扩展包,包含以正确方式格式化复数的此功能。

准备

如果您还没有扩展包,执行go get -x golang.org/x/text获取扩展包。

怎么做。。。

  1. 打开控制台,创建文件夹chapter03/recipe07
  2. 导航到该目录。
  3. 创建具有以下内容的plurals.go文件:
        package main

        import (
          "golang.org/x/text/feature/plural"
          "golang.org/x/text/language"
          "golang.org/x/text/message"
        )

        func main() {

          message.Set(language.English, "%d items to do",
            plural.Selectf(1, "%d", "=0", "no items to do",
              plural.One, "one item to do",
              "<100", "%[1]d items to do",
              plural.Other, "lot of items to do",
          ))

          message.Set(language.English, "The average is %.2f",
            plural.Selectf(1, "%.2f",
              "<1", "The average is zero",
              "=1", "The average is one",
              plural.Other, "The average is %[1]f ",
          ))

          prt := message.NewPrinter(language.English)
          prt.Printf("%d items to do", 0)
          prt.Println()
          prt.Printf("%d items to do", 1)
          prt.Println()
          prt.Printf("%d items to do", 10)
          prt.Println()
          prt.Printf("%d items to do", 1000)
          prt.Println()

          prt.Printf("The average is %.2f", 0.8)
          prt.Println()
          prt.Printf("The average is %.2f", 1.0)
          prt.Println()
          prt.Printf("The average is %.2f", 10.0)
          prt.Println()

        }
  1. 在主终端运行go run plurals.go执行代码。

  2. 您将看到以下输出:

它是如何工作的。。。

golang.org/x/text/message包含函数NewPrinter,该函数接受语言标识并创建格式化的 I/O,与fmt包相同,但具有基于性别和复数形式翻译消息的能力。

message包的Set功能增加了翻译和复数选择。复数形式本身是根据通过Selectf函数设置的规则选择的。Selectf函数根据plural.Form或选择器生成具有规则的catalog.Message类型。

前面的示例代码使用plural.Oneplural.Other表单以及=x, <x选择器。这些与格式动词%d匹配(也可以使用其他动词)。选择第一个匹配案例。

还有更多。。。

有关选择器和表单的更多信息,请参阅golang.org/x/text/message包的文档。

生成随机数

这个食谱展示了如何生成随机数。此功能由math/rand包提供。math/rand生成的随机数被认为是加密不安全的,因为序列在给定种子下是可重复的。

要生成加密安全的数字,应使用crypto/rand包。这些序列是不可重复的。

怎么做。。。

  1. 打开控制台,创建文件夹chapter03/recipe08
  2. 导航到该目录。
  3. 创建具有以下内容的rand.go文件:
        package main

        import (
          crypto "crypto/rand"
          "fmt"
          "math/big"
          "math/rand"
        )

        func main() {

          sec1 := rand.New(rand.NewSource(10))
          sec2 := rand.New(rand.NewSource(10))
          for i := 0; i < 5; i++ {
            rnd1 := sec1.Int()
            rnd2 := sec2.Int()
            if rnd1 != rnd2 {
              fmt.Println("Rand generated non-equal sequence")
              break
            } else {
              fmt.Printf("Math/Rand1: %d , Math/Rand2: %d\n", rnd1, rnd2)
            }
          }

          for i := 0; i < 5; i++ {
            safeNum := NewCryptoRand()
            safeNum2 := NewCryptoRand()
            if safeNum == safeNum2 {
              fmt.Println("Crypto generated equal numbers")
              break
            } else {
              fmt.Printf("Crypto/Rand1: %d , Crypto/Rand2: %d\n",
                         safeNum, safeNum2)
            }
          }
        }

        func NewCryptoRand() int64 {
          safeNum, err := crypto.Int(crypto.Reader, big.NewInt(100234))
          if err != nil {
            panic(err)
          }
          return safeNum.Int64()
        }
  1. 在主终端运行go run rand.go执行代码。
  2. 您将看到以下输出:

它是如何工作的。。。

前面的代码提供了两种生成随机数的方法。第一个选项使用math/rand包,这是加密不安全的,允许我们使用具有相同种子编号的Source生成相同序列。这种方法通常用于测试。这样做的原因是为了序列的再现性。

第二种选择是使用crypto/rand包,即加密安全的选择。API 使用Reader提供加密强伪随机生成器的实例。包本身有默认的Reader,通常基于基于系统的随机数生成器。

运算复数

复数通常用于科学应用和计算。Go 将复数实现为基本类型。复数的具体操作是math/cmplx包的一部分。

怎么做。。。

  1. 打开控制台,创建文件夹chapter03/recipe09
  2. 导航到该目录。
  3. 创建具有以下内容的complex.go文件:
        package main

        import (
          "fmt"
          "math/cmplx"
        )

        func main() {

          // complex numbers are
          // defined as real and imaginary
          // part defined by float64
          a := complex(2, 3)

          fmt.Printf("Real part: %f \n", real(a))
          fmt.Printf("Complex part: %f \n", imag(a))

          b := complex(6, 4)

          // All common
          // operators are useful
          c := a - b
          fmt.Printf("Difference : %v\n", c)
          c = a + b
          fmt.Printf("Sum : %v\n", c)
          c = a * b
          fmt.Printf("Product : %v\n", c)
          c = a / b
          fmt.Printf("Product : %v\n", c)

          conjugate := cmplx.Conj(a)
          fmt.Println("Complex number a's conjugate : ", conjugate)

          cos := cmplx.Cos(b)
          fmt.Println("Cosine of b : ", cos)

        }
  1. 在主终端运行go run complex.go执行代码。
  2. 您将看到以下输出:

它是如何工作的。。。

基本运算符是为基元类型complex实现的。关于复数的其他操作由math/cmplx包提供。如果需要高精度的操作,则不存在big实现

另一方面,复数可以实现为实数,虚部用big.Float类型表示。

在度和弧度之间转换

三角运算和几何操作通常以弧度进行;将这些转换为度总是很有用的,反之亦然。本食谱将向您展示如何处理这些单位之间转换的一些技巧。

怎么做。。。

  1. 打开控制台,创建文件夹chapter03/recipe10
  2. 导航到该目录。
  3. 创建具有以下内容的radians.go文件:
        package main

        import (
          "fmt"
          "math"
        )

        type Radian float64

        func (rad Radian) ToDegrees() Degree {
          return Degree(float64(rad) * (180.0 / math.Pi))
        }

        func (rad Radian) Float64() float64 {
          return float64(rad)
        }

        type Degree float64

        func (deg Degree) ToRadians() Radian {
          return Radian(float64(deg) * (math.Pi / 180.0))
        }

        func (deg Degree) Float64() float64 {
          return float64(deg)
        }

        func main() {

          val := radiansToDegrees(1)
          fmt.Printf("One radian is : %.4f degrees\n", val)

          val2 := degreesToRadians(val)
          fmt.Printf("%.4f degrees is %.4f rad\n", val, val2)

          // Conversion as part
          // of type methods
          val = Radian(1).ToDegrees().Float64()
          fmt.Printf("Degrees: %.4f degrees\n", val)

          val = Degree(val).ToRadians().Float64()
          fmt.Printf("Rad: %.4f radians\n", val)
        }

        func degreesToRadians(deg float64) float64 {
          return deg * (math.Pi / 180.0)
        }

        func radiansToDegrees(rad float64) float64 {
          return rad * (180.0 / math.Pi)
        }
  1. 在主终端运行go run radians.go执行代码。
  2. 您将看到以下输出:

它是如何工作的。。。

Go 标准库不包含任何具有将弧度转换为度的功能的软件包,反之亦然。但至少 Pi 常量是math包的一部分,因此可以按照示例代码所示进行转换。

前面的代码还介绍了使用其他方法定义自定义类型的方法。这些都简化了 handy API 的值转换。

取对数

对数用于科学应用以及数据可视化和测量。内置的math包包含常用的对数基数。使用这些,你可以得到所有的基础。

怎么做。。。

  1. 打开控制台,创建文件夹chapter03/recipe11
  2. 导航到该目录。
  3. 创建具有以下内容的log.go文件:
        package main

        import (
          "fmt"
          "math"
        )

        func main() {

          ln := math.Log(math.E)
          fmt.Printf("Ln(E) = %.4f\n", ln)

          log10 := math.Log10(-100)
          fmt.Printf("Log10(10) = %.4f\n", log10)

          log2 := math.Log2(2)
          fmt.Printf("Log2(2) = %.4f\n", log2)

          log_3_6 := Log(3, 6)
          fmt.Printf("Log3(6) = %.4f\n", log_3_6)

        }

        // Log computes the logarithm of
        // base > 1 and x greater 0
        func Log(base, x float64) float64 {
          return math.Log(x) / math.Log(base)
        }
  1. 在主终端运行go run log.go执行代码。
  2. 您将看到以下输出:

它是如何工作的。。。

标准包math包含所有常用对数的函数,因此您可以轻松获得二进制、十进制和自然对数。参见Log函数,该函数通过助手定义的公式计算yx的任何对数:

标准库中对数的内部实现自然基于近似。此功能可以在$GOROOT/src/math/log.go文件中看到。

生成校验和

哈希或所谓的校验和是快速比较任何内容的最简单方法。此配方演示如何创建文件内容的校验和。出于演示目的,将使用 MD5 哈希函数。

怎么做。。。

  1. 打开控制台,创建文件夹chapter03/recipe12

  2. 导航到该目录。

  3. 创建具有以下内容的content.dat文件:

        This is content to check
  1. 创建具有以下内容的checksum.go文件:
        package main

        import (
          "crypto/md5"
          "fmt"
          "io"
          "os"
        )

        var content = "This is content to check"

        func main() {

          checksum := MD5(content)
          checksum2 := FileMD5("content.dat")

          fmt.Printf("Checksum 1: %s\n", checksum)
          fmt.Printf("Checksum 2: %s\n", checksum2)
          if checksum == checksum2 {
            fmt.Println("Content matches!!!")
          }

        }

        // MD5 creates the md5
        // hash for given content encoded in
        // hex string
        func MD5(data string) string {
          h := md5.Sum([]byte(data))
          return fmt.Sprintf("%x", h)
        }

        // FileMD5 creates hex encoded md5 hash
        // of file content
        func FileMD5(path string) string {
          h := md5.New()
          f, err := os.Open(path)
          if err != nil {
            panic(err)
          }
          defer f.Close()
          _, err = io.Copy(h, f)
          if err != nil {
            panic(err)
          }
          return fmt.Sprintf("%x", h.Sum(nil))
        }
  1. 在主终端运行go run checksum.go执行代码。
  2. 您将看到以下输出:

  1. 创建具有以下内容的sha_panic.go文件:
        package main

        import (
          "crypto"
        )

        func main() {
          crypto.SHA1.New()
        }
  1. 在主终端运行go run sha_panic.go执行代码。
  2. 您将看到以下输出:

它是如何工作的。。。

crypto包包含著名散列函数的实现。MD5散列函数位于crypto/md5包中。crypto包中的每个哈希函数实现Hash接口,注意Hash包含Write方法。使用Write方法,它可以用作Writer。这可以在FileMD5功能中看到。HashSum方法接受 byte slice 的参数,由此产生的散列应该放在这里。

当心这个。Sum方法不计算参数的散列,而是将散列计算到参数中。

另一方面,package 函数md5.Sum可以直接用于生成散列。在这种情况下,Sum函数的参数是计算出的散列值中的参数。

当然,crypto包也实现了SHA变体和其他哈希函数。它们通常以相同的方式使用。散列函数可以通过crypto包常量crypto.Hash(例如crypto.MD5.New())访问,但这样,具有给定函数的包也必须链接到内置二进制文件(可以使用空白导入,import _ "crypto/md5"),否则对New的调用将死机。

hash包本身包含 CRC 校验和等。**