go语言文本操作

 : 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)

}


   

备案编号:赣ICP备15011386号

联系方式:qq:1150662577    邮箱:1150662577@qq.com