: jank : : 2942 : 2016-11-14 00:01 go
1.XML本质上是一种树形的数据格式,而我们可以定义与之匹配的go 语言的 struct类型,然后通过xml.Unmarshal来将xml中的数据解析成对应的struct对象
1.XML转换成struct格式
package main
import(
"encoding/xml"
"fmt"
"io/ioutil"
"os"
)
type Recurlyservers struct {
XMLName xml.Name `xml:"servers"` //反引号
Version string `xml:"version,attr"`
Svs []server `xml:"server"`
//Description string `xml:",innerxml"` //定义将一并输出原xml格式
}
type server struct {
XMLName xml.Name `xml:"server"`
ServerName string `xml:"serverName"`
ServerIp string `xml:"serverIP"`
}
func main(){
file, err := os.Open("servers.xml") //打开servers.xml
defer file.Close()
data, err := ioutil.ReadAll(file) //读取文件所有内容
v := Recurlyservers{}
err = xml.Unmarshal(data, &v) //转出struct对象
if err != nil {
fmt.Println(err)
return
}
fmt.Println(v)
}
2.struct转换成xml格式
//xml包中提供了Marshal和MarshalIndent两个函数,来满足我们的需求。这两个函数主要的区别是第二个函数会增加换行和缩进,函数的定义如下所示:
// func Marshal(v interface{}) ([]byte, error)
// func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error)
package main
import(
"encoding/xml"
"fmt"
"os"
)
type Servers struct {
XMLName xml.Name `xml:"servers"` //反引号
Version string `xml:"version,attr"`
Svs []server `xml:"server"`
}
type server struct {
ServerName string `xml:"serverName"`
ServerIP string `xml:"serverIP"`
}
func main(){
v := &Servers{Version:"1"} //定义一个struct
v.Svs = append(v.Svs, server{"Shanghhai_VPN", "127.0.0.1"}) //添加内容
v.Svs = append(v.Svs, server{"Beijing_VPN", "127.0.0.2"})
//output, err := xml.MarshalIndent(v, " ", " ") //带前缀缩进转换
output, err := xml.Marshal(v) //不带换行缩进转换
if err != nil {
fmt.Println("error: %v ", err)
}
os.Stdout.Write([]byte(xml.Header)) //输出xml头信息
os.Stdout.Write(output) //输出
}
2.json处理
1.json是一种轻量级的数据交换语言,以文字为基础,具有自我描述性且易于让人阅读。目前很多开放平台,基本上都是采用了json作为他们的数据交互的接口。JSON与XML最大的不同在于XML是一个完整的标记语言,而JSON不是。JSON由于比XML更小、更快,更易解析,以及浏览器的内建快速解析支持,使得其更适用于网络数据传输领域。
2.首先查找tag含有Foo的可导出的struct字段(首字母大写)
其次查找字段名是Foo的导出字段
最后查找类似FOO或者FoO这样的除了首字母之外其他大小写不敏感的导出字段
3.能够被赋值的字段必须是可导出字段(即首字母大写)。同时JSON解析的时候只会解析能找得到的字段,
找不到的字段会被忽略,这样的一个好处是:当你接收到一个很大的JSON数据结构而你却只想获取其中的部分数据的时候,你只需将你想要的数据对应的字段名大写,即可轻松解决这个问题。
1.已知json结构数据转换
例:
package main
import (
"encoding/json" //引入json包
"fmt"
)
type Server struct {
ServerName string
ServerIP string
}
type Serverslice struct {
Servers []Server
}
func main(){
var s Serverslice //定义一个结构体
str := `{"servers":[{"serverName":"Shanghai","serverIP":"127.0.0.1"},{"serverName":"Beijing","serverIP":"127.0.0.2"}]}`
json.Unmarshal([]byte(str), &s) //json 格式转换,将str转化的值
fmt.Println(s)
}
2.未知json结构数据转换
例:
//官方提供的解决方法,利用断言处理
package main
import (
"encoding/json" //引入json包
"fmt"
)
func main(){
var f interface{} //定义一个空interface{}型,因其可以存储任意类型数据
b := []byte(`{"Name":"Wednesday","Age":6,"Parents":["Gomez","Morticia"]}`)//假设这是一个未知结构的json数据
json.Unmarshal(b, &f) //把当前未知结构的json存储在f的指针,得到如下结构
// f = map[string]interface{} {
// "Name": "jank",
// "Age": 6,
// "Parents": []interface{} {
// "king",
// "hui",
// },
// }
m := f.(map[string]interface{}) //把得到的interface赋给m
for k, v :=range m { //最后对其进行断言
switch kv := v.(type){
case string:
fmt.Println(k, "is string", kv)
case int:
fmt.Println(k, "is int", kv)
case float64:
fmt.Println(k, "is float64", kv)
case []interface{}:
fmt.Println(k, "is an array;")
for i, u :=range kv {
fmt.Println(i, u)
}
default:
fmt.Println(k, "is a type type that i don`t know how to handle")
}
}
}
很多时候我们通过类型断言,操作起来不是很方便,目前bitly公司开源了一个叫做simplejson的包,在处理未知结构体的JSON时相当方便:https://github.com/bitly/go-simplejson
3.正则处理
1.通过正则判断是否匹配,使用regexp包
regexp包中含有三个函数用来判断是否匹配,如果匹配返回true,否则返回false:
func Match(pattern string, b []byte) (matched bool, error error)
func MatchReader(pattern string, r io.RuneReader) (matched bool, error error)
func MatchString(pattern string, s string) (matched bool, error error)
2.正则匹配
例:
package main
import (
"fmt"
"regexp"
)
func janks(ip string) (b bool) {
if m, _ := regexp.MatchString("^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$", ip); !m {
return false
}
return true
}
func main(){
var myip string = "120.27.37.185"
fmt.Println(janks(myip)) //输出 true
}
3.正则获取
例:
package main
import (
"fmt"
"io/ioutil"
"net/http"
"regexp"
"strings"
)
func main() {
resp, err := http.Get("http://www.baidu.com")
if err != nil {
fmt.Println("http get error.")
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println("http read error")
return
}
src := string(body)
//将HTML标签全转换成小写
re, _ := regexp.Compile("\<[\S\s]+?\>")
src = re.ReplaceAllStringFunc(src, strings.ToLower)
//去除STYLE
re, _ = regexp.Compile("\<style[\S\s]+?\</style\>")
src = re.ReplaceAllString(src, "")
//去除SCRIPT
re, _ = regexp.Compile("\<script[\S\s]+?\</script\>")
src = re.ReplaceAllString(src, "")
//去除所有尖括号内的HTML代码,并换成换行符
re, _ = regexp.Compile("\<[\S\s]+?\>")
src = re.ReplaceAllString(src, " ")
//去除连续的换行符
re, _ = regexp.Compile("\s{2,}")
src = re.ReplaceAllString(src, " ")
fmt.Println(strings.TrimSpace(src))
}
4.Go模板使用
1.在Go语言中,我们使用template包来进行模板处理,使用类似Parse、ParseFile、Execute等方法从文件或者字符串加载模板。
在Go语言中,我们使用template包来进行模板处理,使用类似Parse、ParseFile、Execute等方法从文件或者字符串加载模板,然后执行类似上面图片展示的模板的merge操作。请看下面的例子:
func handler(w http.ResponseWriter, r *http.Request) {
t := template.New("some template") //创建一个模板
t, _ = t.ParseFiles("tmpl/welcome.html", nil) //解析模板文件
user := GetUser() //获取当前用户信息
t.Execute(w, user) //执行模板的merger操作
}
通过上面的例子我们可以看到Go语言的模板操作非常的简单方便,和其他语言的模板处理类似,都是先获取数据,然后渲染数据。
为了演示和测试代码的方便,我们在接下来的例子中采用如下格式的代码
使用Parse代替ParseFiles,因为Parse可以直接测试一个字符串,而不需要额外的文件
不使用handler来写演示代码,而是每个测试一个main,方便测试
使用os.Stdout代替http.ResponseWriter,因为os.Stdout实现了io.Writer接口
2.字段操作
Go语言的模板通过{{}}来包含需要在渲染时被替换的字段,{{.}}表示当前的对象,这和Java或者C++中的this类似,如果要访问当前对象的字段通过{{.FieldName}},但是需要注意一点:这个字段必须是导出的(字段首字母必须是大写的),否则在渲染的时候就会报错,请看下面的这个例子:
package main
import (
"html/template"
"os"
)
type Person struct {
UserName string
}
func main() {
t := template.New("fieldname example")
t, _ = t.Parse("hello {{.UserName}}!")
p := Person{UserName: "Astaxie"}
t.Execute(os.Stdout, p)//代替http.ResponseWriter,因为os.Stdout实现了io.Writer接口
}
上面的代码我们可以正确的输出hello Astaxie,但是如果我们稍微修改一下代码,在模板中含有了未导出的字段,那么就会报错,如下:
type Person struct {
UserName string
email string //未导出的字段,首字母是小写的
}
t, _ = t.Parse("hello {{.UserName}}! {{.email}}")
上面的代码就会报错,因为我们调用了一个未导出的字段,但是如果我们调用了一个不存在的字段是不会报错的,而是输出为空。
如果模板中输出{{.}},这个一般应用于字符串对象,默认会调用fmt包输出字符串的内容。
3.输出嵌套字段内容
上面我们例子展示了如何针对一个对象的字段输出,那么如果字段里面还有对象,如何来循环的输出这些内容呢?我们可以使用{{with …}}…{{end}}和{{range …}}{{end}}来进行数据的输出。
{{range}} 这个和Go语法里面的range类似,循环操作数据
{{with}}操作是指当前对象的值,类似上下文的概念
详细的使用请看下面的例子:
package main
import (
"html/template"
"os"
)
type Friend struct {
Fname string
}
type Person struct {
UserName string
Emails []string
Friends []*Friend
}
func main() {
f1 := Friend{Fname: "minux.ma"}
f2 := Friend{Fname: "xushiwei"}
t := template.New("fieldname example")
t, _ = t.Parse(`hello {{.UserName}}!
{{range .Emails}}
an email {{.}}
{{end}}
{{with .Friends}}
{{range .}}
my friend name is {{.Fname}}
{{end}}
{{end}}
`)
p := Person{UserName: "Astaxie",
Emails: []string{"astaxie@beego.me", "astaxie@gmail.com"},
Friends: []*Friend{&f1, &f2}}
t.Execute(os.Stdout, p)
}
4.判断处理
在Go模板里面如果需要进行条件判断,那么我们可以使用和Go语言的if-else语法类似的方式来处理,如果pipeline为空,那么if就认为是false,下面的例子展示了如何使用if-else语法:
package main
import (
"os"
"text/template"
)
func main() {
tEmpty := template.New("template test")
tEmpty = template.Must(tEmpty.Parse("空 pipeline if demo: {{if ``}} 不会输出. {{end}} "))
tEmpty.Execute(os.Stdout, nil)//注意:if语句只是bool形式,不能带有条件判断
tWithValue := template.New("template test")
tWithValue = template.Must(tWithValue.Parse("不为空的 pipeline if demo: {{if `anything`}} 我有内容,我会输出. {{end}} "))
tWithValue.Execute(os.Stdout, nil)
tIfElse := template.New("template test")
tIfElse = template.Must(tIfElse.Parse("if-else demo: {{if `anything`}} if部分 {{else}} else部分.{{end}} "))
tIfElse.Execute(os.Stdout, nil)
}
5.pipelines
Unix用户已经很熟悉什么是pipe了,ls | grep "beego"类似这样的语法你是不是经常使用,过滤当前目录下面的文件,显示含有"beego"的数据,表达的意思就是前面的输出可以当做后面的输入,最后显示我们想要的数据,而Go语言模板最强大的一点就是支持pipe数据,在Go语言里面任何{{}}里面的都是pipelines数据,例如我们上面输出的email里面如果还有一些可能引起XSS注入的,那么我们如何来进行转化呢?
{{. | html}}
在email输出的地方我们可以采用如上方式可以把输出全部转化html的实体,上面的这种方式和我们平常写Unix的方式是不是一模一样,操作起来相当的简便,调用其他的函数也是类似的方式。
6.模板函数
每一个模板函数都有一个唯一值的名字,然后与一个Go函数关联,通过如下的方式来关联
type FuncMap map[string]interface{}
例如,如果我们想要的email函数的模板函数名是emailDeal,它关联的Go函数名称是EmailDealWith,那么我们可以通过下面的方式来注册这个函数
t = t.Funcs(template.FuncMap{"emailDeal": EmailDealWith})
EmailDealWith这个函数的参数和返回值定义如下:
func EmailDealWith(args …interface{}) string
我们来看下面的实现例子:
package main
import (
"fmt"
"html/template"
"os"
"strings"
)
type Friend struct {
Fname string
}
type Person struct {
UserName string
Emails []string
Friends []*Friend
}
func EmailDealWith(args ...interface{}) string {//自定义函数
ok := false
var s string
if len(args) == 1 {
s, ok = args[0].(string)
}
if !ok {
s = fmt.Sprint(args...)
}
// find the @ symbol
substrs := strings.Split(s, "@")
if len(substrs) != 2 {
return s
}
// replace the @ by " at "
return (substrs[0] + " at " + substrs[1])
}
func main() {
f1 := Friend{Fname: "minux.ma"}
f2 := Friend{Fname: "xushiwei"}
t := template.New("fieldname example")
t = t.Funcs(template.FuncMap{"emailDeal": EmailDealWith})//调用函数
t, _ = t.Parse(`hello {{.UserName}}!
{{range .Emails}}
an emails {{.|emailDeal}}
{{end}}
{{with .Friends}}
{{range .}}
my friend name is {{.Fname}}
{{end}}
{{end}}
`)
p := Person{UserName: "Astaxie",
Emails: []string{"astaxie@beego.me", "astaxie@gmail.com"},
Friends: []*Friend{&f1, &f2}}
t.Execute(os.Stdout, p)
}
在模板包内部已经有内置的实现函数,下面代码截取自模板包里面
var builtins = FuncMap{
"and": and,
"call": call,
"html": HTMLEscaper,
"index": index,
"js": JSEscaper,
"len": length,
"not": not,
"or": or,
"print": fmt.Sprint,
"printf": fmt.Sprintf,
"println": fmt.Sprintln,
"urlquery": URLQueryEscaper,
}
7.Must操作
模板包里面有一个函数Must,它的作用是检测模板是否正确,例如大括号是否匹配,注释是否正确的关闭,变量是否正确的书写。接下来我们演示一个例子,用Must来判断模板是否正确:
package main
import (
"fmt"
"text/template"
)
func main() {
tOk := template.New("first")
template.Must(tOk.Parse(" some static text /* and a comment */"))
fmt.Println("The first one parsed OK.")
template.Must(template.New("second").Parse("some static text {{ .Name }}"))
fmt.Println("The second one parsed OK.")
fmt.Println("The next one ought to fail.")
tErr := template.New("check parse error with Must")
template.Must(tErr.Parse(" some static text {{ .Name }"))//报错信息panic: template: check parse error with Must:1: unexpected "}" in command
}
8.嵌套模板
我们平常开发Web应用的时候,经常会遇到一些模板有些部分是固定不变的,然后可以抽取出来作为一个独立的部分,例如一个博客的头部和尾部是不变的,而唯一改变的是中间的内容部分。所以我们可以定义成header、content、footer三个部分。Go语言中通过如下的语法来申明
{{define "子模板名称"}}内容{{end}}
通过如下方式来调用:
{{template "子模板名称"}}
接下来我们演示如何使用嵌套模板,我们定义三个文件,header.tmpl、content.tmpl、footer.tmpl文件,里面的内容如下
//header.tmpl
{{define "header"}}
<html>
<head>
<title>演示信息</title>
</head>
<body>
{{end}}
//content.tmpl
{{define "content"}}
{{template "header"}}
<h1>演示嵌套</h1>
<ul>
<li>嵌套使用define定义子模板</li>
<li>调用使用template</li>
</ul>
{{template "footer"}}
{{end}}
//footer.tmpl
{{define "footer"}}
</body>
</html>
{{end}}
演示代码如下:
package main
import (
"fmt"
"os"
"text/template"
)
func main() {
s1, _ := template.ParseFiles("header.tmpl", "content.tmpl", "footer.tmpl")
s1.ExecuteTemplate(os.Stdout, "header", nil)
fmt.Println()
s1.ExecuteTemplate(os.Stdout, "content", nil)
fmt.Println()
s1.ExecuteTemplate(os.Stdout, "footer", nil)
fmt.Println()
s1.Execute(os.Stdout, nil)
}
通过上面的例子我们可以看到通过template.ParseFiles把所有的嵌套模板全部解析到模板里面,其实每一个定义的{{define}}都是一个独立的模板,他们相互独立,是并行存在的关系,内部其实存储的是类似map的一种关系(key是模板的名称,value是模板的内容),然后我们通过ExecuteTemplate来执行相应的子模板内容,我们可以看到header、footer都是相对独立的,都能输出内容,content 中因为嵌套了header和footer的内容,就会同时输出三个的内容。但是当我们执行s1.Execute,没有任何的输出,因为在默认的情况下没有默认的子模板,所以不会输出任何的东西。
同一个集合类的模板是互相知晓的,如果同一模板被多个集合使用,则它需要在多个集合中分别解析
5.文件操作
1.文件夹操作
package main
import (
"fmt"
"os"
)
func main() {
os.Mkdir("astaxie", 0777) //创建文件夹
os.MkdirAll("astaxie/test1/test2", 0777)//创建多级文件夹
err := os.Remove("astaxie")//删除文件夹
if err != nil {
fmt.Println(err)
}
os.RemoveAll("astaxie")//删除文件夹及文件夹下的所有
}
2.写文件
package main
import (
"fmt"
"os"
)
func main() {
userFile := "astaxie.txt"
fout, err := os.Create(userFile) //创建文件
if err != nil {
fmt.Println(userFile, err)
return
}
defer fout.Close()
for i := 0; i < 10; i++ {
fout.WriteString("Just a test! ")//写入文件内容
fout.Write([]byte("Just a test! "))
}
}
3.读文件/删除文件
package main
import (
"fmt"
"os"
)
func main() {
userFile := "asatxie.txt"
fl, err := os.Open(userFile)
if err != nil {
fmt.Println(userFile, err)
return
}
defer fl.Close()
buf := make([]byte, 1024)
for {
n, _ := fl.Read(buf)
if 0 == n {
break
}
os.Stdout.Write(buf[:n])
}
os.Remove(userFile)
}
6.字符串操作
字符串在我们平常的Web开发中经常用到,包括用户的输入,数据库读取的数据等,我们经常需要对字符串进行分割、连接、转换等操作,本小节将通过Go标准库中的strings和strconv两个包中的函数来讲解如何进行有效快速的操作。
字符串操作
下面这些函数来自于strings包,这里介绍一些我平常经常用到的函数,更详细的请参考官方的文档。
1.Contains 判断字符串包含
函数格式:func Contains(s, substr string) bool
字符串s中是否包含substr,返回bool值
fmt.Println(strings.Contains("seafood", "foo"))//判断字符串seafood中是否包含foo,返回bool值
2.join连接
函数格式:func Join(a []string, sep string) string
字符串链接,把slice a通过sep链接起来
s := []string{"foo", "bar", "baz"}
fmt.Println(strings.Join(s, ", "))
//Output:foo, bar, baz
func Index(s, sep string) int
3.在字符串s中查找sep所在的位置,返回位置值,找不到返回-1
fmt.Println(strings.Index("chicken", "ken"))
fmt.Println(strings.Index("chicken", "dmr"))
//Output:4
//-1
func Repeat(s string, count int) string
4.重复s字符串count次,最后返回重复的字符串
fmt.Println("ba" + strings.Repeat("na", 2))
//Output:banana
func Replace(s, old, new string, n int) string
5.在s字符串中,把old字符串替换为new字符串,n表示替换的次数,小于0表示全部替换
fmt.Println(strings.Replace("oink oink oink", "k", "ky", 2))
fmt.Println(strings.Replace("oink oink oink", "oink", "moo", -1))
//Output:oinky oinky oink
//moo moo moo
func Split(s, sep string) []string
6.把s字符串按照sep分割,返回slice
fmt.Printf("%q ", strings.Split("a,b,c", ","))
fmt.Printf("%q ", strings.Split("a man a plan a canal panama", "a "))
fmt.Printf("%q ", strings.Split(" xyz ", ""))
fmt.Printf("%q ", strings.Split("", "Bernardo O'Higgins"))
//Output:["a" "b" "c"]
//["" "man " "plan " "canal panama"]
//[" " "x" "y" "z" " "]
//[""]
7.Trim去除两端
func Trim(s string, cutset string) string
在s字符串的头部和尾部去除cutset指定的字符串
fmt.Printf("[%q]", strings.Trim(" !!! Achtung !!! ", "! "))
//Output:["Achtung"]
func Fields(s string) []string
去除s字符串的空格符,并且按照空格分割返回slice
fmt.Printf("Fields are: %q", strings.Fields(" foo bar baz "))
//Output:Fields are: ["foo" "bar" "baz"]
8.字符串转换
字符串转化的函数在strconv中,如下也只是列出一些常用的:
Append 系列函数将整数等转换为字符串后,添加到现有的字节数组中。
package main
import (
"fmt"
"strconv"
)
func main() {
str := make([]byte, 0, 100)
str = strconv.AppendInt(str, 4567, 10)
str = strconv.AppendBool(str, false)
str = strconv.AppendQuote(str, "abcdefg")
str = strconv.AppendQuoteRune(str, '单')
fmt.Println(string(str))
}
9.Format 系列函数把其他类型的转换为字符串
package main
import (
"fmt"
"strconv"
)
func main() {
a := strconv.FormatBool(false)
b := strconv.FormatFloat(123.23, 'g', 12, 64)
c := strconv.FormatInt(1234, 10)
d := strconv.FormatUint(12345, 10)
e := strconv.Itoa(1023)
fmt.Println(a, b, c, d, e)
}
10.Parse 系列函数把字符串转换为其他类型
package main
import (
"fmt"
"strconv"
)
func checkError(e error){
if e != nil{
fmt.Println(e)
}
}
func main() {
a, err := strconv.ParseBool("false")
checkError(err)
b, err := strconv.ParseFloat("123.23", 64)
checkError(err)
c, err := strconv.ParseInt("1234", 10, 64)
checkError(err)
d, err := strconv.ParseUint("12345", 10, 64)
checkError(err)
e, err := strconv.Atoi("1023")
checkError(err)
fmt.Println(a, b, c, d, e)
}