rot13 加密

##

rot13 算法将每个字母按如下图所示的规则替换成另外的字母

有种常见的模式是一个 io.Reader 包装另一个 io.Reader ,然后通过某种方式修改其数据流。 rot13Reader 包装了 io.Reader, 然后从中读入数据流,通过 rot13 算法将数据流加密。

代码实现

package main

import (
	"io"
	"os"
	"strings"
)

func rot13(b byte) byte {
	var a, z byte
	switch {
	case 'a' <= b && b <= 'z':
		a, z = 'a', 'z'
	case 'A' <= b && b <= 'Z':
		a, z = 'A', 'Z'
	default:
		return b
	}
	return (b-a+13)%(z-a+1) + a
}

type rot13Reader struct {
	r io.Reader
}

func (r rot13Reader) Read(p []byte) (n int, err error) {
	n, err = r.r.Read(p)
	for i := 0; i < n; i++ {
		p[i] = rot13(p[i])
	}
	return
}

func main() {
	s := strings.NewReader(
		"Lbh penpxrq gur pbqr!")
	r := rot13Reader{s}
	io.Copy(os.Stdout, &r)
}

运行结果

You cracked the code!

用tr命令实现 rot13 加密

echo "Lbh penpxrq gur pbqr" | tr 'A-Za-z' 'N-ZA-Mn-za-m'

用牛顿法实现平方根函数

算法原理

牛顿法是通过选择一个起点 z 然后重复以下过程来求 Sqrt(x) 的近似值:

递推公式

z -= (z*z - x) / (2 * z)

迭代一定次数之后,z 的值就是 Sqrt(x) 的近似值

详细算法原理可以看:如何通俗易懂地讲解牛顿迭代法

Go 语言实现

第一种实现,迭代5次后停止循环

package main

import (
	"fmt"
	"math"
)

func Sqrt(x float64) float64 {
	z := 1.0
	for i := 0; i < 5; i++ {
		z -= (z*z - x) / (2 * z)
	}
	return z
}

func main() {
	n := 2.0
	fmt.Println(Sqrt(n))
	fmt.Println(math.Sqrt(n))
}

迭代5次的运行结果

1.4142135623730951
1.4142135623730951

迭代到前后值相差为 1e-6 时,停止迭代

package main

import (
	"fmt"
	"math"
)

const delta = 1e-6

func Sqrt(x float64) float64 {
	z := x
	n := 0.0
	for math.Abs(n-z) > delta {
		n, z = z, z-(z*z-x)/(2*z)
	}
	return z
}

func main() {
	const x = 2
	mine, theirs := Sqrt(x), math.Sqrt(x)
	fmt.Println(mine, theirs, mine-theirs)
}

实现 error 接口

## 创建一个新的类型 ErrNegativeSqrt , 它实现 Error 方法 注意: 在 Error 方法内调用 fmt.Sprint(e) 会让程序陷入死循环。可以通过先转换 e 来避免这个问题:fmt.Sprint(float64(e))

代码实现

package main

import (
	"fmt"
)

type ErrNegativeSqrt float64

func (e ErrNegativeSqrt) Error() string {
	return fmt.Sprintf("cannot Sqrt negative number: %f", float64(e))
}

func Sqrt(x float64) (float64, error) {
	if x < 0 {
		return 0, ErrNegativeSqrt(x)
	}
	z := 1.0
	for i := 0; i < 5; i++ {
		z -= (z*z - x) / (2 * z)
	}
	return z, nil
}

func main() {
	fmt.Println(Sqrt(2))
	fmt.Println(Sqrt(-2))
}

运行结果

1.4142135623730951 <nil>
0 cannot Sqrt negative number: -2.000000

io.Reader 接口

介绍

io 包指定了 io.Reader 接口, 它表示从数据流的末尾进行读取。 Go 标准库包含了该接口的许多实现, 包括文件、网络连接、压缩和加密等等。 io.Reader 接口有一个 Read 方法:

func (T) Read(b []byte) (n int, err error)`

T 类型的方法 Read 用类型 T 的内部数据填充给定的字节切片 b 并返回填充的字节数和错误值。 在遇到数据流的结尾时,它会返回一个 io.EOF 错误。

示例代码

以下示例代码创建了一个 strings.Reader 并以每次 8 字节的速度读取它的输出。

package main

import (
	"fmt"
	"io"
	"strings"
)

func main() {
	r := strings.NewReader("Hello, Reader!")

	b := make([]byte, 8)
	for {
		n, err := r.Read(b)
		fmt.Printf("n = %v err = %v b = %v\n", n, err, b)
		fmt.Printf("b[:n] = %q\n", b[:n])
		if err == io.EOF {
			break
		}
	}
}

运行结果

n = 8 err = <nil> b = [72 101 108 108 111 44 32 82]
b[:n] = "Hello, R"
n = 6 err = <nil> b = [101 97 100 101 114 33 32 82]
b[:n] = "eader!"
n = 0 err = EOF b = [101 97 100 101 114 33 32 82]
b[:n] = ""

fmt包里的接口:Stringer

接口 Stringer

fmt 包中定义的 Stringer 是最普遍的接口之一。

type Stringer interface {
    String() string
}

Stringer 是一个可以用字符串描述自己的类型。fmt 包(还有很多包)都通过此接口来打印值。 通过让 IPAddr 类型实现 fmt.Stringer 来打印点号分隔的地址。 例如,IPAddr{1, 2, 3, 4} 应当打印为 “1.2.3.4” 。

实现

package main

import "fmt"

type IPAddr [4]byte

func (i IPAddr) String() string {
	return fmt.Sprintf("%d.%d.%d.%d",i[0],i[1],i[2],i[3])
}

func main() {
	hosts := map[string]IPAddr{
		"loopback":  {127, 0, 0, 1},
		"googleDNS": {8, 8, 8, 8},
	}
	for name, ip := range hosts {
		fmt.Printf("%v: %v\n", name, ip)
	}
}

运行结果

loopback: 127.0.0.1
googleDNS: 8.8.8.8