golang中的print系函数详解

golang中的print系函数详解 pirnt系函数来自fmt包,主要用于做各种格式的输出 这些函数主要有 golang中的print系函数详解 fmt.Fprintf fmt.Printf fmt.Sprintf fmt.Fprint fmt.Print fmt.Sprint fmt.Fprintln fmt.Println fmt.Sprintln 总结 下面来逐个分析 import ( "fmt" "os" "io" ) fmt.Fprintf 函数原型: Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) 官方注释 Fprintf formats according to a format specifier and writes to w.It returns the number of bytes written and any write error encountered. Arguement fmt.Fprintf() 依据指定的格式向第一个参数内写入字符串,第一参数必须实现了 io....

February 25, 2021 · 5 min · 李昌

Go的http包详解

Go的http包详解 详细地解剖一下 http 包,看它到底是怎样实现整个过程的。 Go 的 http 有两个核心功能:Conn、ServeMux Conn的goroputine 为了实现高并发和高性能,go使用了goroutine来处理Conn的读写事件,这样每个请求都能保持独立,相互不会阻塞,可以高效的相应网络事件。 go在等待客户端请求中是这样的: c, err := srv.newConn(rw) if err != nil { continue } go c.serve() 可以看到,客户端的每次请求都会创建一个Conn,这个Conn里面保存了该次请求的信息,然后再传递到相应的handler,该handler中便可以读取到相应的header信息,这样保证了每个请求的独立性。 ServeMux的自定义 conn.server内部调用了http包默认的路由器,通过路由器把本次请求的信息传递到了后端的处理函数,那么这个路由器是怎么实现的呢? 它的结构如下: type ServeMux struct{ mu sync.RWMutext // 锁,请求涉及到并发处理,因此需要一个锁机制 m map[string]muxEntry // 路由规则,一个String对应一个mux实体,这里的String就是注册的一个路由表达式 hosts bool // 是否在任意的规则中带有host信息 } 下面看一下muxEntry type muxEntry struct { explicit bool // 是否精确匹配 h Handler // 这个路由表达式对应哪个handler pattern string // 匹配字符串 } 在看一下Handler的定义 type Handler interface { ServeHTTP(ResponseWriter, *Request) // 路由实现器 } Handler是一个接口,但是附中的sayhelloName函数中并没有实现ServeHTTP这个接口,为什么能添加呢?这是因为http包里面还定义了一个类型HandlerFunc,定义的函数sayhelloName就是这个HandlerFunc调用之后的结果,这个类型默认就实现了ServeHTTP这个方法,即我们调用了HandlerFunc(f),强制类型转换f成为HandlerFunc类型,这样f就拥有了ServeHTTP方法。...

February 25, 2021 · 2 min · 李昌

Go语言中值类型与引用类型

1.值类型与引用类型 值类型:int、float、bool和string这些类型都属于值类型,使用这些类型的变量直接指向存在内存中的值,值类型的变量的值存储在栈中。当使用等号=将一个变量的值赋给另一个变量时,如 j = i ,实际上是在内存中将 i 的值进行了拷贝。可以通过 &i 获取变量 i 的内存地址 引用类型:特指slice、map、channel这三种预定义类型。引用类型拥有更复杂的存储结构:(1)分配内存 (2)初始化一系列属性等。一个引用类型的变量r1存储的是r1的值所在的内存地址(数字),或内存地址中第一个字所在的位置,这个内存地址被称之为指针,这个指针实际上也被存在另外的某一个字中 2.值类型与引用类型的区别 首先明确,golang中无论是什么类型,传参时都是传递的原值的复制,因此,值类型直接传入函数,任何变动都会反映到原值上,而对于引用类型,其传递的是原值类型的一个复制,因此在函数内的修改,可能会反映到原值中,也可能不会。具体取决引用类型的构造方式及其内部定义。可参考另一篇文章slice和数组的区别 2.1.值类型 //先定义一个数组 var a = [5]int{1, 2, 3, 4, 5} //定义一个函数,将数组中的第一个值设为0 func change(a [5]int){ a[0] = 0 fmt.Println(a) } change(a) fmt.Println(a) 输出: [0 2 3 4 5] [1 2 3 4 5] 可以看到,数组在函数内部被变成{0,1,2,3,4},但当函数结束,还是原来的值没有变。 2.2 引用类型 map的构造函数返回的是一个指针,指向map对象,因此对于map的任何操作都会反映到原map中 // 定义一个map var dit = make(map[string]int) dit["one"] = 1 fmt.Println(dit) // 传参并做改变 func change(dit map[string]int){ dit["two"] = 2 fmt....

February 25, 2021 · 1 min · 李昌

Go语言中的字面量

Go语言中的字面量 Go语言中的字面量 什么是字面量 整型和浮点型的字面值 字符串的字面值 常量的字面值 数组的字面值 Slice的字面值 Map的字面值 结构体的字面值 什么是字面量 在计算机科学中,字面量(literal)是用于表达源代码中一个固定值的表示法(notation)。 简单的说,字面量或者说字面值就是一个变量的值。 整型和浮点型的字面值 var i int = 1 var f float64 = 3.14159 字符串的字面值 字符串值也可以用字符串面值方式编写,只要将一系列字节序列包含在双引号即可: "Hello 世界" `世界` 世界 一个原生的字符串面值形式是 `...` 使用反引号代替双引号。在原生的字符串面值中,没有转义操作;全部的内容都是字面的意思,包含退格和换行,因此一个程序中的原生字符串面值可能跨越多行(译注:在原生字符串面值内部是无法直接写```````字符的,可以用八进制或十六进制转义或+"`"链接字符串常量完成)。唯一的特殊处理是会删除回车以保证在所有平台上的值都是一样的,包括那些把回车也放入文本文件的系统(译注:Windows系统会把回车和换行一起放入文本文件中)。 原生字符串面值用于编写正则表达式会很方便,因为正则表达式往往会包含很多反斜杠。原生字符串面值同时被广泛应用于HTML模板、JSON面值、命令行提示信息以及那些需要扩展到多行的场景。 const GoUsage = `Go is a tool for managing Go source code. Usage: go command [arguments] ...` GoUsage Go is a tool for managing Go source code. Usage: go command [arguments] ....

February 25, 2021 · 1 min · 李昌

Go语言中的错误处理策略

0. 错误处理的编码风格 检查某个子函数是否失败后,我们通常将处理失败的逻辑代码放在处理成功的代码之前。如果某个错误会导致函数返回,那么成功的逻辑代码不应该放在else中,而应直接放在函数体中。 1. 错误传播 函数某个子程序的失败,会变成该函数的失败 resp, err := http.Get(url) if err != nil{ return nill, err } 或是构造新的错误信息返回给调用者 doc, err := html.Parse(resp.Body) resp.Body.Close() if err != nil { return nil, fmt.Errorf("parsing %s as HTML: %v", url,err) } 一般而言,被调函数f(x)会将调用信息和参数信息作为发生错误时的上下文放在错误信息中并返回给调用者,调用者需要添加一些错误信息中不包含的信息。 2. 重试失败的操作 如果错误的发生是偶然的,或由不可预知的问题导致的。此时可重新尝试失败的操作,但是在重试时,要限制重试的时间间隔或重试的时间次数,防止无限制的重试。 func WaitForServer(url string) error { const timeout = 1 * time.Minute deadline := time.Now().Add(timeout) for tries := 0; time.Now().Before(deadline); tries++ { _, err := http.Head(url) if err == nil { return nil // success } log....

February 25, 2021 · 1 min · 李昌

http/template

http/template 什么是模板 模板是一种常见的视图,通过它我们可以传递数据以使该视图有意义。可以以任何方式对其进行自定义以获取任何可能的输出。 模板包 Go中的模板附带两个包text/template和html/template。文本包允许我们使用模板插入文本,而HTML模板通过提供安全的HTML代码来帮助我们。 Part of template 1. 模板动作 模板动作是主要的控制流程,数据评估功能。这些动作控制最终输出将如何显示 {{ /* a comment isside template */ }} 2. 控制结构 控制结构确定模板的控制流程,有助于产生结构化的输出,以下是模板中的一些控制结构 if语句 {{ if .condition }} {{ else }} {{ end }} 循环块 {{ range .Items }} {{ end }} 3. 功能 函数也可以在模板内部使用,可以使用管道符|来使用预定义的函数 如何预定义函数 下面的代码创建并分析上面定义的模板templ。注意方法调用链的顺序:template.New先创建并返回一个模板;Funcs方法将daysAgo等自定义函数注册到模板中,并返回模板;最后调用Parse函数分析模板。 report, err := template.New("report").Funcs(template.FuncMap{"daysAgo": daysAgo}).Parse(templ) if err != nil { log.Fatal(err) } 在Go中解析模板 现在,我们来解析一些文本和HTML模板 1. 访问数据 要访问传递的数据,使用点.,如下所示: {{ .data }} 2. 解析文本模板 现在,来解析一个文本模板...

February 25, 2021 · 2 min · 李昌

RESTfulAPI入门

RESTful API 入门 1. 简介 表现层状态转换(英语:Representational State Transfer,缩写:REST)是Roy Thomas Fielding博士于2000年在他的博士论文中提出来的一种万维网软件架构风格,目的是便于不同软件/程序在网络(例如互联网)中互相传递信息。表现层状态转换是根基于超文本传输协议(HTTP)之上而确定的一组约束和属性,是一种设计提供万维网络服务的软件构建风格。符合或兼容于这种架构风格(简称为 REST 或 RESTful)的网络服务,允许客户端发出以统一资源标识符访问和操作网络资源的请求,而与预先定义好的无状态操作集一致化。因此表现层状态转换提供了在互联网络的计算系统之间,彼此资源可交互使用的协作性质(interoperability)。相对于其它种类的网络服务,例如SOAP服务,则是以本身所定义的操作集,来访问网络上的资源。 2. REST 架构约束 客户端-服务器 从本质上讲,这意味着客户端应用程序和服务器应用程序必须能够独立发展而彼此之间没有任何依赖关系。客户端应该只知道资源URI,仅此而已。今天,这是Web开发中的常规做法,因此您不需要任何花哨。把事情简单化。 服务器和客户端也可以独立替换和开发,只要它们之间的接口没有更改即可。 无状态 Roy fielding的灵感来自HTTP,因此它反映了这一约束。使所有客户端-服务器交互都变为无状态。服务器将不存储有关客户端发出的最新HTTP请求的任何内容。它将每个请求视为新请求。没有会议,没有历史。 如果客户端应用程序需要是最终用户的有状态应用程序,则用户必须登录一次并在此之后执行其他授权操作,则来自客户端的每个请求都应包含服务于该请求的所有必要信息,包括身份验证和授权细节。 请求之间不得在服务器上存储任何客户端上下文。客户端负责管理应用程序的状态。 统一的接口 在约束名称本身适用的情况下,您必须为系统内部暴露给API使用者并认真遵循的资源确定API接口。系统中的资源应仅具有一个逻辑URI,并且应提供一种获取相关或附加数据的方式。最好将资源与网页同义。 任何单个资源都不应太大,并在其表示中包含所有内容。只要相关,资源应包含指向相对URI的链接(HATEOAS),以获取相关信息。 此外,整个系统上的资源表示应遵循特定的准则,例如命名约定,链接格式或数据格式(XML或/和JSON)。 所有资源都应通过通用方法(例如HTTP GET)进行访问,并使用一致的方法进行类似的修改。 一旦开发人员熟悉您的一个API,他就应该能够对其他API遵循类似的方法。 分层系统 REST允许您使用分层的系统架构,在该架构中,您可以在服务器A上部署API,并在服务器B上存储数据并在服务器C中对请求进行身份验证。客户端通常无法确定它是直接连接到最终服务器还是中​​间连接。 可缓存的 在当今世界中,缓存数据和响应在任何适用/可能的地方都至关重要。我们阅读的网页也是HTML页面的缓存版本。缓存可以提高客户端的性能,并为服务器提供更好的可伸缩性。 在REST中,缓存应在适用时应用于资源,然后这些资源必须声明自己可缓存。可以在服务器或客户端上实现缓存。 管理良好的缓存部分或完全消除了某些客户端-服务器交互,从而进一步提高了可伸缩性和性能。 按需代码(可选) 好吧,这个约束是可选的。大多数时候,您将以XML或JSON的形式发送资源的静态表示。但是,如果需要,您可以自由地return executable code支持应用程序的一部分,例如,客户端可以调用您的API来获取UI小部件呈现代码。这是允许的。 以上所有约束条件都可以帮助您构建真正的RESTful API,并且应该遵循它们。不过,有时您可能会发现自己违反了一两个约束。别担心; 您仍在制作RESTful API,但不是“真正的RESTful”。 3. REST资源命名指南 在REST中,主要数据表示称为Resource。从长远来看,拥有一个强大且一致的REST资源命名策略–无疑将证明是最佳的设计决策之一。...

February 25, 2021 · 1 min · 李昌

类型断言

类型断言 类型断言是一个使用在接口上的操作,语法上看起来像是x.(T),因此被称为断言类型,这里x是接口,T是类型。一个类型断言检查它操作对象的动态类型是否和断言的类型匹配。 这里有两种可能: 如果断言的类型T是一个具体类型 类型断言检查x的动态类型是否和T相同。如果检查成功了,类型断言的结果是x的动态值,即T。换句话说,具体类型的类型断言从它的操作对象中获得具体的值。如果检查失败,接下来这个操作会panic。 import ( "io" "os" "bytes" "fmt" ) var w io.Writer w = os.Stdout f := w.(*os.File) // 类型检查成功了,所以f的值为os.Stdout f == os.Stdout // true fmt.Printf("%p", f) 输出 0xc0004560c0 c := w.(*bytes.Buffer) // 类型检查失败 输出 interface conversion: <io.Writer> is <*os.File>, not <*bytes.Buffer> 断言的类型T是一个接口类型 t, ok := i.(T) 如果i是类型T(实现了T接口),即检查成功了,那么t将是i的原值,ok为true;如果检查失败了,t将为T类型的零值,ok为false,并且不引发panic。 对一个接口类型的类型断言改变了类型的表述方式,改变了可以获取的方法集合(通常更大), 但是它保护了接口值内部的动态类型和值的部分。 var w io Wirter w = os.Stdout rw := w....

February 25, 2021 · 1 min · 李昌

鸭子类型

鸭子类型 1. 什么是鸭子类型 只要走起路来像鸭子,叫起来像鸭子,就可以认为是鸭子。这就是鸭子类型 2. 鸭子类型有什么作用? 对应于golang中的接口的概念,一个接口定义了一组操作,这组操作可以看做是鸭子的走路,叫。也就是说,只要任何类型满足了这组方法,那么就可以看做是鸭子–即满足了这个接口,可以看做是这个接口类型。

February 25, 2021 · 1 min · 李昌

defer用法

Revise in 2022-3-18 defer用法 defer用来延迟对某个语句的调用,常用于处理成对的操作,如打开、关闭、连接、断开连接,加锁、释放锁。通过defer语句,无论函数逻辑多复杂,都能保证在任何代码执行路径下,资源被释放。defer应该直接跟在请求资源的语句后。 defer语句将函数的调用push到一个列表中,当外层函数返回时,会执行保存的函数列表 举个例子,这个程序打开两个文件并将一个文件的内容复制到另一个文件的函数 func CopyFile(dstName, srcName string) (written int64, err error) { src, err := os.Open(srcName) if err != nil { return } dst, err := os.Create(dstName) if err != nil { return } written, err = io.Copy(dst, src) dst.Close() src.Close() return } 这个函数似乎可以正常工作,但其实存在一个bug,如果对os.Create的调用失败,该函数将返回但却不关闭源文件,通过在第二个return语句中调用src.Close可以解决这个问题。但是如果函数更加复杂,问题可能不会那么容易被发现和解决。通过使用defer语句,可以确保始终关闭文件。 func CopyFile(dstName, srcName string) (written int64, err error) { src, err := os.Open(srcName) if err != nil { return } defer src....

2 min · 李昌