当前位置: 首页 > news >正文

网站平台建设服务合同找建设企业网站公司

网站平台建设服务合同,找建设企业网站公司,怎么学php网站开发,网络营销推广方法研究4、复合数据类型 数组、slice、map和结构体 如何使用结构体来解码和编码到对应JSON格式的数据#xff0c;并且通过结合使用模板来生成HTML页面 数组和结构体是聚合类型;它们的值由许多元素或成员字段的值组成。数组是由同构的元素组成#xff08;每个数组元素都是完全相同的…4、复合数据类型 数组、slice、map和结构体 如何使用结构体来解码和编码到对应JSON格式的数据并且通过结合使用模板来生成HTML页面 数组和结构体是聚合类型;它们的值由许多元素或成员字段的值组成。数组是由同构的元素组成每个数组元素都是完全相同的类型结构体则是由异构的元素组成的。数组和结构体都是有固定内存大小的数据结构。 slice和map则是动态的数据结构它们将根据需要动态增长。 4.1 数组 数组是一个由固定长度的特定类型元素组成的序列一个数组可以由零个或多个元素组成。 和数组对应的类型是 Slice(切片)它是可以增长和收缩动态序列slice功能也更灵活。 var a [3]int // array of 3 integers fmt.Println(a[0]) // print the first element fmt.Println(a[len(a)-1]) // print the last element, a[2] // Print the indices and elements. for i, v : range a {fmt.Printf(%d %d\n, i, v) } // Print the elements only. for _, v : range a {fmt.Printf(%d\n, v) }初始化 var q [3]int [3]int{1, 2, 3} var r [3]int [3]int{1, 2} fmt.Println(r[2]) // 0如果在数组的长度位置出现的是“…”省略号则表示数组的长度是根据初始 化值的个数来计算。 q : [...]int{1, 2, 3} fmt.Printf(%T\n, q) // [3]int数组的长度是数组类型的一个组成部分[3]int和[4]int是两种不同的数组类型。数组的长度必须是常量表达式因为数组的长度需要在编译阶段确定 q : [3]int{1, 2, 3} //报错 q [4]int{1, 2, 3, 4} // compile error: cannot assign [4]int to [3]int指定一个索引和对应值列表的方式初始化 type Currency int const (USD Currency iota// 美元EUR// 欧元GBP// 英镑RMB// 人民币 ) symbol : [...]string{USD: $, EUR: €, GBP: £, RMB: ¥} fmt.Println(RMB, symbol[RMB]) // 3 ¥//定义了一个含有100个元素的数组r最后一个元素被初始化为-1其它元素都是用0初始化r : [...]int{99: -1}数组比较 a : [2]int{1, 2} b : [...]int{1, 2} c : [2]int{1, 3} fmt.Println(a b, a c, b c) // true false false d : [3]int{1, 2} fmt.Println(a d) // compile error: cannot compare [2]int [3]int函数调用中通过指针来传递数组参数是高效的允许在函数内部修改数组的值。但是数组依然是僵化的类型因为数组的类型包含了僵化的长度信息。数组依然很少用作函数参数;相反我们一 般使用slice来替代数组。 4.2 Slice Slice(切片)代表变长的序列序列中每个元素都有相同的类型。一个slice类型一般写作 []T其中T代表slice中元素的类型;slice的语法和数组很像只是没有固定长度而已。 一个slice是一个轻量级的数据结构提供了访问数组子序 列(或者全部)元素的功能而且slice的底层确实引用一个数组对象 一个slice由三个部分构成: 指针指向第一个slice元素对应的底层数组元素的地址slice的第一个元素并不一定就是数组的第一个元素长度对应slice中元素的数目;长度不能超过容量容量一般是从slice的开始位置到底层数据的结尾位置 切片操作s[i:j]其中0 ≤ i≤ j≤ cap(s)用于创建一个新的slice引用s的从第i个元素开 始到第j-1个元素的子序列。新的slice将只有j-i个元素 多个slice之间可以共享底层数据。 数组定义 months : [...]string{1: January, /* ... */, 12: December}Q2 : months[4:7] summer : months[6:9] fmt.Println(Q2) // [April May June] fmt.Println(summer) // [June July August]两个slice都包含了六月份测试包含相同月份 for _, s : range summer {for _, q : range Q2 {if s q {fmt.Printf(%s appears in both\n, s)} } }如果切片操作超出cap(s)的上限将导致一个panic异常但是超出len(s)则是意味着扩展了 slice因为新slice的长度会变大: fmt.Println(summer[:20]) // panic: out of range endlessSummer : summer[:5] // extend a slice (within capacity) fmt.Println(endlessSummer) // [June July August September October]因为slice值包含指向第一个slice元素的指针因此向函数传递slice将允许在函数内部修改底层数组的元素。复制一个slice只是对底层的数组创建了一个新的slice别名。 reverse函数在原内存空间将[]int类型的slice反转而且它可以用于任意长度的slice。 // reverse reverses a slice of ints in place. func reverse(s []int) {for i, j : 0, len(s)-1; i j; i, j i1, j-1 {s[i], s[j] s[j], s[i]} }a : [...]int{0, 1, 2, 3, 4, 5} reverse(a[:]) fmt.Println(a) // [5 4 3 2 1 0]将slice元素循环向左旋转n个元素的方法是三次调用reverse反转函数 s : []int{0, 1, 2, 3, 4, 5} // Rotate s left by two positions. reverse(s[:2])//[1,0,2,3,4,5] reverse(s[2:])//[1,0,5,4,3,2] reverse(s) fmt.Println(s) // [2 3 4 5 0 1]slice类型的变量s初始化语法没有指明序列的 长度。这会隐式地创建一个合适大小的数组然后slice的指针指向底层的数组。 slice之间不能比较自己展开每个元素进行比较 func equal(x, y []string) bool {if len(x) ! len(y) {return false}for i : range x {if x[i] ! y[i] {return false}}return true }不支持操作的原因 一个slice的元素是间接引用的一个slice甚至可以包含自身。一个固定的slice值(译注:指slice本身的值不 是元素的值)在不同的时刻可能包含不同的元素因为底层数组的元素可能会被修改。 slice唯一合法的比较操作是和nil比较 if summer nil { /* ... */ }一个nil值的slice并没有底层数组长度和容量都是0。 var s []int // len(s) 0, s nil s nil // len(s) 0, s nil s []int(nil) // len(s) 0, s nil s []int{} // len(s) 0, s ! nil内置的make函数创建一个指定元素类型 make([]T, len)//容量等于长度 make([]T, len, cap) // same as make([]T, cap)[:len]make创建了一个匿名的数组变量然后返回一个slice;只有通过返回的slice才能引 用底层匿名的数组变量。 4.2.1 append函数 向slice追加元素理解slice底层是如何工作 var runes []rune for _, r : range Hello, 世界 {runes append(runes, r) } fmt.Printf(%q\n, runes) // [H e l l o , 世 界]appendInt函数 func appendInt(x []int, y int) []int {var z []intzlen : len(x) 1//检测slice底层数组是否有足够的容量来保存新添加的元素if zlen cap(x) {// There is room to grow. Extend the slice.z x[:zlen]} else {// There is insufficient space. Allocate a new array.// Grow by doubling, for amortized linear complexity.zcap : zlen//通过在每次扩展数组时直接将长度翻倍从而避免了多次内存分配if zcap 2*len(x) {zcap 2 * len(x)}z make([]int, zlen, zcap)copy(z, x) // a built-in function; see text}z[len(x)] yreturn z }通过在每次 扩展数组时直接将长度翻倍从而避免了多次内存分配 func main() {var x, y []intfor i : 0; i 10; i {y appendInt(x, i)fmt.Printf(%d cap%d\t%v\n, i, cap(y), y) xy} }每一次容量的变化都会导致重新分配内存和copy操作: i3次的迭代 i4次的迭代 slice实际上是一个类似下面结构体的聚合类型: type IntSlice struct {ptr *intlen, cap int }内置的append函数则可以追加多个 元素甚至追加一个slice。 var x []int x append(x, 1) x append(x, 2, 3) x append(x, 4, 5, 6) x append(x, x...) // append the slice x fmt.Println(x) // [1 2 3 4 5 6 1 2 3 4 5 6]4.2.2 Slice内存技巧 nonempty函数将在原有slice内存空间之上返回不包含空字符串的列表: package main import fmt // nonempty returns a slice holding only the non-empty strings. // The underlying array is modified during the call. func nonempty(strings []string) []string {i : 0for _, s : range strings {if s ! {strings[i] si }}return strings[:i] }输入的slice和输出的slice共享一个底层数组可以避免分配另一个数 组不过原来的数据将可能会被覆盖 data : []string{one, , three} fmt.Printf(%q\n, nonempty(data)) // [one three] fmt.Printf(%q\n, data) // [one three three]data nonempty(data)nonempty函数使用append函数实现: func nonempty2(strings []string) []string {out : strings[:0] // zero-length slice of originalfor _, s : range strings {if s ! {out append(out, s)} }return out }一个slice可以用来模拟一个stack。最初给定的空slice对应一个空的stack然后可以使用 append函数将新的值压入stack: stack append(stack, v) // push vstack的顶部位置对应slice的最后一个元素: top : stack[len(stack)-1] // top of stack通过收缩stack可以弹出栈顶的元素 stack stack[:len(stack)-1] // pop要删除slice中间的某个元素并保存原有的元素顺序可以通过内置的copy函数将后面的子 slice向前依次移动一位完成: func remove(slice []int, i int) []int {copy(slice[i:], slice[i1:])return slice[:len(slice)-1] } func main() {s : []int{5, 6, 7, 8, 9}fmt.Println(remove(s, 2)) // [5 6 8 9] }如果删除元素后不用保持原来顺序的话我们可以简单的用最后一个元素覆盖被删除的元素: func remove(slice []int, i int) []int {slice[i] slice[len(slice)-1]return slice[:len(slice)-1] } func main() {s : []int{5, 6, 7, 8, 9}fmt.Println(remove(s, 2)) // [5 6 9 8] }4.3 Map 哈希表是一个无序的key/value对的集合其中所有的key 都是不同的然后通过给定的key可以在常数时间复杂度内检索、更新或删除对应的value。map就是一个哈希表的引用 map[K]Vmap中所有的key都有相同的类型所有的value也有着相同的类型但是 key和value之间可以是不同的数据类型。 key必须是支持比较运算符的数据类型map可以通过测试key是否相等来判断是否已经存在。 内置的make函数可以创建一个map: ages : make(map[string]int) // mapping from strings to ints用map字面值的语法创建map同时还可以指定一些最初的key/value: ages : map[string]int{alice: 31,charlie: 34, }ages : make(map[string]int) ages[alice] 31 ages[charlie] 34创建空的map的表达式 map[string]int{}Map中的元素通过key对应的下标访问: ages[alice] 32 fmt.Println(ages[alice]) // 32使用内置的delete函数可以删除元素: delete(ages, alice) // remove element ages[alice]元素不在map中也没有关系;如果一个查找失败将返回 value类型对应的零值。 x y 和 x 等简短赋值语法也可以用在map 但是map中的元素并不是一个变量因此我们不能对map的元素进行取址操作。原因map可能随着元素数量的增长而重新分配更大的内存空间从而可能导致之前的地址无效。 遍历map中全部的key/value对 for name, age : range ages {fmt.Printf(%s\t%d\n, name, age) }Map的迭代顺序是不确定的并且不同的哈希函数实现可能导致不同的遍历顺序。 如果要按顺序遍历key/value对我们必须显式地对key进行排序可以使用sort包的Strings函数对字符串slice进行排序。 import sort var names []string for name : range ages {names append(names, name) } sort.Strings(names) for _, name : range names {fmt.Printf(%s\t%d\n, name, ages[name]) }创建了一个空的slice但是slice的容量刚好可以放下map中全部的key: names : make([]string, 0, len(ages))map类型的零值是nil也就是没有引用任何哈希表。 var ages map[string]int fmt.Println(ages nil) // true fmt.Println(len(ages) 0) // truemap上的大部分操作包括查找、删除、len和range循环都可以安全工作在nil值的map上它 们的行为和一个空的map类似。但是向一个nil值的map存入元素将导致一个panic异常: ages[carol] 21 // panic: assignment to entry in nil map在向map存数据前必须先创建map。 如果元素类型是一个数字你可以需要区分一个已经存在的0和不存在而返回 零值的0可以像下面这样测试: age, ok : ages[bob] if !ok { /* bob is not a key in this map; age 0. */ }//ok报告元素是否真的存在 if age, ok : ages[bob]; !ok { /* ... */ }map之间也不能进行相等比较;唯一的例外是和nil进行比较。 判断两个map是 否包含相同的key和value我们必须通过一个循环实现: func equal(x, y map[string]int) bool {if len(x) ! len(y) {return false}for k, xv : range x {if yv, ok : y[k]; !ok || yv ! xv {return false}}return true }Go语言中并没有提供一个set类型但是map中的key也是不相同的可以用map实现类似set 的功能。 dedup程序读取多行输入但是只打印第一次出现的行。dedup程序通过map来表示所有的输入行所对应的 set集合以确保已经在集合存在的行不会被重复打印。 func main() {seen : make(map[string]bool) // a set of stringsinput : bufio.NewScanner(os.Stdin)for input.Scan() {line : input.Text()if !seen[line] {seen[line] truefmt.Println(line)}}if err : input.Err(); err ! nil {fmt.Fprintf(os.Stderr, dedup: %v\n, err)os.Exit(1)} }需要一个map或set的key是slice类型但是map的key必须是可比较的类型但是 slice并不满足这个条件。 使用map来记录提交相同的字符串列表的次数处理任何不可比较的key类型 var m make(map[string]int) func k(list []string) string { return fmt.Sprintf(%q, list) } func Add(list []string) { m[k(list)] } func Count(list []string) int { return m[k(list)] }Map的value类型也可以是一个聚合类型比如是一个map或slice。 4.4 结构体 结构体是一种聚合的数据类型是由零个或多个任意类型的值聚合成的实体。每个值称为结构体的成员。 声明了叫Employee的命名的结构体类型声明一个Employee类型的变量dilbert type Employee struct {ID intName stringAddress stringDoB time.TimePosition stringSalary intManagerID int } var dilbert Employeedilbert结构体变量的成员可以通过点操作符访问 对成员赋值: dilbert.Salary - 5000 // demoted, for writing too few lines of code对成员取地址然后通过指针访问: position : dilbert.Position *position Senior *position // promoted, for outsourcing to Elbonia点操作符和指向结构体的指针一起工作: var employeeOfTheMonth *Employee dilbert employeeOfTheMonth.Position (proactive team player)EmployeeByID函数将根据给定的员工ID返回对应的员工信息结构体的指针。 func EmployeeByID(id int) *Employee { /* ... */ } fmt.Println(EmployeeByID(dilbert.ManagerID).Position) // Pointy-haired boss id : dilbert.ID EmployeeByID(id).Salary 0 // fired for... no real reason如果相邻的成员类型如果相 同的话可以被合并到一行 type Employee struct {ID intName, Address stringDoB time.TimePosition stringSalary intManagerID int }结构体成员的输入顺序也有重要的意义。 如果结构体成员名字是以大写字母开头的那么该成员就是导出的;一个结构体可能同时包含导出和未导出的成员。 一个命名为S的结构体类型将不能再包含S类型的成员:因为一个聚合的值不能包含它自身。但是S类型的结构体可以包含 *S 指针类型的成员这可以让我 们创建递归的数据结构比如链表和树结构等。 结构体类型的零值是每个成员都是零值。对于 bytes.Buffer类型结构体初始值就是一个随时可用的空缓存sync.Mutex的零值也是有效的未锁定状态。 如果结构体没有任何成员的话就是空结构体写作struct{}。它的大小为0也不包含任何信息但是有时候依然是有价值的。 seen : make(map[string]struct{}) // set of strings // ... if _, ok : seen[s]; !ok {seen[s] struct{}{}// ...first time seeing s... }4.4.1 结构体字面值 结构体值也可以用结构体字面值表示结构体字面值可以指定每个成员的值。 type Point struct{ X, Y int } //法一以结构体成员定义的顺序 为每个结构体成员指定一个字面值 //一般只在定义结构体的包内部使用或者是在较小的结构体中使用 p : Point{1, 2}以成员名字和相应的值来初始化 //法二 anim : gif.GIF{LoopCount: nframes}结构体可以作为函数的参数和返回值。 func Scale(p Point, factor int) Point {return Point{p.X * factor, p.Y * factor} } fmt.Println(Scale(Point{1, 2}, 5)) // {5 10}考虑效率较大的结构体通常会用指针的方式传入和返回 func Bonus(e *Employee, percent int) int {return e.Salary * percent / 100 }如果要在函数内部修改结构体成员的话用指针传入是必须的;因为在Go语言中所有的函数参数都是值拷贝传入的函数参数将不再是函数调用时的原始变量。 func AwardAnnualRaise(e *Employee) {e.Salary e.Salary * 105 / 100 }创建并初始化一个结构体变量并返回结构体的地址: pp : Point{1, 2}下面语句等价 pp : new(Point) *pp Point{1, 2}4.4.2 结构体比较 如果结构体的全部成员都是可以比较的那么结构体也是可以比较的那样的话两个结构体 将可以使用或!运算符进行比较。 type Point struct{ X, Y int } p : Point{1, 2} q : Point{2, 1} fmt.Println(p.X q.X p.Y q.Y) // false fmt.Println(p q) // false可比较的结构体类型可以用于map的key类型。 type address struct {hostname stringport int } hits : make(map[address]int) hits[address{golang.org, 443}]4.4.3 结构体嵌入和匿名成员 结构体嵌入机制让一个命名的结构体包含另一个结构体类型的匿名成员通过简单的点运算符x.f来访问匿名成员链中嵌套的x.d.e.f成员。 type Circle struct {X, Y, Radius int } type Wheel struct {X, Y, Radius, Spokes int }创建一个wheel变量: var w Wheel w.X 8 w.Y 8 w.Radius 5 w.Spokes 20将相同的属性独立出来: type Point struct {X, Y int } type Circle struct {Center PointRadius int } type Wheel struct {Circle CircleSpokes int }结构体类型变的清晰了但是这种修改同时也导致了访问每个成员变得繁琐: var w Wheel w.Circle.Center.X 8 w.Circle.Center.Y 8 w.Circle.Radius 5 w.Spokes 20只声明一个成员对应的数据类型而不指名成员的名字;这类成员就叫匿名成员。匿名成员的数据类型必须是命名的类型或指向一个命名的类型的指针 type Circle struct {PointRadius int } type Wheel struct {CircleSpokes int }匿名嵌入的特性我们可以直接访问叶子属性而不需要给出完整的路径: 我们在访问子成员的时候可以忽略任何匿名成员部分。 var w Wheel w.X 8 w.Y 8 w.Radius 5 w.Spokes 20结构体字面值并没有简短表示匿名成员的语法下面的语句都不能编译通过: w Wheel{8, 8, 5, 20} // compile error: unknown fields w Wheel{X: 8, Y: 8, Radius: 5, Spokes: 20} // compile error: unknown fields结构体字面值必须遵循形状类型声明时的结构: w Wheel{Circle{Point{8, 8}, 5}, 20} w Wheel{Circle: Circle{Point: Point{X: 8, Y: 8}, Radius: 5, },Spokes: 20, // NOTE: trailing comma necessary here (and at Radius) } fmt.Printf(%#v\n, w) // Output: // Wheel{Circle:Circle{Point:Point{X:8, Y:8}, Radius:5}, Spokes:20} w.X 42 fmt.Printf(%#v\n, w) // Output: // Wheel{Circle:Circle{Point:Point{X:42, Y:8}, Radius:5}, Spokes:20}#:用和Go语言类似的语法打印值 不能同时包含两个类型相同的匿名成员 匿名成员并不要求是结构体类型;其实任何命名的类型都可以作为结构体的匿名成员 匿名类型的方法集 外层的结构体不仅仅是获得了匿名成员类型的所有成员而 且也获得了该类型导出的全部的方法。这个机制可以用于将一个有简单行为的对象组合成有复杂行为的对象。 4.5 JSON JavaScript对象表示法JSON是一种发送和接收结构化信息的标准协议。Go语言对于这些标准格式的编码和解码都有良好的支持由标准库中的encoding/json、 encoding/xml、encoding/asn1等包提供支持。 JSON是对JavaScript中各种类型的值——字符串、数字、布尔值和对象——Unicode本文编码。 基本的JSON类型有数字(十进制或科学记数法)、布尔值(true或false)、字符串其中字符串是以双引号包含的Unicode字符序列支持和Go语言类似的反斜杠转义特性不过JSON 使用的是 \Uhhhh 转义数字来表示一个UTF-16编码(译注:UTF-16和UTF-8一样是一种变长 的编码有些Unicode码点较大的字符需要用4个字节表示;而且UTF-16还有大端和小端的问题)而不是Go语言的rune类型。 一个JSON数组是一个有序的值序列写在一个方括号中并以逗号分隔;一个JSON数组可以用于编码Go语言的数组和slice。一个JSON对象是一个字符串到值的映射写成以系列的name:value对形式用花括号包含并以逗号分隔JSON的对象类型可以用于编码Go语言的map类型(key类型是字符串) 和结构体。 boolean true number -273.15 string She said \Hello, BF\ array [gold, silver, bronze] object {year: 1980,event: archery,medals: [gold, silver, bronze]}考虑一个应用程序该程序负责收集各种电影评论并提供反馈功能。 type Movie struct {Title stringYear int json:releasedColor bool json:color,omitemptyActors []string } var movies []Movie{{Title: Casablanca, Year: 1942, Color: false,Actors: []string{Humphrey Bogart, Ingrid Bergman}},{Title: Cool Hand Luke, Year: 1967, Color: true,Actors: []string{Paul Newman}},{Title: Bullitt, Year: 1968, Color: true,Actors: []string{Steve McQueen, Jacqueline Bisset}},// ... }将一个Go语言中类似movies的结构体slice转为JSON的过程叫编组(marshaling)。编组通过调用 json.Marshal函数完成: data, err : json.Marshal(movies) if err ! nil {log.Fatalf(JSON marshaling failed: %s, err) } fmt.Printf(%s\n, data)Marshal函数返还一个编码后的字节slice包含很长的字符串并且没有空白缩进;我们将它 折行以便于显示: [{Title:Casablanca,released:1942,Actors:[Humphrey Bogart,Ingr id Bergman]},{Title:Cool Hand Luke,released:1967,color:true,Ac tors:[Paul Newman]},{Title:Bullitt,released:1968,color:true, Actors:[Steve McQueen,Jacqueline Bisset]}]json.MarshalIndent函数将产生整齐缩进的输出。该函数有两个额外的字符串参数用于表示每一行输出的前缀和每一个层级的缩进: data, err : json.MarshalIndent(movies, , ) if err ! nil {log.Fatalf(JSON marshaling failed: %s, err) } fmt.Printf(%s\n, data)上面的代码将产生这样的输出: [{Title: Casablanca,released: 1942,Actors: [Humphrey Bogart,Ingrid Bergman] },{Title: Cool Hand Luke,released: 1967,color: true,Actors: [Paul Newman] },{Title: Bullitt,released: 1968,color: true,Actors: [Steve McQueen,Jacqueline Bisset] } ]在编码时默认使用Go语言结构体的成员名字作为JSON的对象。只有导出的结构体成员才会被编码这也就是我们为什么选择用大写字母开头的成员名称。 Year名字的成员在编码后变成了released还有Color成员 编码后变成了小写字母开头的color。这是因为构体成员Tag所导致的。一个构体成员Tag是和在编译阶段关联到该成员的元信息字符串: Year int json:released Color bool json:color,omitempty结构体的成员Tag可以是任意的字符串面值但是通常是一系列用空格分隔的key:value键值 对序列;因为值中含义双引号字符因此成员Tag一般用原生字符串面值的形式书写。 omitempty选项表示当Go语言结构体成员为空或零值时不生成JSON对象 解码对应将JSON数据解码为Go语言的数据结构Go语言中一般叫 unmarshaling通过json.Unmarshal函数完成。 选择性 地解码JSON中感兴趣的成员。当Unmarshal函数调用返回slice将被只含有Title信息值填 充其它JSON成员将被忽略。 var titles []struct{ Title string } if err : json.Unmarshal(data, titles); err ! nil {log.Fatalf(JSON unmarshaling failed: %s, err) } fmt.Println(titles) // [{Casablanca} {Cool Hand Luke} {Bullitt}]许多web服务都提供JSON接口通过HTTP接口发送JSON格式请求并返回JSON格式的信 息。 Github的issue查询服务: 定义合适的类型和常量 // Package github provides a Go API for the GitHub issue tracker. // See https://developer.github.com/v3/search/#search-issues. package githubimport timeconst IssuesURL https://api.github.com/search/issuestype IssuesSearchResult struct {TotalCount int json:total_countItems []*Issue }type Issue struct {Number intHTMLURL string json:html_urlTitle stringState stringUser *UserCreatedAt time.Time json:created_atBody string // in Markdown format }type User struct {Login stringHTMLURL string json:html_url }对应的JSON对象名是小写字母每个结构体的成员名也是声明为大写字母开头的。 SearchIssues函数发出一个HTTP请求然后解码返回的JSON格式的结果。因为用户提供的 查询条件可能包含类似 ? 和 之类的特殊字符为了避免对URL造成冲突我们用 url.QueryEscape来对查询中的特殊字符进行转义操作。 package github import (encoding/jsonfmtnet/httpnet/urlstrings ) // SearchIssues queries the GitHub issue tracker. func SearchIssues(terms []string) (*IssuesSearchResult, error) {q : url.QueryEscape(strings.Join(terms, ))resp, err : http.Get(IssuesURL ?q q)if err ! nil {return nil, err}// We must close resp.Body on all execution paths.// (Chapter 5 presents defer, which makes this simpler.)if resp.StatusCode ! http.StatusOK {resp.Body.Close()return nil, fmt.Errorf(search query failed: %s, resp.Status)}var result IssuesSearchResultif err : json.NewDecoder(resp.Body).Decode(result); err ! nil {resp.Body.Close()return nil, err}resp.Body.Close()return result, nil }基于流式的解码器json.Decoder它可以从一个输入流解码 JSON数据针对输出流的json.Encoder编码对象。 4.6 文本和HTML模板 复杂的打印格式需要将格式化代码分离出来以便更安全地修改。 由text/template 和html/template等模板包提供的它们提供了一个将变量值填充到一个文本或HTML格式的模板的机制。 text/template 一个模板是一个字符串或一个文件里面包含了一个或多个由双花括号包含的 {{action}} 对象。 大部分的字符串只是按面值打印但是对于actions部分将触发其它的行为。每个actions 都包含了一个用模板语言书写的表达式一个action虽然简短但是可以输出复杂的打印值模 板语言包含通过选择结构体的成员、调用函数或方法、表达式控制流if-else语句和range循环 语句还有其它实例化模板等诸多特性。 const templ {{.TotalCount}} issues: {{range .Items}}---------------------------------------- Number: {{.Number}} User: {{.User.Login}} Title: {{.Title | printf %.64s}} Age: {{.CreatedAt | daysAgo}} days {{end}}对于每一个action都有一个当前值的概念对应点操作符写作“.”。当前值“.”最初被 初始化为调用模板时的参数 {{range .Items}} 和 {{end}} 对应一个循环action循环每次迭代的当前值对应当前的Items元素的值。 | 操作符表示将前一个表达式的结果作为后一个函数的输入。 func daysAgo(t time.Time) int {return int(time.Since(t).Hours() / 24) }time.Time类型对应的JSON值是一个标准时间格式的字符串。 生成模板的输出分析模板并转为内部表示然后基于指定的 输入执行模板。 //1、创建并返回一个模板 //2、自定义函数注册到模板中并返回模板 //3、分析模板 report, err : template.New(report).Funcs(template.FuncMap{daysAgo: daysAgo}).Parse(templ) if err ! nil {log.Fatal(err) }如果模板解析失败将是一个致命的错误。template.Must 辅助函数可以简化这个致命错误的处理:它接受一个模板和一个error类型的参数检测error 是否为nil(如果不是nil则发出panic异常)然后返回传入的模板。 一旦模板已经创建、注册了daysAgo函数、并通过分析和检测我们就可以使用 github.IssuesSearchResult作为输入源、os.Stdout作为输出源来执行模板: var report template.Must(template.New(issuelist).Funcs(template.FuncMap{daysAgo: daysAgo}).Parse(templ))func main() {result, err : github.SearchIssues(os.Args[1:])if err ! nil {log.Fatal(err)}if err : report.Execute(os.Stdout, result); err ! nil {log.Fatal(err)} }程序输出一个纯文本报告。 html/template模板包 和text/template包相同的API和模板语言但是增加了一个将字符串自动转义特性避免输入字符串和HTML、JavaScript、CSS或 URL语法产生冲突的问题还可以避免一些长期存在的安全问题。 模板以HTML格式输出issue列表 import html/template var issueList template.Must(template.New(issuelist).Parse( h1{{.TotalCount}} issues/h1 table tr styletext-align: leftth#/ththState/ththUser/ththTitle/th /tr {{range .Items}} trtda href{{.HTMLURL}}{{.Number}}/a/tdtd{{.State}}/tdtda href{{.User.HTMLURL}}{{.User.Login}}/a/tdtda href{{.HTMLURL}}{{.Title}}/a/td /tr {{end}} /table ))
http://www.dnsts.com.cn/news/93310.html

相关文章:

  • 开源的 二次网站开发学校网站推广
  • 白云高端网站建设案例网页制作和网页制作技术
  • 企业网站怎么做才好开发公司对代理公司管理
  • 建设企业网站小微什么是软件开发过程
  • 慈溪网站建设公司网站再就业技能培训班
  • 网站建设 苏州跨境电商入门基础知识
  • iis7发布网站教程泉州网络推广专员
  • 怎么样建一个网站wordpress 会员注册插件
  • 厦门淘宝网站设计公司泰安人才网网上办事
  • 做网站php与python怎样做 网站做seo
  • 北京网站开发招聘58平板网站开发环境
  • 免费网站重生做军嫂怎样购买起名软件自己做网站
  • 三合一网站管理系统怎么做的五莲网站建设
  • 网站设计作品龙口网站制作公司
  • 做啥网站比较好赚钱怎么开网店做代理
  • 重庆智能网站建设多少钱郑州建立一个网站需要哪些
  • 网站建设类有哪些职位重庆网站建设重庆网站制作
  • 南京网络建站模板做外文网站
  • 风讯网站内容管理系统好的网站建设公司
  • 宠物网站建设的可行性坪地网站建设怎么样
  • 贵阳网站建设官方app下载
  • 南京网站定制南京建设互联网地方垂直网站
  • 网站建设有啥费用wordpress the post
  • 河北网站建设品牌大全阿里做网站
  • 高校档案室网站建设介绍网页设计
  • 外贸网站平台都有哪些平台毕业设计做网站简单吗
  • 网站的营销特点麦当劳订餐网站 是谁做的
  • 襄阳市作风建设年 网站园区网互联及网站建设项目
  • 建筑方案设计网站旺店通app手机企业版下载
  • 利用表单大师做网站济南建网站app