bl双性强迫侵犯h_国产在线观看人成激情视频_蜜芽188_被诱拐的少孩全彩啪啪漫画

Go語言開發(十六)、Go語言常用標準庫六-創新互聯

Go語言開發(十六)、Go語言常用標準庫六

一、json

1、json簡介

Go的標準包encoding/json對JSON的編解碼提供了完整的支持。

創新互聯建站致力于互聯網網站建設與網站營銷,提供做網站、網站制作、網站開發、seo優化、網站排名、互聯網營銷、小程序制作、公眾號商城、等建站開發,創新互聯建站網站建設策劃專家,為不同類型的客戶提供良好的互聯網應用定制解決方案,幫助客戶在新的全球化互聯網環境中保持優勢。

2、編碼

在編碼過程中,json包會將Go的類型轉換為JSON類型,轉換規則如下:
bool 轉換為JSON boolean
浮點數, 整數, Number 轉換為:JSON number
string轉換為:JSON string
數組、切片 轉換為:JSON數組
[]byte 轉換為:base64 string
struct、map轉換為:JSON object
func Marshal(v interface{}) ([]byte, error)
Marshal函數返回v的json編碼。Marshal函數會遞歸的處理值。如果一個值實現了Marshaler接口且非nil指針,會調用其MarshalJSON方法來生成json編碼。否則,Marshal函數使用默認編碼格式。
(1)結構體編碼
json包通過反射機制來實現編解碼,因此結構體必須導出所轉換的字段,不導出的字段不會被json包解析。

package main

import (
   "encoding/json"
   "fmt"
)

type Person struct {
   Name string
   Age  int
   sex  string
}

func main() {
   bauer := Person{"Bauer", 25, "Man"}
   bytes, err := json.Marshal(bauer)
   if err != nil {
      fmt.Println("Marshal failed.")
   }
   fmt.Println(string(bytes)) // {"Name":"Bauer","Age":25}
}

(2)結構體字段標簽
json包在解析結構體時,如果遇到key為json的字段標簽,則會按照一定規則解析該標簽:第一個字段是在JSON串中使用的名字,后續字段為其它選項,例如omitempty指定空值字段不出現在JSON中。如果整個value為"-",則不解析該字段。

package main

import (
   "encoding/json"
   "fmt"
)

type Person struct {
   Name string `json:"name,omitempty"`
   Age  int    `json:"age"`
   Sex  string `json:"-"`
}

func main() {
   bauer := Person{"Bauer", 25, "Man"}
   bytes, err := json.Marshal(bauer)
   if err != nil {
      fmt.Println("Marshal failed.")
   }
   fmt.Println(string(bytes)) // {"name":"Bauer","age":25}
}

(3)匿名字段
json包在解析匿名字段時,會將匿名字段的字段當成該結構體的字段處理。

package main

import (
   "encoding/json"
   "fmt"
)

type Point struct{ X, Y int }

type Circle struct {
   Point
   Radius int
}

func main() {
   data, err := json.Marshal(Circle{Point{50, 50}, 25})
   if err == nil {
      fmt.Println(string(data))
   }
}

// output:
//{"X":50,"Y":50,"Radius":25}

(4)轉換接口
在調用Marshal(v interface{})函數時,Marshal函數會判斷v是否滿足json.Marshaler接口或encoding.TextMarshaler接口。如果滿足,則會調用json.Marshaler接口或encoding.TextMarshaler接口來進行轉換(如果兩個都滿足,優先調用json.Marshaler)。json.Marshaler接口或encoding.TextMarshaler接口定義如下:

type Marshaler interface {
   MarshalJSON() ([]byte, error)
}
type TextMarshaler interface {
   MarshalText() (text []byte, err error)
}

json.Marshaler示例如下:

package main

import (
   "encoding/json"
   "fmt"
)

type Point struct{ X, Y int }

func (pt Point) MarshalJSON() ([]byte, error) {
   return []byte(fmt.Sprintf(`{"X":%d,"Y":%d}`, pt.X, pt.Y)), nil
}

func main() {
   data, err := json.Marshal(Point{50, 50})
   if err == nil {
      fmt.Printf("%s\n", data)
   }
}

// output:
// {"X":50,"Y":50}

encoding.TextMarshaler示例如下:

package main

import (
   "encoding/json"
   "fmt"
)

type Point struct{ X, Y int }

func (pt Point) MarshalText() ([]byte, error) {
   return []byte(fmt.Sprintf("{\"X\":%d,\"Y\":%d}", pt.X, pt.Y)), nil
}
func main() {
   data, err := json.Marshal(Point{50, 50})
   if err == nil {
      fmt.Printf("%s\n", data)
   }
}

// output:
// "{\"X\":50,\"Y\":50}"

(5)編碼到輸出流
func NewEncoder(w io.Writer) *Encoder
NewEncoder創建一個將數據寫入w的*Encoder
func (enc *Encoder) Encode(v interface{}) error
Encode將v的json編碼寫入輸出流,并會寫入一個換行符。
使用示例如下:

package main

import (
   "encoding/json"
   "os"
)

type Person struct {
   Name string
   Age  int
}

func main() {
   persons := []Person{
      {"Bauer", 30},
      {"Bob", 20},
      {"Lee", 24},
   }
   encoder := json.NewEncoder(os.Stdout)
   for _, person := range persons {
      encoder.Encode(person)
   }
}

// output:
// {"Name":"Bauer","Age":30}
// {"Name":"Bob","Age":20}
// {"Name":"Lee","Age":24}

3、解碼

解碼將JSON轉換為Go數據類型。在解碼過程中,json包會將JSON類型轉換為Go類型,轉換規則如下:
JSON boolean 轉換為 bool
JSON number 轉換為 float64
JSON string 轉換為 string
JSON數組 轉換為 []interface{}
JSON object 轉換為 map
null 轉換為 nil
func Unmarshal(data []byte, v interface{}) error
Unmarshal函數解析json編碼的數據data并將結果存入v指向的值,v通常傳入指針,否則解析雖不報錯,但數據無法賦值到接受體中。
要將json數據解碼寫入一個指針對象,Unmarshal函數首先處理json數據中json字面值null的情況。此時,函數將指針設為nil;否則,函數將json數據解碼寫入指針指向的值;如果指針本身是nil,函數會先申請一個值并使指針指向它。
要將json數據解碼寫入一個結構體,函數會匹配輸入對象的鍵和Marshal使用的鍵(結構體字段名或者字段標簽指定的鍵名),優先選擇精確的匹配,但也接受大小寫不敏感的匹配。
如果一個JSON值不匹配給出的目標類型,或者如果一個json數字寫入目標類型時溢出,Unmarshal函數會跳過該字段并盡量完成其余的解碼操作。如果沒有出現更加嚴重的錯誤,函數會返回一個描述第一個此類錯誤的詳細信息的UnmarshalTypeError。
JSON的null值解碼為go的接口、指針、切片時會將其值設為nil,null在json一般表示“不存在”。解碼json的null值到go類型時,不會造成任何改變,也不會產生錯誤。
當解碼字符串時,不合法的utf-8或utf-16字符不視為錯誤,而是將非法字符替換為unicode字符。
(1)JSON轉結構體
JSON可以轉換成結構體。json包通過反射機制來實現解碼,因此結構體必須導出所轉換的字段,不導出的字段不會被json包解析,另外解析時不區分大小寫:

package main

import (
   "encoding/json"
   "fmt"
)

type Person struct {
   Name string
   Age  int
   sex  string
}

func main() {
   data := []byte(`{"Name":"Bauer","Age":25,"sex":"Man"}`)
   var bauer Person
   json.Unmarshal(data, &bauer)
   fmt.Printf("Name:%s Age:%d sex:%s\n", bauer.Name, bauer.Age, bauer.sex)
}

// output:
// Name:Bauer Age:25 sex:

(2)結構體字段標簽
解碼時依然支持結構體字段標簽,規則和編碼相同。

package main

import (
   "encoding/json"
   "fmt"
)

type Person struct {
   Name string `json:"name,omitempty"`
   Age  int    `json:"age"`
   Sex  string `json:"-"`
}

func main() {
   data := []byte(`{"name":"Bauer","age":25,"Sex":"Man"}`)
   var bauer Person
   json.Unmarshal(data, &bauer)
   fmt.Printf("Name:%s, Age:%d, Sex:%s\n", bauer.Name, bauer.Age, bauer.Sex)
}

// output:
// Name:Bauer, Age:25, Sex:

(3)匿名字段
在解碼JSON時,如果找不到字段,則查找字段的字段。

package main

import (
   "encoding/json"
   "fmt"
)

type Point struct {
   X, Y int
}

type Circle struct {
   Point
   Radius int
}

func main() {

   data := []byte(`{"X":80,"Y":80,"Radius":40}`)

   var c Circle
   json.Unmarshal(data, &c)

   fmt.Printf("X:%d,Y:%d,Radius:%d\n", c.X, c.Y, c.Radius)
}

// output:
// X:80,Y:80,Radius:40

(4)轉換接口
解碼時根據參數類型是否滿足json.Unmarshaler和encoding.TextUnmarshaler來調用相應函數(若兩個函數都存在,則優先調用json.Unmarshaler)。json.Unmarshaler和encoding.TextUnmarshaler接口定義如下:

type Unmarshaler interface {
   UnmarshalJSON([]byte) error
}
type TextUnmarshaler interface {
   UnmarshalText(text []byte) error
}

json.Unmarshaler接口示例:

package main

import (
   "encoding/json"
   "fmt"
)

type Point struct{ X, Y int }

func (pt Point) UnmarshalJSON(data []byte) error {
   fmt.Println(string(data))
   return nil
}

func main() {
   data := []byte(`{"X":50,"Y":50}`)
   var point Point
   json.Unmarshal(data, &point)
}

// output:
// {"X":50,"Y":50}

encoding.TextUnmarshaler接口示例:

package main

import (
   "encoding/json"
   "fmt"
)

type Point struct{ X, Y int }

func (pt Point) UnmarshalText(text []byte) error {
   fmt.Println(string(text))
   return nil
}

func main() {
   data := []byte(`"{\"X\":50,\"Y\":50}"`)
   var point Point
   json.Unmarshal(data, &point)
}

// output:
// {"X":50,"Y":50}

(5)從輸入流解碼
func NewDecoder(r io.Reader) *Decoder
NewDecoder創建一個從r讀取并解碼json對象的*Decoder,Decoder有自己的緩沖,并可能超前讀取部分json數據。
func (dec *Decoder) Buffered() io.Reader
Buffered方法返回保存在dec緩存里數據的讀取器,該返回值在下次調用Decode方法前有效。
func (dec *Decoder) UseNumber()
UseNumber方法將dec設置為當接收端是interface{}接口時將json數字解碼為Number類型而不是float64類型。
func (dec *Decoder) Decode(v interface{}) error
Decode從輸入流讀取下一個json編碼值并保存在v指向的值里

package main

import (
   "encoding/json"
   "fmt"
   "io"
   "strings"
)

type Person struct {
   Name string
   Age  int
}

func main() {
   const dataStream = ` 
        { "Name" : "Bauer" , "Age" : 30} 
        { "Name" : "Bob" , "Age" : 24 } 
        { "Name" : "Lee" , "Age": 20}
    `
   dec := json.NewDecoder(strings.NewReader(dataStream))
   for {
      var person Person
      if err := dec.Decode(&person); err == io.EOF {
         break
      }
      fmt.Printf("Name: %s, Age: %d\n", person.Name, person.Age)
   }
}

// output:
// Name: Bauer, Age: 30
// Name: Bob, Age: 24
// Name: Lee, Age: 20

二、xml

1、xml簡介

Go的標準庫encoding/xml提供了對XML的操作。xml包提供了兩種方式來操作XML,一種是高階的方式,一種是低階的方式。高階的方式提供了Marshal和Unmarshal兩個函數分別來編碼(將Go數據結構轉換成XML)和解碼(將XML轉換成Go數據結構)。低階的方法則基于token來進行編碼和解碼。

2、低階方式Token

低階方法是以Token為單位操縱XML,Token有四種類型:StartElement用來表示XML開始節點;EndElement用來表示XML結束節點;CharData即為XML的原始文本(raw text);Comment表示注釋。低階方法通常用在解析XML中的若干節點場景。

<!-- comment -->
<action application="answer">raw text</action>

上述xml文件中, < action application="answer" > 為StartElement, < /action > 為EndElement,raw text為CharData, < !-- -- > 為Comment。
Go語言xml包對Token的數據結構進行了封裝,代碼如下:

type Name struct {
   Space, Local string
}

type Attr struct {
   Name  Name
   Value string
}

type Token interface{}

type StartElement struct {
   Name Name
   Attr []Attr
}

func (e StartElement) Copy() StartElement {
   attrs := make([]Attr, len(e.Attr))
   copy(attrs, e.Attr)
   e.Attr = attrs
   return e
}

func (e StartElement) End() EndElement {
   return EndElement{e.Name}
}

type EndElement struct {
   Name Name
}

type CharData []byte

func (c CharData) Copy() CharData { return CharData(makeCopy(c)) }

type Comment []byte

xml包提供對xml文件的編碼解碼常用方法如下:

type TokenReader interface {
   Token() (Token, error)
}
type Decoder struct {
   Strict bool
   AutoClose []string
   Entity map[string]string
   CharsetReader func(charset string, input io.Reader) (io.Reader, error)
   DefaultSpace string
   r              io.ByteReader
   t              TokenReader
   buf            bytes.Buffer
   saved          *bytes.Buffer
   stk            *stack
   free           *stack
   needClose      bool
   toClose        Name
   nextToken      Token
   nextByte       int
   ns             map[string]string
   err            error
   line           int
   offset         int64
   unmarshalDepth int
}
func NewDecoder(r io.Reader) *Decoder

NewDecoder從io.Reader對象讀取xml數據,創建一個Decoder
func NewTokenDecoder(t TokenReader) *Decoder
NewTokenDecoder使用底層Token流創建一個XML解析器
func (d *Decoder) Token() (Token, error)
Token返回解析器的下一個Token,解析結束返回io.EOF

type Encoder struct {
   p printer
}
func NewEncoder(w io.Writer) *Encoder

創建編碼器,參數為io.Writer
func (enc *Encoder) EncodeToken(t Token) error
編碼Token
func (enc *Encoder) Flush() error
刷新緩沖區,將已經編碼內容寫入io.Writer
func (enc *Encoder) Indent(prefix, indent string)
縮進
示例如下:

package main

import (
   "bytes"
   "encoding/xml"
   "fmt"
   "io"
)

var file string = `<person id="13"><name><first>John</first><last>Doe</last></name><age>42</age><Married>false</Married><City>Hanga Roa</City><State>Easter Island</State><!-- Need more details. --></person>`

func parseXMLFromToken(xmlFile string) {
   // 創建一個io.Reader
   reader := bytes.NewReader([]byte(xmlFile))
   // 創建×××
   dec := xml.NewDecoder(reader)
   // 開始遍歷解碼
   indent := ""  // 控制縮進
   sep := "    " // 每層的縮進量為四個空格
   for {
      tok, err := dec.Token() // 返回下一個Token
      // 錯誤處理
      if err == io.EOF { // 如果讀到結尾,則退出循環
         break
      }
      switch tok := tok.(type) { // Type switch
      case xml.StartElement: // 開始節點,打印名字和屬性
         fmt.Print(indent)
         fmt.Printf("<%s ", tok.Name.Local)
         s := ""
         for _, v := range tok.Attr {
            fmt.Printf(`%s%s="%s"`, s, v.Name.Local, v.Value)
            s = " "
         }
         fmt.Println(">")
         indent += sep // 遇到開始節點,則增加縮進量
      case xml.EndElement: // 結束節點,打印名字
         indent = indent[:len(indent)-len(sep)] // 遇到結束節點,則減少縮進量
         fmt.Printf("%s</%s>\n", indent, tok.Name.Local)
      case xml.CharData: // 原始字符串,直接打印
         fmt.Printf("%s%s\n", indent, tok)
      case xml.Comment: // 注釋,直接打印
         fmt.Printf("%s<!-- %s -->\n", indent, tok)
      }
   }
}

type AttrMap map[string]string // 屬性的鍵值對容器

// start()用來構建開始節點
func start(tag string, attrs AttrMap) xml.StartElement {
   var a []xml.Attr
   for k, v := range attrs {
      a = append(a, xml.Attr{xml.Name{"", k}, v})
   }
   return xml.StartElement{xml.Name{"", tag}, a}
}

func generateXMLFile() {
   // 創建編碼器
   buffer := new(bytes.Buffer)
   enc := xml.NewEncoder(buffer)
   // 開始生成XML
   startPerson := start("person", AttrMap{"id": "13"})
   enc.EncodeToken(startPerson)

   startName := start("name", AttrMap{})
   enc.EncodeToken(startName)

   startFirstName := start("first", AttrMap{})
   enc.EncodeToken(startFirstName)
   enc.EncodeToken(xml.CharData("John"))
   enc.EncodeToken(startFirstName.End())
   starLastName := start("last", AttrMap{})
   enc.EncodeToken(starLastName)
   enc.EncodeToken(xml.CharData("Doe"))
   enc.EncodeToken(starLastName.End())
   enc.EncodeToken(startName.End())

   startAge := start("age", AttrMap{})
   enc.EncodeToken(startAge)
   enc.EncodeToken(xml.CharData("42"))
   enc.EncodeToken(startAge.End())
   startMarried := start("Married", AttrMap{})
   enc.EncodeToken(startMarried)
   enc.EncodeToken(xml.CharData("false"))
   enc.EncodeToken(startMarried.End())
   startCity := start("City", AttrMap{})
   enc.EncodeToken(startCity)
   enc.EncodeToken(xml.CharData("Hanga Roa"))
   enc.EncodeToken(startCity.End())
   startState := start("State", AttrMap{})
   enc.EncodeToken(startState)
   enc.EncodeToken(xml.CharData("Easter Island"))
   enc.EncodeToken(startState.End())
   enc.EncodeToken(xml.Comment("Need more details."))

   enc.EncodeToken(startPerson.End())

   // 寫入XML
   enc.Flush()

   // 打印結果
   fmt.Println(buffer)
}
func main() {
   fmt.Println("Decode XML:")
   parseXMLFromToken(file)
   fmt.Println("Encode XML:")
   generateXMLFile()
}

3、高階方式

xml包以反射機制實現的編解碼,因此自定義的結構體必須導出所要轉換的字段。xml包定義了結構體和XML數據的轉換規則。xml包根據字段的命名,字段的標簽來映射XML元素,轉換規則如下:
1、xml:"value,value,..."結構體標簽為xml包所解析,第一個value對應XML中的名字(節點名、屬性名)。
2、字段與XML節點名對應關系:
A、如果存在名為XMLName的字段,并且標簽中存在名字值,則該名字值為節點名稱,否則
B、如果存在名為XMLName的字段,并且類型為xml.Name,則該字段的值為節點名稱,否則
C、結構體名稱。
3、字段標簽的解析
A、"-"忽略該字段
B、"name,attr"字段映射為XML屬性,name為屬性名
C、",attr"字段映射為XML屬性,字段名為屬性名
D、",chardata"字段映射為原始字符串
E、"omitempty"若包含此標簽則在字段值為0值時忽略此字段
4、視匿名字段的字段為結構體的字段
xml高階方式常用方法如下:
func Marshal(v interface{}) ([]byte, error)
接收一個interface{},遍歷其結構,編碼為XML

type Marshaler interface {
   MarshalXML(e *Encoder, start StartElement) error
}
func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error)

接收一個interface{},遍歷其結構,編碼為XML,增加縮進
func Unmarshal(data []byte, v interface{}) error
將data解碼為v,v通常為結構體
高階方法適用于需要編碼和解碼整個XML并且需要以結構化的數據操縱XML的場景。高階方法必須導出結構體,會破壞封裝。
創建一個test.xml,內容如下:

<?xml version="1.0" encoding="UTF-8"?>
<person id="13">
    <name>
        <first>John</first>
        <last>Doe</last>
    </name>
    <age>42</age>
    <Married>false</Married>
    <City>Hanga Roa</City>
    <State>Easter Island</State>
</person>

示例如下:

package main

import (
   "encoding/xml"
   "fmt"
   "io/ioutil"
   "os"
)

type Person struct {
   XMLName string `xml:"person"`
   ID      string `xml:"id,attr"`
   Name    Name
   Age     Age
   Married Married
   City    City
   State   State
}
type Name struct {
   XMLName string `xml:"name"`
   First   FirstName
   Last    LastName
}
type FirstName struct {
   XMLName string `xml:"first"`
   Data    string `xml:",chardata"`
}
type LastName struct {
   XMLName string `xml:"last"`
   Data    string `xml:",chardata"`
}
type Age struct {
   XMLName string `xml:"age"`
   Data    int    `xml:",chardata"`
}
type Married struct {
   XMLName string `xml:"Married"`
   Data    bool   `xml:",chardata"`
}
type City struct {
   XMLName string `xml:"City"`
   Data    string `xml:",chardata"`
}
type State struct {
   XMLName string `xml:"State"`
   Data    string `xml:",chardata"`
}

func generateXMLFile(xmlFile string) {
   first := FirstName{"first", "John"}
   last := LastName{"last", "Doe"}
   name := Name{"name", first, last}
   age := Age{"age", 42}
   married := Married{"Married", false}
   city := City{"City", "Hanga Roa"}
   state := State{"State", "Easter Island"}
   person := Person{"person", "13", name, age, married, city, state}

   data, _ := xml.MarshalIndent(person, "", "    ")
   headerBytes := []byte(xml.Header) //加入XML頭
   outputData := append(headerBytes, data...)
   ioutil.WriteFile("test.xml", outputData, os.ModeAppend)
}
func parseXMLFile(xmlFile string) {
   bytes, err := ioutil.ReadFile(xmlFile)
   if err != nil {
      fmt.Println(err)
   }
   var person Person
   xml.Unmarshal(bytes, &person)
   fmt.Println("FirstName: ", person.Name.First.Data)
   fmt.Println("LastName: ", person.Name.Last.Data)
   fmt.Println("ID: ", person.ID)
   fmt.Println("Married: ", person.Married.Data)
   fmt.Println("Age: ", person.Age.Data)
   fmt.Println("City: ", person.City.Data)
   fmt.Println("State: ", person.State.Data)
}
func main() {
   generateXMLFile("test.xml")
   parseXMLFile("test.xml")
}

三、base64

1、base64簡介

Base64是網絡上最常見的用于傳輸8Bit字節代碼的編碼方式之一。Base64編碼可用于在HTTP環境下傳遞較長的標識信息。在Java Persistence系統Hibernate中,就采用了Base64來將一個較長的唯一標識符(一般為128-bit的UUID)編碼為一個字符串,用作HTTP表單和HTTP GET URL中的參數。采用Base64編碼具有不可讀性,即所編碼的數據不會被人用肉眼所直接看到。

2、base64常用方法

Go語言中encoding/base64提供了對base64編解碼支持,encoding/base64定義了一個Encoding結構體,表示Base64的Encoding。并且導出了四個常用的Encoding對象:StdEncoding、URLEncoding、RawStdEncoding、RawURLEncoding。StdEncoding表示標準的Encoding,URLEncoding用于對URL編解碼,編解碼過程中會將Base64編碼中的特殊標記+和/替換為-和_,RawStdEncoding和RawURLEncoding是StdEncoding和URLEncoding的非padding版本。

type Encoding struct {
   encode    [64]byte
   decodeMap [256]byte
   padChar   rune
   strict    bool
}
// 四個導出的編碼/×××
var StdEncoding = NewEncoding(encodeStd)
var URLEncoding = NewEncoding(encodeURL)
var RawStdEncoding = StdEncoding.WithPadding(NoPadding)
var RawURLEncoding = URLEncoding.WithPadding(NoPadding)

func (enc *Encoding) Encode(dst, src []byte)
將src編碼為dst
func (enc *Encoding) EncodeToString(src []byte) string
將src編碼,返回string
func (enc *Encoding) Decode(dst, src []byte) (n int, err error)
將src解碼并寫入dst,成功返回寫入的字節數和error
func (enc *Encoding) DecodeString(s string) ([]byte, error)
將字符串s解碼并返回[]byte
func (enc Encoding) WithPadding(padding rune) *Encoding
設置enc的padding,返回Encoding指針,NoPadding表示不進行padding操作
func NewDecoder(enc *Encoding, r io.Reader) io.Reader
創建一個base64的輸入流×××
func NewEncoder(enc *Encoding, w io.Writer) io.WriteCloser
創建一個base64的輸出流編碼器

3、base64示例

package main

import (
   "encoding/base64"
   "fmt"
   "io"
   "os"
   "strings"
)

func StdEncodingExample() {
   data := "Hello world!"
   encoded := base64.StdEncoding.EncodeToString([]byte(data))
   fmt.Println(encoded)
   decoded, err := base64.StdEncoding.DecodeString(encoded)
   if err == nil {
      fmt.Println(string(decoded))
   }
   // Output:
   // SGVsbG8gd29ybGQh
   // Hello world!
}

func URLEncodingExample() {
   url := []byte("https://blog.51cto.com/9291927")
   encoded := base64.URLEncoding.EncodeToString(url)
   fmt.Println(encoded)
   decoded, err := base64.URLEncoding.DecodeString(encoded)
   if err == nil {
      fmt.Println(string(decoded))
   }
   // Output:
   // aHR0cDovL2Jsb2cuNTFjdG8uY29tLzkyOTE5Mjc=
   // https://blog.51cto.com/9291927
}

func ExampleStream() {
   data := []byte("Hello Hyperledger Fabric")
   encoder := base64.NewEncoder(base64.StdEncoding, os.Stdout)
   encoder.Write(data)
   encoder.Close()
   fmt.Println()
   input := "SGVsbG8gSHlwZXJsZWRnZXIgRmFicmlj"
   reader := strings.NewReader(input)
   decoder := base64.NewDecoder(base64.StdEncoding, reader)
   io.Copy(os.Stdout, decoder)
   // output:
   // SGVsbG8gSHlwZXJsZWRnZXIgRmFicmlj
   // Hello Hyperledger Fabric
}

func main() {
   StdEncodingExample()
   URLEncodingExample()
   ExampleStream()
}

四、unicode/utf-8

1、utf-8簡介

utf8實現了函數和常量來支持UTF-8編碼的文本。

const (
   RuneError = '\uFFFD'     // 錯誤的 Rune 或 Unicode 代理字符
   RuneSelf  = 0x80         // ASCII 字符范圍
   MaxRune   = '\U0010FFFF' // Unicode 碼點的大值
   UTFMax    = 4            // 一個字符編碼的大長度
)
func EncodeRune(p []byte, r rune) int

將r轉換為UTF-8編碼寫入p中(p必須足夠長,通常為4個字節)
如果r是無效的Unicode字符,則寫入RuneError
返回寫入的字節數
func DecodeRune(p []byte) (r rune, size int)
解碼p中的第一個字符,返回解碼后的字符和p中被解碼的字節數
如果p為空,則返回(RuneError, 0)
如果p中的編碼無效,則返回(RuneError, 1)
無效編碼:UTF-8 編碼不正確(比如長度不夠)、結果超出Unicode范圍、編碼不是最短的。
func DecodeRuneInString(s string) (r rune, size int)
解碼s中的第一個字符,返回解碼后的字符和p中被解碼的字節數
func DecodeLastRune(p []byte) (r rune, size int)
解碼p中的最后一個字符,返回解碼后的字符和p中被解碼的字節數
如果p為空,則返回(RuneError, 0)
如果p中的編碼無效,則返回(RuneError, 1)
func DecodeLastRuneInString(s string) (r rune, size int)
解碼p中的最后一個字符,返回解碼后的字符和p中被解碼的字節數
func FullRune(p []byte) bool
FullRune檢測p中第一個字符的UTF-8編碼是否完整(完整并不表示有效)。
一個無效的編碼也被認為是完整字符,將被轉換為一個RuneError字符。
func FullRuneInString(s string) bool
FullRune檢測s中第一個字符的UTF-8編碼是否完整(完整并不表示有效)。
func RuneCount(p []byte) int
返回p中的字符個數
錯誤的UTF8編碼和長度不足的UTF8編碼將被當作單字節的RuneError處理
func RuneCountInString(s string) (n int)
返回s中的字符個數
func RuneLen(r rune) int
RuneLen返回需要多少字節來編碼字符r,如果r是無效的字符,則返回-1
func RuneStart(b byte) bool
判斷b是否為UTF8字符的首字節編碼,最高位(bit)是不是10的字節就是首字節。
func Valid(p []byte) bool
Valid判斷p是否為完整有效的UTF8編碼序列。
func ValidString(s string) bool
Valid判斷s是否為完整有效的UTF8編碼序列。
func ValidRune(r rune) bool
ValidRune判斷r能否被正確的轉換為UTF8編碼。
超出Unicode范圍的碼點或UTF-16代理區中的碼點不能轉換。

2、utf-8示例

package main

import (
   "fmt"
   "unicode/utf8"
)

func ExampleDecodeLastRune() {
   b := []byte("Hello, 世界")

   for len(b) > 0 {
      r, size := utf8.DecodeLastRune(b)
      fmt.Printf("%c %v\n", r, size)

      b = b[:len(b)-size]
   }
   // Output:
   // 界 3
   // 世 3
   //   1
   // , 1
   // o 1
   // l 1
   // l 1
   // e 1
   // H 1
}

func ExampleDecodeLastRuneInString() {
   str := "Hello, 世界"

   for len(str) > 0 {
      r, size := utf8.DecodeLastRuneInString(str)
      fmt.Printf("%c %v\n", r, size)

      str = str[:len(str)-size]
   }
   // Output:
   // 界 3
   // 世 3
   //   1
   // , 1
   // o 1
   // l 1
   // l 1
   // e 1
   // H 1
}

func ExampleDecodeRune() {
   b := []byte("Hello, 世界")

   for len(b) > 0 {
      r, size := utf8.DecodeRune(b)
      fmt.Printf("%c %v\n", r, size)

      b = b[size:]
   }
   // Output:
   // H 1
   // e 1
   // l 1
   // l 1
   // o 1
   // , 1
   //   1
   // 世 3
   // 界 3
}

func ExampleDecodeRuneInString() {
   str := "Hello, 世界"

   for len(str) > 0 {
      r, size := utf8.DecodeRuneInString(str)
      fmt.Printf("%c %v\n", r, size)

      str = str[size:]
   }
   // Output:
   // H 1
   // e 1
   // l 1
   // l 1
   // o 1
   // , 1
   //   1
   // 世 3
   // 界 3
}

func ExampleEncodeRune() {
   r := '世'
   buf := make([]byte, 3)

   n := utf8.EncodeRune(buf, r)

   fmt.Println(buf)
   fmt.Println(n)
   // Output:
   // [228 184 150]
   // 3
}

func ExampleFullRune() {
   buf := []byte{228, 184, 150} // 世
   fmt.Println(utf8.FullRune(buf))
   fmt.Println(utf8.FullRune(buf[:2]))
   // Output:
   // true
   // false
}

func ExampleFullRuneInString() {
   str := "世"
   fmt.Println(utf8.FullRuneInString(str))
   fmt.Println(utf8.FullRuneInString(str[:2]))
   // Output:
   // true
   // false
}

func ExampleRuneCount() {
   buf := []byte("Hello, 世界")
   fmt.Println("bytes =", len(buf))
   fmt.Println("runes =", utf8.RuneCount(buf))
   // Output:
   // bytes = 13
   // runes = 9
}

func ExampleRuneCountInString() {
   str := "Hello, 世界"
   fmt.Println("bytes =", len(str))
   fmt.Println("runes =", utf8.RuneCountInString(str))
   // Output:
   // bytes = 13
   // runes = 9
}

func ExampleRuneLen() {
   fmt.Println(utf8.RuneLen('a'))
   fmt.Println(utf8.RuneLen('界'))
   // Output:
   // 1
   // 3
}

func ExampleRuneStart() {
   buf := []byte("a界")
   fmt.Println(utf8.RuneStart(buf[0]))
   fmt.Println(utf8.RuneStart(buf[1]))
   fmt.Println(utf8.RuneStart(buf[2]))
   // Output:
   // true
   // true
   // false
}

func ExampleValid() {
   valid := []byte("Hello, 世界")
   invalid := []byte{0xff, 0xfe, 0xfd}

   fmt.Println(utf8.Valid(valid))
   fmt.Println(utf8.Valid(invalid))
   // Output:
   // true
   // false
}

func ExampleValidRune() {
   valid := 'a'
   invalid := rune(0xfffffff)

   fmt.Println(utf8.ValidRune(valid))
   fmt.Println(utf8.ValidRune(invalid))
   // Output:
   // true
   // false
}

func ExampleValidString() {
   valid := "Hello, 世界"
   invalid := string([]byte{0xff, 0xfe, 0xfd})

   fmt.Println(utf8.ValidString(valid))
   fmt.Println(utf8.ValidString(invalid))
   // Output:
   // true
   // false
}

func main() {
   ExampleDecodeLastRune()
   ExampleDecodeLastRuneInString()
   ExampleDecodeRune()
   ExampleDecodeRuneInString()
   ExampleEncodeRune()
   ExampleFullRune()
   ExampleFullRuneInString()
   ExampleRuneCount()
   ExampleRuneCountInString()
   ExampleRuneLen()
   ExampleRuneStart()
   ExampleValid()
   ExampleValidRune()
   ExampleValidString()
}

五、net/rpc

1、RPC簡介

RPC(Remote Procedure Call,遠程過程調用)是一種通過網絡從遠程計算機程序上請求服務,而不需要了解底層網絡細節的應用程序通信協議。在OSI網絡通信模型中,RPC跨越了傳輸層和應用層。RPC使得開發包括網絡分布式多程序在內的應用程序更加容易。
RPC采用客戶機/服務器模式。請求程序是一個客戶機,而服務提供程序是一個服務器。首先,客戶機調用進程發送一個有進程參數的調用信息到服務進程,然后等待應答信息。在服務器端,進程保持睡眠狀態直到調用信息到達為止。當一個調用信息到達,服務器獲得進程參數,計算結果,發送答復信息,然后等待下一個調用信息,最后,客戶端調用進程接收答復信息,獲得進程結果,然后調用執行繼續進行。
RPC調用過程如下:
Go語言開發(十六)、Go語言常用標準庫六
1、調用客戶端句柄;執行傳送參數
2、調用本地系統內核發送網絡消息
3、消息傳送到遠程主機
4、服務器句柄得到消息并取得參數
5、執行遠程過程
6、執行的過程將結果返回服務器句柄
7、服務器句柄返回結果,調用遠程系統內核
8、消息傳回本地主機
9、客戶句柄由內核接收消息
10、客戶接收句柄返回的數據
Go的rpc支持三個級別的RPC:TCP、HTTP、JSONRPC。但Go的RPC包是獨一無二的RPC,與傳統的RPC系統不同,只支持Go開發的服務器與客戶端之間的交互,因為內部采用Gob編碼。Gob是Golang包自帶的一個數據結構序列化的編碼/解碼工具,編碼使用Encoder,解碼使用Decoder,其典型應用場景就是RPC。
Go RPC的函數只有符合下面的條件才能被遠程訪問,不然會被忽略,詳細的要求如下:
(1)函數必須是導出的(首字母大寫)
(2)必須有兩個導出類型的參數,第一個參數是接收的參數,第二個參數是返回給客戶端的參數,第二個參數必須是指針類型的。
(3)函數還要有一個返回值error
func (t *T) MethodName(argType T1, replyType *T2) error
T、T1和T2類型必須能被encoding/gob包編解碼。

2、rpc常用接口

net/rpc定義了一個缺省的DefaultServer,實現一個簡單的Server,可以直接調用Server的很多方法。

var DefaultServer = NewServer()
func HandleHTTP() {
   DefaultServer.HandleHTTP(DefaultRPCPath, DefaultDebugPath)
}

如果需要配置不同的Server,如不同的監聽地址或端口,需要自己創建Server。
func NewServer() *Server
Server的監聽方式如下:

func (server *Server) Accept(lis net.Listener)
func (server *Server) HandleHTTP(rpcPath, debugPath string)
func (server *Server) ServeCodec(codec ServerCodec)
func (server *Server) ServeConn(conn io.ReadWriteCloser)
func (server *Server) ServeHTTP(w http.ResponseWriter, req *http.Request)
func (server *Server) ServeRequest(codec ServerCodec) error

ServeHTTP?用于處理http請求的業務邏輯,首先處理http的?CONNECT請求,通過http.Hijacker創建連接conn, 然后調用ServeConn處理連接上 客戶端的請求。

func (server *Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
   if req.Method != "CONNECT" {
     w.Header().Set("Content-Type", "text/plain; charset=utf-8")
     w.WriteHeader(http.StatusMethodNotAllowed)
      io.WriteString(w, "405 must CONNECT\n")
      return
   }
   conn, _, err := w.(http.Hijacker).Hijack()
   if err != nil {
      log.Print("rpc hijacking ", req.RemoteAddr, ": ", err.Error())
      return
   }
   io.WriteString(conn, "HTTP/1.0 "+connected+"\n\n")
   server.ServeConn(conn)
}

Server.HandleHTTP用于設置rpc的上下文路徑,rpc.HandleHTTP使用默認的上下文路徑。
當使用http.ListenAndServe啟動一個http server的時,HandleHTTP設置的上下文將用作RPC傳輸,上下文的請求由ServeHTTP來處理。

func (server *Server) HandleHTTP(rpcPath, debugPath string) {
   http.Handle(rpcPath, server)
   http.Handle(debugPath, debugHTTP{server})
}

Accept用來處理一個監聽器,監聽客戶端的連接,一旦監聽器接收了一個連接,則交給ServeConn在另外一個goroutine中處理。

func (server *Server) Accept(lis net.Listener) {
   for {
      conn, err := lis.Accept()
      if err != nil {
         log.Print("rpc.Serve: accept:", err.Error())
         return
      }
      go server.ServeConn(conn)
   }
}
func (server *Server) ServeConn(conn io.ReadWriteCloser) {
   buf := bufio.NewWriter(conn)
   srv := &gobServerCodec{
      rwc:    conn,
      dec:    gob.NewDecoder(conn),
      enc:    gob.NewEncoder(buf),
      encBuf: buf,
   }
   server.ServeCodec(srv)
}

連接最終由ServerCodec處理,默認使用gobServerCodec處理,可以使用其它的Coder。
客戶端建立和服務器的連接

func Dial(network, address string) (*Client, error)
func DialHTTP(network, address string) (*Client, error)
func DialHTTPPath(network, address, path string) (*Client, error)
func NewClient(conn io.ReadWriteCloser) *Client
func NewClientWithCodec(codec ClientCodec) *Client

DialHTTP?和?DialHTTPPath通過HTTP的方式和服務器建立連接。

func DialHTTPPath(network, address, path string) (*Client, error) {
   var err error
   conn, err := net.Dial(network, address)
   if err != nil {
      return nil, err
   }
   io.WriteString(conn, "CONNECT "+path+" HTTP/1.0\n\n")

   // Require successful HTTP response
   // before switching to RPC protocol.
   resp, err := http.ReadResponse(bufio.NewReader(conn), &http.Request{Method: "CONNECT"})
   if err == nil && resp.Status == connected {
      return NewClient(conn), nil
   }
   if err == nil {
      err = errors.New("unexpected HTTP response: " + resp.Status)
   }
   conn.Close()
   return nil, &net.OpError{
      Op:   "dial-http",
      Net:  network + " " + address,
      Addr: nil,
      Err:  err,
   }
}

首先發送CONNECT請求,如果連接成功則通NewClient(conn)創建client。
Dial通過TCP與服務器建立連接。

func Dial(network, address string) (*Client, error) {
   conn, err := net.Dial(network, address)
   if err != nil {
      return nil, err
   }
   return NewClient(conn), nil
}

NewClient創建一個缺省codec為glob序列化庫的客戶端。

func NewClient(conn io.ReadWriteCloser) *Client {
   encBuf := bufio.NewWriter(conn)
   client := &gobClientCodec{conn, gob.NewDecoder(conn), gob.NewEncoder(encBuf), encBuf}
   return NewClientWithCodec(client)
}

NewClientWithCodec創建一個codec序列化庫的客戶端。

func NewClientWithCodec(codec ClientCodec) *Client {
   client := &Client{
      codec:   codec,
      pending: make(map[uint64]*Call),
   }
   go client.input()
   return client
}

客戶端的調用RPC服務方法有兩個方法:?Go?和?Call。?Go方法是異步的,返回一個Call指針對象, 它的Done是一個channel,如果服務返回,Done就可以得到返回的對象(實際是Call對象,包含Reply和error信息)。?Go是同步的方式調用,它實際是調用Call實現的

func (client *Client) Call(serviceMethod string, args interface{}, reply interface{}) error {
   call := <-client.Go(serviceMethod, args, reply, make(chan *Call, 1)).Done
   return call.Error
}

rpc框架默認使用gob序列化庫,為了追求更好的效率或者追求更通用的序列化格式,可以采用其它序列化方式,如protobuf,,json,,xml等。
gob序列化庫要求注冊接口類型的具體實現類型。

func (server *Server) Register(rcvr interface{}) error
func (server *Server) RegisterName(name string, rcvr interface{}) error

3、基于HTTP的RPC

Server.go:

package main

import (
   "log"
   "net/http"
   "net/rpc"
)

type Args struct {
  width  int
   Height int
}

type Rect struct{}

func (r *Rect) GetArea(p Args, ret *int) error {
   *ret = p.Width * p.Height
   return nil
}

func (r *Rect) GetPerimeter(p Args, ret *int) error {
   *ret = (p.Width + p.Height) * 2
   return nil
}

func main() {
   rect := new(Rect)
   //注冊一個rect服務
   rpc.Register(rect)
   //綁定服務到HTTP協議
   rpc.HandleHTTP()
   err := http.ListenAndServe(":8081", nil)
   if err != nil {
      log.Fatal(err)
   }
}

Client.go:

package main

import (
   "fmt"
   "log"
   "net/rpc"
)

type Args struct {
  width  int
   Height int
}

func main() {
   //連接遠程RPC服務
   rpc, err := rpc.DialHTTP("tcp", "127.0.0.1:8081")
   if err != nil {
      log.Fatal(err)
   }
   ret := 0
   //調用服務方法
   err = rpc.Call("Rect.GetArea", Args{50, 100}, &ret)
   if err != nil {
      log.Fatal(err)
   }
   fmt.Println(ret)
   // 調用服務方法
   err = rpc.Call("Rect.GetPerimeter", Args{50, 100}, &ret)
   if err != nil {
      log.Fatal(err)
   }
   fmt.Println(ret)
}

// output:
// 5000
// 300

4、基于TCP的RPC

Server.go:

package main

import (
   "log"
   "net"
   "net/rpc"
)

type Args struct {
  width  int
   Height int
}

type Rect struct{}

func (r *Rect) GetArea(p Args, ret *int) error {
   *ret = p.Width * p.Height
   return nil
}

func (r *Rect) GetPerimeter(p Args, ret *int) error {
   *ret = (p.Width + p.Height) * 2
   return nil
}

func errorHandler(err error) {
   if err != nil {
      log.Fatal(err)
   }
}

func main() {
   rect := new(Rect)
   //注冊RPC服務
   rpc.Register(rect)
   tcpADDR, err := net.ResolveTCPAddr("tcp", "127.0.0.1:8081")
   errorHandler(err)
   //監聽端口
   tcpListen, err := net.ListenTCP("tcp", tcpADDR)
   errorHandler(err)
   // 處理RPC連接請求
   for {
      conn, err := tcpListen.Accept()
      if err != nil {
         continue
      }
      // goroutine處理RPC連接請求
      go rpc.ServeConn(conn)
   }
}

Client.go:

package main

import (
   "fmt"
   "log"
   "net/rpc"
)

type Args struct {
  width  int
   Height int
}

func main() {
   //連接遠程RPC服務
   rpc, err := rpc.Dial("tcp", "127.0.0.1:8081")
   if err != nil {
      log.Fatal(err)
   }
   ret := 0
   //調用服務方法
   err = rpc.Call("Rect.GetArea", Args{50, 100}, &ret)
   if err != nil {
      log.Fatal(err)
   }
   fmt.Println(ret)
   err = rpc.Call("Rect.GetPerimeter", Args{50, 100}, &ret)
   if err != nil {
      log.Fatal(err)
   }
   fmt.Println(ret)
}

// output:
// 5000
// 300

5、基于JSON的RPC

JSON RPC方式使用json進行數據編解碼,而不是gob編碼。
Server.go:

package main

import (
   "log"
   "net"
   "net/rpc"
   "net/rpc/jsonrpc"
)

type Args struct {
  width  int
   Height int
}

type Rect struct{}

func (r *Rect) GetArea(p Args, ret *int) error {
   *ret = p.Width * p.Height
   return nil
}

func (r *Rect) GetPerimeter(p Args, ret *int) error {
   *ret = (p.Width + p.Height) * 2
   return nil
}

func errorHandler(err error) {
   if err != nil {
      log.Fatal(err)
   }
}

func main() {
   rect := new(Rect)
   //注冊RPC服務
   rpc.Register(rect)
   tcpAddr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:8081")
   errorHandler(err)
   //監聽端口
   tcpListen, err := net.ListenTCP("tcp", tcpAddr)
   errorHandler(err)
   for {
      conn, err := tcpListen.Accept()
      if err != nil {
         continue
      }
      //處理RPC連接請求
      go jsonrpc.ServeConn(conn)
   }
}

Client.go:

package main

import (
   "fmt"
   "log"
   "net/rpc/jsonrpc"
)

type Args struct {
  width  int
   Height int
}

func main() {
   //連接遠程RPC服務
   rpc, err := jsonrpc.Dial("tcp", "127.0.0.1:8081")
   if err != nil {
      log.Fatal(err)
   }
   ret := 0
   //調用服務方法
   err = rpc.Call("Rect.GetArea", Args{50, 100}, &ret)
   if err != nil {
      log.Fatal(err)
   }
   fmt.Println(ret)
   err = rpc.Call("Rect.GetPerimeter", Args{50, 100}, &ret)
   if err != nil {
      log.Fatal(err)
   }
   fmt.Println(ret)
}

// output:
// 5000
// 300

另外有需要云服務器可以了解下創新互聯scvps.cn,海內外云服務器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務器、裸金屬服務器、高防服務器、香港服務器、美國服務器、虛擬主機、免備案服務器”等云主機租用服務以及企業上云的綜合解決方案,具有“安全穩定、簡單易用、服務可用性高、性價比高”等特點與優勢,專為企業上云打造定制,能夠滿足用戶豐富、多元化的應用場景需求。

本文題目:Go語言開發(十六)、Go語言常用標準庫六-創新互聯
網站地址:http://vcdvsql.cn/article28/isijp.html

成都網站建設公司_創新互聯,為您提供企業網站制作移動網站建設網站排名面包屑導航網站設計網站維護

廣告

聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯

h5響應式網站建設