字符串函数
package main
import s "strings"
import "fmt"
// 我们给 fmt.Println 一个短名字的别名,我们随后将会经常用到。
var p = fmt.Println
// 标准库的 strings 包提供了很多有用的字符串相关的函数。
// 这里是一些用来让你对这个包有个初步了解的例子。
func main() {
// 这是一些 strings 中的函数例子。
// 注意他们都是包中的函数,不是字符串对象自身的方法,这意味着我们需要考虑在调用时传递字符作为第一个参数进行传递。
p("Contains: ", s.Contains("test", "es"))
p("Count: ", s.Count("test", "t"))
p("HasPrefix: ", s.HasPrefix("test", "te"))
p("HasSuffix: ", s.HasSuffix("test", "st"))
p("Index: ", s.Index("test", "e"))
p("Join: ", s.Join([]string{"a", "b"}, "-"))
p("Repeat: ", s.Repeat("a", 5))
p("Replace: ", s.Replace("foo", "o", "0", -1))
p("Replace: ", s.Replace("foo", "o", "0", 1))
p("Split: ", s.Split("a-b-c-d-e", "-"))
p("ToLower: ", s.ToLower("TEST"))
p("ToUpper: ", s.ToUpper("test"))
// 你可以在 strings包文档中找到更多的函数
p()
// 虽然不是 strings 的一部分,但是仍然值得一提的是获取字符串长度和通过索引获取一个字符的机制。
p("Len: ", len("hello"))
p("Char:", "hello"[1])
}
字符串格式化
package main
import "fmt"
import "os"
type point struct {
x, y int
}
func main() {
// Go 为常规 Go 值的格式化设计提供了多种打印方式。例如,这里打印了 point 结构体的一个实例。
p := point{1, 2}
fmt.Printf("%v\n", p)
// 如果值是一个结构体,%+v 的格式化输出内容将包括结构体的字段名。
fmt.Printf("%+v\n", p)
// %#v 形式则输出这个值的 Go 语法表示。例如,值的运行源代码片段。
fmt.Printf("%#v\n", p)
// 需要打印值的类型,使用 %T。
fmt.Printf("%T\n", p)
// 格式化布尔值是简单的。
fmt.Printf("%t\n", true)
// 格式化整形数有多种方式,使用 %d进行标准的十进制格式化。
fmt.Printf("%d\n", 123)
// 这个输出二进制表示形式。
fmt.Printf("%b\n", 14)
// 这个输出给定整数的对应字符。
fmt.Printf("%c\n", 33)
// %x 提供十六进制编码。
fmt.Printf("%x\n", 456)
// 对于浮点型同样有很多的格式化选项。使用 %f 进行最基本的十进制格式化。
fmt.Printf("%f\n", 78.9)
// %e 和 %E 将浮点型格式化为(稍微有一点不同的)科学技科学记数法表示形式。
fmt.Printf("%e\n", 123400000.0)
fmt.Printf("%E\n", 123400000.0)
// 使用 %s 进行基本的字符串输出。
fmt.Printf("%s\n", "\"string\"")
// 像 Go 源代码中那样带有双引号的输出,使用 %q。
fmt.Printf("%q\n", "\"string\"")
// 和上面的整形数一样,%x 输出使用 base-16 编码的字符串,每个字节使用 2 个字符表示。
fmt.Printf("%x\n", "hex this")
// 要输出一个指针的值,使用 %p。
fmt.Printf("%p\n", &p)
// 当输出数字的时候,你将经常想要控制输出结果的宽度和精度,可以使用在 % 后面使用数字来控制输出宽度。
// 默认结果使用右对齐并且通过空格来填充空白部分。
fmt.Printf("|%6d|%6d|\n", 12, 345)
// 你也可以指定浮点型的输出宽度,同时也可以通过 宽度.精度 的语法来指定输出的精度。
fmt.Printf("|%6.2f|%6.2f|\n", 1.2, 3.45)
// 要左对齐,使用 - 标志。
fmt.Printf("|%-6.2f|%-6.2f|\n", 1.2, 3.45)
// 你也许也想控制字符串输出时的宽度,特别是要确保他们在类表格输出时的对齐。
// 这是基本的右对齐宽度表示。
fmt.Printf("|%6s|%6s|\n", "foo", "b")
// 要左对齐,和数字一样,使用 - 标志。
fmt.Printf("|%-6s|%-6s|\n", "foo", "b")
// 到目前为止,我们已经看过 Printf了,它通过 os.Stdout输出格式化的字符串。
// Sprintf 则格式化并返回一个字符串而不带任何输出。
s := fmt.Sprintf("a %s", "string")
fmt.Println(s)
// 你可以使用 Fprintf 来格式化并输出到 io.Writers而不是 os.Stdout。
fmt.Fprintf(os.Stderr, "an %s\n", "error")
}
数字转换
package main
import "strconv"
import "fmt"
// 从字符串中解析数字在很多程序中是一个基础常见的任务,在Go 中是这样处理的。
func main() {
// 内置的 strconv 包提供了数字解析功能。
// 使用 ParseFloat 解析浮点数,这里的 64 表示表示解析的数的位数。
f, _ := strconv.ParseFloat("1.234", 64)
fmt.Println(f)
// 在使用 ParseInt 解析整形数时,例子中的参数 0 表示自动推断字符串所表示的数字的进制。
// 64 表示返回的整形数是以 64 位存储的。
i, _ := strconv.ParseInt("123", 0, 64)
fmt.Println(i)
// ParseInt 会自动识别出十六进制数。
d, _ := strconv.ParseInt("0x1c8", 0, 64)
fmt.Println(d)
// ParseUint 也是可用的。
u, _ := strconv.ParseUint("789", 0, 64)
fmt.Println(u)
// Atoi 是一个基础的 10 进制整型数转换函数。
k, _ := strconv.Atoi("135")
fmt.Println(k)
// 在输入错误时,解析函数会返回一个错误。
_, e := strconv.Atoi("wat")
fmt.Println(e)
}
时间函数
package main
import "fmt"
import "time"
func main() {
p := fmt.Println
// 得到当前时间。
now := time.Now()
p(now)
// 通过提供年月日等信息,你可以构建一个 time。时间总是关联着位置信息,例如时区。
then := time.Date(
2009, 11, 17, 20, 34, 58, 651387237, time.UTC)
p(then)
// 你可以提取出时间的各个组成部分。
p(then.Year())
p(then.Month())
p(then.Day())
p(then.Hour())
p(then.Minute())
p(then.Second())
p(then.Nanosecond())
p(then.Location())
// 输出是星期一到日的 Weekday 也是支持的。
p(then.Weekday())
// 这些方法来比较两个时间,分别测试一下是否是之前,之后或者是同一时刻,精确到秒。
p(then.Before(now))
p(then.After(now))
p(then.Equal(now))
// 方法 Sub 返回一个 Duration 来表示两个时间点的间隔时间。
diff := now.Sub(then)
p(diff)
// 我们计算出不同单位下的时间长度值。
p(diff.Hours())
p(diff.Minutes())
p(diff.Seconds())
p(diff.Nanoseconds())
// 你可以使用 Add 将时间后移一个时间间隔,或者使用一个 - 来将时间前移一个时间间隔。
p(then.Add(diff))
p(then.Add(-diff))
p("################")
// 格式化
// 这里是一个基本的按照 RFC3339 进行格式化的例子,使用对应模式常量。
t := time.Now()
p(t.Format(time.RFC3339))
// 时间解析使用同 Format 相同的形式值。
t1, e := time.Parse(
time.RFC3339,
"2012-11-01T22:08:41+00:00")
p(t1)
// ormat 和 Parse 使用基于例子的形式来决定日期格式,
// 一般你只要使用 time 包中提供的模式常量就行了,但是你也可以实现自定义模式。
// 模式必须使用时间 Mon Jan 2 15:04:05 MST 2006来指定给定时间/字符串的格式化/解析方式。
// 时间一定要按照如下所示:2006为年,15 为小时,Monday 代表星期几,等等。
p(t.Format("3:04PM"))
p(t.Format("Mon Jan _2 15:04:05 2006"))
p(t.Format("2006-01-02T15:04:05.999999-07:00"))
form := "3 04 PM"
t2, e := time.Parse(form, "8 41 PM")
p(t2)
// 对于纯数字表示的时间,你也可以使用标准的格式化字符串来提出出时间值得组成。
fmt.Printf("%d-%02d-%02dT%02d:%02d:%02d-00:00\n",
t.Year(), t.Month(), t.Day(),
t.Hour(), t.Minute(), t.Second())
// Parse 函数在输入的时间格式不正确是会返回一个错误。
ansic := "Mon Jan _2 15:04:05 2006"
_, e = time.Parse(ansic, "8:41PM")
p(e)
}
JSON转换
package main
import "encoding/json"
import "fmt"
import "os"
// 下面我们将使用这两个结构体来演示自定义类型的编码和解码。
type Response1 struct {
Page int
Fruits []string
}
type Response2 struct {
Page int `json:"page"`
Fruits []string `json:"fruits"`
}
func main() {
// 首先我们来看一下基本数据类型到 JSON 字符串的编码过程。这里是一些原子值的例子。
bolB, _ := json.Marshal(true)
fmt.Println(string(bolB))
intB, _ := json.Marshal(1)
fmt.Println(string(intB))
fltB, _ := json.Marshal(2.34)
fmt.Println(string(fltB))
strB, _ := json.Marshal("gopher")
fmt.Println(string(strB))
// 这里是一些切片和 map 编码成 JSON 数组和对象的例子。
slcD := []string{"apple", "peach", "pear"}
slcB, _ := json.Marshal(slcD)
fmt.Println(string(slcB))
mapD := map[string]int{"apple": 5, "lettuce": 7}
mapB, _ := json.Marshal(mapD)
fmt.Println(string(mapB))
// JSON 包可以自动的编码你的自定义类型。
// 编码仅输出可导出的字段,并且默认使用他们的名字作为 JSON 数据的键。
res1D := &Response1{
Page: 1,
Fruits: []string{"apple", "peach", "pear"}}
res1B, _ := json.Marshal(res1D)
fmt.Println(string(res1B))
// 你可以给结构字段声明标签来自定义编码的 JSON 数据键名称。
// 在上面 Response2 的定义可以作为这个标签这个的一个例子。
res2D := &Response2{
Page: 1,
Fruits: []string{"apple", "peach", "pear"}}
res2B, _ := json.Marshal(res2D)
fmt.Println(string(res2B))
// 现在来看看解码 JSON 数据为 Go 值的过程。
// 这里是一个普通数据结构的解码例子。
byt := []byte(`{"num":6.13,"strs":["a","b"]}`)
// 我们需要提供一个 JSON 包可以存放解码数据的变量。
// 这里的 map[string]interface{} 将保存一个 string 为键,值为任意值的map。
var dat map[string]interface{}
// 这里就是实际的解码和相关的错误检查。
if err := json.Unmarshal(byt, &dat); err != nil {
panic(err)
}
fmt.Println(dat)
// 为了使用解码 map 中的值,我们需要将他们进行适当的类型转换。
// 例如这里我们将 num 的值转换成 float64类型。
num := dat["num"].(float64)
fmt.Println(num)
// 访问嵌套的值需要一系列的转化。
strs := dat["strs"].([]interface{})
str1 := strs[0].(string)
fmt.Println(str1)
// 我们也可以解码 JSON 值到自定义类型。
// 这个功能的好处就是可以为我们的程序带来额外的类型安全加强,并且消除在访问数据时的类型断言。
str := `{"page": 1, "fruits": ["apple", "peach"]}`
res := Response2{}
json.Unmarshal([]byte(str), &res)
fmt.Println(res)
fmt.Println(res.Fruits[0])
// 在上面的例子中,我们经常使用 byte 和 string 作为使用标准输出时数据和 JSON 表示之间的中间值。
// 我们也可以和os.Stdout 一样,直接将 JSON 编码直接输出至 os.Writer流中,或者作为 HTTP 响应体。
enc := json.NewEncoder(os.Stdout)
d := map[string]int{"apple": 5, "lettuce": 7}
enc.Encode(d)
}
文件写入
package main
import (
"bufio"
"fmt"
"io/ioutil"
"os"
)
// Go 写文件和我们前面看过的读操作有着相似的方式。
// 读取文件需要经常进行错误检查,这个帮助方法可以精简下面的错误检查过程。
func check(e error) {
if e != nil {
panic(e)
}
}
func main() {
d1 := []byte("hello\ngo\n")
// 开始,这里是展示如写入一个字符串(或者只是一些字节)到一个文件。
err := ioutil.WriteFile("D:/study/dat1", d1, 0644)
check(err)
// 对于更细粒度的写入,先打开一个文件。
f, err := os.Create("D:/study/dat2")
check(err)
// 打开文件后,习惯立即使用 defer 调用文件的 Close操作。
defer f.Close()
// 你可以写入你想写入的字节切片
d2 := []byte{115, 111, 109, 101, 10}
n2, err := f.Write(d2)
check(err)
fmt.Printf("wrote %d bytes\n", n2)
// WriteString 也是可用的。
n3, err := f.WriteString("writes\n")
fmt.Printf("wrote %d bytes\n", n3)
// 调用 Sync 来将缓冲区的信息写入磁盘。
f.Sync()
// bufio 提供了和我们前面看到的带缓冲的读取器一样的带缓冲的写入器。
w := bufio.NewWriter(f)
n4, err := w.WriteString("buffered\n")
fmt.Printf("wrote %d bytes\n", n4)
// 使用 Flush 来确保所有缓存的操作已写入底层写入器。
w.Flush()
}