针对这本书做了一些细致化的改进
This commit is contained in:
3
2.2.md
3
2.2.md
@@ -443,9 +443,6 @@ slice有一些简便的操作
|
|||||||
bool false
|
bool false
|
||||||
string ""
|
string ""
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## links
|
## links
|
||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一章: [你好,Go](<2.1.md>)
|
* 上一章: [你好,Go](<2.1.md>)
|
||||||
|
|||||||
56
2.3.md
56
2.3.md
@@ -455,6 +455,62 @@ Go程序会自动调用`init()`和`main()`,所以你不需要在任何地方
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
### import
|
||||||
|
我们在写Go代码的时候经常用到import这个命令用来导入包文件,而我们经常看到的方式参考如下:
|
||||||
|
|
||||||
|
import(
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
然后我们代码里面可以通过如下的方式调用
|
||||||
|
|
||||||
|
fmt.Println("hello world")
|
||||||
|
|
||||||
|
上面这个fmt是Go语言的标准库,其实是去goroot下去加载该模块,当然Go的import还支持如下两种方式来加载自己写的模块:
|
||||||
|
|
||||||
|
1. 相对路径
|
||||||
|
|
||||||
|
import “./model” //当前文件同一目录的model目录,但是不建议这种方式来import
|
||||||
|
|
||||||
|
2. 绝对路径
|
||||||
|
|
||||||
|
import “shorturl/model” //加载gopath/src/shorturl/model模块
|
||||||
|
|
||||||
|
|
||||||
|
上面展示了一些import常用的几种方式,但是还有一些特殊的import,让很多新手很费解,下面我们来一一讲解一下到底是怎么一回事
|
||||||
|
|
||||||
|
|
||||||
|
1. 点操作
|
||||||
|
|
||||||
|
我们有时候会看到如下的方式导入包
|
||||||
|
|
||||||
|
import(
|
||||||
|
. "fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
这个点操作的含义就是这个包导入之后在你调用这个包的函数时,你可以省略前缀的包名,也就是前面你调用的fmt.Println("hello world")可以省略的写成Println("hello world")
|
||||||
|
|
||||||
|
2. 别名操作
|
||||||
|
|
||||||
|
别名操作顾名思义我们可以把包命名成另一个我们用起来容易记忆的名字
|
||||||
|
|
||||||
|
import(
|
||||||
|
f "fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
别名操作的话调用包函数时前缀变成了我们的前缀,即f.Println("hello world")
|
||||||
|
|
||||||
|
3. _操作
|
||||||
|
|
||||||
|
这个操作经常是让很多人费解的一个操作符,请看下面这个import
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
_ "github.com/ziutek/mymysql/godrv"
|
||||||
|
)
|
||||||
|
|
||||||
|
_操作其实是引入该包,而不直接使用包里面的函数,而是调用了该包里面的init函数。
|
||||||
|
|
||||||
|
|
||||||
## links
|
## links
|
||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
|
|||||||
67
2.7.md
67
2.7.md
@@ -2,15 +2,15 @@
|
|||||||
|
|
||||||
有人把Go比作21世纪的C语言,第一是因为Go语言设计简单,第二,21世纪最重要的就是并行程序设计,而GO从语言层面就支持了并行。
|
有人把Go比作21世纪的C语言,第一是因为Go语言设计简单,第二,21世纪最重要的就是并行程序设计,而GO从语言层面就支持了并行。
|
||||||
|
|
||||||
## Goroutines
|
## goroutine
|
||||||
|
|
||||||
Goroutines是Go并行设计的核心。Goroutines说到底其实就是线程,但是他比线程更小,十几个Goroutines可能体现在底层就是五六个线程,Go语言内部帮你实现了这些Goroutines之间的内存共享。Go语言的作者经常说着这样一句话,不要通过共享来通信,而要通过通信来共享。
|
goroutine是Go并行设计的核心。goroutine说到底其实就是线程,但是他比线程更小,十几个goroutine可能体现在底层就是五六个线程,Go语言内部帮你实现了这些goroutine之间的内存共享。执行goroutine只需极少的栈内存(大概是4~5KB),当然会根据相应的数据伸缩。也正因为如此,可同时运行成千上万个并发任务。goroutine比thread更易用、更高效、更轻便。
|
||||||
|
|
||||||
Goroutines是通过Go的runtime管理的一个线程管理器。Goroutines通过`go`关键字实现了,其实就是一个普通的函数。
|
goroutine是通过Go的runtime管理的一个线程管理器。goroutine通过`go`关键字实现了,其实就是一个普通的函数。
|
||||||
|
|
||||||
go hello(a, b, c)
|
go hello(a, b, c)
|
||||||
|
|
||||||
通过关键字go就启动了一个Goroutines。我们来看一个例子
|
通过关键字go就启动了一个goroutine。我们来看一个例子
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
@@ -43,9 +43,14 @@ Goroutines是通过Go的runtime管理的一个线程管理器。Goroutines通过
|
|||||||
hello
|
hello
|
||||||
|
|
||||||
我们可以看到go关键字很方便的就实现了并发编程。
|
我们可以看到go关键字很方便的就实现了并发编程。
|
||||||
runtime.Gosched()表示让CPU把时间片让给别人。
|
上面的多个goroutine运行在同一个进程里面,共享内存数据,不过设计上我们要遵循:不要通过共享来通信,而要通过通信来共享。
|
||||||
|
|
||||||
|
> runtime.Gosched()表示让CPU把时间片让给别人,结束当前的goroutine。
|
||||||
|
|
||||||
|
>默认情况下,调度器仅使用单线程,也就是说只实现了并发。想要发挥多核处理器的并行,需要在我们的程序中显示的调用 runtime.GOMAXPROCS(n) 告诉调度器同时使用多个线程。GOMAXPROCS 设置了同时运行逻辑代码的系统线程的最大数量,并返回之前的设置。如果n < 1,不会改变当前设置。以后Go的新版本中调度得到改进后,这将被移除。这里有一篇rob介绍的关于并发和并行的文章:http://concur.rspace.googlecode.com/hg/talk/concur.html#landing-slide
|
||||||
|
|
||||||
## channels
|
## channels
|
||||||
Goroutines运行在相同的地址空间,因此访问共享内存必须做好同步。那么Goroutines之间如何进行数据的通信呢,Go提供了一个很好的通信机制channel。channel可以与Unix shell 中的双向管道做类比:可以通过它发送或者接收值。这些值只能是特定的类型:channel类型。定义一个channel 时,也需要定义发送到channel 的值的类型。注意,必须使用make 创建channel:
|
goroutine运行在相同的地址空间,因此访问共享内存必须做好同步。那么goroutine之间如何进行数据的通信呢,Go提供了一个很好的通信机制channel。channel可以与Unix shell 中的双向管道做类比:可以通过它发送或者接收值。这些值只能是特定的类型:channel类型。定义一个channel时,也需要定义发送到channel的值的类型。注意,必须使用make 创建channel:
|
||||||
|
|
||||||
ci := make(chan int)
|
ci := make(chan int)
|
||||||
cs := make(chan string)
|
cs := make(chan string)
|
||||||
@@ -81,7 +86,7 @@ channel通过操作符`<-`来接收和发送数据
|
|||||||
fmt.Println(x, y, x + y)
|
fmt.Println(x, y, x + y)
|
||||||
}
|
}
|
||||||
|
|
||||||
默认情况下,channel接收和发送数据都是阻塞的,除非另一端已经准备好,这样就使得Goroutines同步变的更加的简单,而不需要显式的lock。所谓阻塞,也就是如果读取(value := <-ch)它将会被阻塞,直到有数据接收。其次,任何发送(ch<-5)将会被阻塞,直到数据被读出。无缓冲channel 是在多个goroutine之间同步很棒的工具。
|
默认情况下,channel接收和发送数据都是阻塞的,除非另一端已经准备好,这样就使得Goroutines同步变的更加的简单,而不需要显式的lock。所谓阻塞,也就是如果读取(value := <-ch)它将会被阻塞,直到有数据接收。其次,任何发送(ch<-5)将会被阻塞,直到数据被读出。无缓冲channel是在多个goroutine之间同步很棒的工具。
|
||||||
|
|
||||||
## Buffered Channels
|
## Buffered Channels
|
||||||
上面我们介绍了默认的非缓存类型的channel,不过Go也允许指定channel的缓冲大小,很简单,就是channel可以存储多少元素。ch:= make(chan bool, 4),创建了可以存储4个元素的bool 型channel。在这个channel 中,前4个元素可以无阻塞的写入。当写入第5个元素时,代码将会阻塞,直到其他goroutine从channel 中读取一些元素,腾出空间。
|
上面我们介绍了默认的非缓存类型的channel,不过Go也允许指定channel的缓冲大小,很简单,就是channel可以存储多少元素。ch:= make(chan bool, 4),创建了可以存储4个元素的bool 型channel。在这个channel 中,前4个元素可以无阻塞的写入。当写入第5个元素时,代码将会阻塞,直到其他goroutine从channel 中读取一些元素,腾出空间。
|
||||||
@@ -172,8 +177,6 @@ channel通过操作符`<-`来接收和发送数据
|
|||||||
fibonacci(c, quit)
|
fibonacci(c, quit)
|
||||||
}
|
}
|
||||||
|
|
||||||
虽然goroutine是并发执行的,但是它们并不是并行运行的。如果不告诉Go额外的东西,同一时刻只会有一个goroutine执行逻辑代码。利用runtime.GOMAXPROCS(n)可以设置goroutine并行执行的数量。GOMAXPROCS 设置了同时运行逻辑代码的系统线程 的最大数量,并返回之前的设置。如果n < 1,不会改变当前设置。以后Go的新版本中调度得到改进后,这将被移除。
|
|
||||||
|
|
||||||
在`select`里面还有default语法,`select`其实就是类似switch的功能,default就是当监听的channel都没有准备好的时候,默认执行的(select不再阻塞等待channel)。
|
在`select`里面还有default语法,`select`其实就是类似switch的功能,default就是当监听的channel都没有准备好的时候,默认执行的(select不再阻塞等待channel)。
|
||||||
|
|
||||||
select {
|
select {
|
||||||
@@ -183,6 +186,52 @@ channel通过操作符`<-`来接收和发送数据
|
|||||||
// 当c阻塞的时候执行这里
|
// 当c阻塞的时候执行这里
|
||||||
}
|
}
|
||||||
|
|
||||||
|
## 超时
|
||||||
|
有时候会出现goroutine阻塞的情况,那么我们如何避免整个的程序进入阻塞的情况呢?我们可以利用select来设置超时,通过如下的方式实现:
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
c := make(chan int)
|
||||||
|
o := make(chan bool)
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case v := <- c:
|
||||||
|
println(v)
|
||||||
|
case <- time.After(5 * time.Second):
|
||||||
|
println("timeout")
|
||||||
|
o <- true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
<- o
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
## runtime goroutine
|
||||||
|
runtime包中有几个处理goroutine的函数:
|
||||||
|
|
||||||
|
- Goexit
|
||||||
|
|
||||||
|
退出当前执行的goroutine,但是defer函数还会继续调用
|
||||||
|
|
||||||
|
- Gosched
|
||||||
|
|
||||||
|
让出当前goroutine的执行权限,调度器安排其他等待的任务运行,并在下次某个时候从该位置恢复执行。
|
||||||
|
|
||||||
|
- NumCPU
|
||||||
|
|
||||||
|
返回 CPU 核数量
|
||||||
|
|
||||||
|
- NumGoroutine
|
||||||
|
|
||||||
|
返回正在执⾏行和排队的任务总数
|
||||||
|
|
||||||
|
- GOMAXPROCS
|
||||||
|
|
||||||
|
用来设置可以运行的CPU核数
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## links
|
## links
|
||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
|
|||||||
2
7.4.md
2
7.4.md
@@ -345,4 +345,4 @@ Unix用户已经很熟悉什么是`pipe`了,`ls | grep "beego"`类似这样的
|
|||||||
## links
|
## links
|
||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [正则处理](<7.3.md>)
|
* 上一节: [正则处理](<7.3.md>)
|
||||||
* 下一节: [小结](<7.5.md>)
|
* 下一节: [文件操作](<7.5.md>)
|
||||||
|
|||||||
152
7.5.md
152
7.5.md
@@ -1,7 +1,153 @@
|
|||||||
# 7.5 小结
|
# 7.5 文件操作
|
||||||
这一章给大家介绍了一些文本处理的工具,包括XML、JSON、正则和模板技术,XML和JSON是数据交互的工具,通过XML和JSON你可以表达各种含义,通过正则你可以处理文本(搜索、替换、截取),通过模板技术你可以展现这些数据给用户。这些都是你开发Web应用过程中需要用到的技术,通过这个小节的介绍你能够了解如何处理文本、展现文本。
|
在任何计算机设备中,文件是都是必须的对象,而在Web编程中,文件的操作一直是Web程序员经常遇到的问题,文件操作在Web应用中是必须的,非常有用的,我们经常遇到生成文件目录,文件(夹)编辑等操作,现在我把Go中的这些操作做一详细总结并实例示范如何使用。
|
||||||
|
## 目录操作
|
||||||
|
文件操作的大多数函数都是在os包里面,下面列举了几个目录操作的:
|
||||||
|
|
||||||
|
- func Mkdir(name string, perm FileMode) error
|
||||||
|
|
||||||
|
创建名称为name的目录,权限设置是perm,例如0777
|
||||||
|
|
||||||
|
- func MkdirAll(path string, perm FileMode) error
|
||||||
|
|
||||||
|
根据path创建多级子目录,例如astaxie/test1/test2。
|
||||||
|
|
||||||
|
- func Remove(name string) error
|
||||||
|
|
||||||
|
删除名称为name的目录,当目录下有文件或者其他目录是会出错
|
||||||
|
|
||||||
|
- func RemoveAll(path string) error
|
||||||
|
|
||||||
|
根据path删除多级子目录,如果path是单个名称,那么该目录不删除。
|
||||||
|
|
||||||
|
|
||||||
|
下面是演示代码:
|
||||||
|
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
## 文件操作
|
||||||
|
|
||||||
|
### 建立与打开文件
|
||||||
|
新建文件可以通过如下两个方法
|
||||||
|
|
||||||
|
- func Create(name string) (file *File, err Error)
|
||||||
|
|
||||||
|
根据提供的文件名创建新的文件,返回一个文件对象,默认权限是0666的文件,返回的文件对象是可读写的。
|
||||||
|
|
||||||
|
- func NewFile(fd uintptr, name string) *File
|
||||||
|
|
||||||
|
根据文件描述符创建相应的文件,返回一个文件对象
|
||||||
|
|
||||||
|
|
||||||
|
通过如下两个方法来打开文件:
|
||||||
|
|
||||||
|
- func Open(name string) (file *File, err Error)
|
||||||
|
|
||||||
|
该方法打开一个名称为name的文件,但是是只读方式,内部实现其实调用了OpenFile。
|
||||||
|
|
||||||
|
- func OpenFile(name string, flag int, perm uint32) (file *File, err Error)
|
||||||
|
|
||||||
|
打开名称为name的文件,flag是打开的方式,只读、读写等,perm是权限
|
||||||
|
|
||||||
|
### 写文件
|
||||||
|
写文件函数:
|
||||||
|
|
||||||
|
- func (file *File) Write(b []byte) (n int, err Error)
|
||||||
|
|
||||||
|
写入byte类型的信息到文件
|
||||||
|
|
||||||
|
- func (file *File) WriteAt(b []byte, off int64) (n int, err Error)
|
||||||
|
|
||||||
|
在指定位置开始写入byte类型的信息
|
||||||
|
|
||||||
|
- func (file *File) WriteString(s string) (ret int, err Error)
|
||||||
|
|
||||||
|
写入string信息到文件
|
||||||
|
|
||||||
|
写文件的示例代码
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
userFile := "astaxie.txt"
|
||||||
|
fout, err := os.Create(userFile)
|
||||||
|
defer fout.Close()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(userFile, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
fout.WriteString("Just a test!\r\n")
|
||||||
|
fout.Write([]byte("Just a test!\r\n"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
### 读文件
|
||||||
|
读文件函数:
|
||||||
|
|
||||||
|
- func (file *File) Read(b []byte) (n int, err Error)
|
||||||
|
|
||||||
|
读取数据到b中
|
||||||
|
|
||||||
|
- func (file *File) ReadAt(b []byte, off int64) (n int, err Error)
|
||||||
|
|
||||||
|
从off开始读取数据到b中
|
||||||
|
|
||||||
|
读文件的示例代码:
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
userFile := "asatxie.txt"
|
||||||
|
fl, err := os.Open(userFile)
|
||||||
|
defer fl.Close()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(userFile, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
buf := make([]byte, 1024)
|
||||||
|
for {
|
||||||
|
n, _ := fl.Read(buf)
|
||||||
|
if 0 == n {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
os.Stdout.Write(buf[:n])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
### 删除文件
|
||||||
|
Go语言里面删除文件和删除文件夹是同一个函数
|
||||||
|
|
||||||
|
- func Remove(name string) Error
|
||||||
|
|
||||||
|
调用该函数就可以删除文件名为name的文件
|
||||||
|
|
||||||
## links
|
## links
|
||||||
* [目录](<preface.md>)
|
* [目录](<preface.md>)
|
||||||
* 上一节: [模板处理](<7.4.md>)
|
* 上一节: [模板处理](<7.4.md>)
|
||||||
* 下一节: [Web服务](<8.md>)
|
* 下一节: [小结](<7.6.md>)
|
||||||
7
7.6.md
Normal file
7
7.6.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# 7.5 小结
|
||||||
|
这一章给大家介绍了一些文本处理的工具,包括XML、JSON、正则和模板技术,XML和JSON是数据交互的工具,通过XML和JSON你可以表达各种含义,通过正则你可以处理文本(搜索、替换、截取),通过模板技术你可以展现这些数据给用户。这些都是你开发Web应用过程中需要用到的技术,通过这个小节的介绍你能够了解如何处理文本、展现文本。
|
||||||
|
|
||||||
|
## links
|
||||||
|
* [目录](<preface.md>)
|
||||||
|
* 上一节: [文件操作](<7.5.md>)
|
||||||
|
* 下一节: [Web服务](<8.md>)
|
||||||
2
7.md
2
7.md
@@ -1,7 +1,7 @@
|
|||||||
# 7 文本处理
|
# 7 文本处理
|
||||||
Web开发中对于文本处理是非常重要的一部分,我们往往需要对输出或者输入的内容进行处理,这里的文本包括字符串、数字、Json、XMl等等。Go语言作为一门高性能的语言,对这些文本的处理都有官方的标准库来支持。而且在你使用中你会发现Go标准库的一些设计相当的巧妙,而且对于使用者来说也很方便就能处理这些文本。本章我们将通过四个小节的介绍,让用户对Go语言处理文本有一个很好的认识。
|
Web开发中对于文本处理是非常重要的一部分,我们往往需要对输出或者输入的内容进行处理,这里的文本包括字符串、数字、Json、XMl等等。Go语言作为一门高性能的语言,对这些文本的处理都有官方的标准库来支持。而且在你使用中你会发现Go标准库的一些设计相当的巧妙,而且对于使用者来说也很方便就能处理这些文本。本章我们将通过四个小节的介绍,让用户对Go语言处理文本有一个很好的认识。
|
||||||
|
|
||||||
XML是目前很多标准接口的交互语言,很多时候和一些Java编写的webserver进行交互都是基于XML标准进行交互,7.1小节将介绍如何处理XML文本,我们使用XML之后发现它太复杂了,现在很多互联网企业对外的API大多数采用了JSON格式,这种格式描述简单,但是又能很好的表达意思,7.2小节我们将讲述如何来处理这样的JSON格式数据。正则是一个让人又爱又恨的工具,它处理文本的能力非常强大,我们在前面表单验证里面已经有所领略它的强大,7.3小节将详细的更深入的讲解如何利用好Go的正则。Web开发中一个很重要的部分就是MVC分离,在Go语言的Web开发中V有一个专门的包来支持`template`,7.4小节将详细的讲解如何使用模版来进行输出内容。
|
XML是目前很多标准接口的交互语言,很多时候和一些Java编写的webserver进行交互都是基于XML标准进行交互,7.1小节将介绍如何处理XML文本,我们使用XML之后发现它太复杂了,现在很多互联网企业对外的API大多数采用了JSON格式,这种格式描述简单,但是又能很好的表达意思,7.2小节我们将讲述如何来处理这样的JSON格式数据。正则是一个让人又爱又恨的工具,它处理文本的能力非常强大,我们在前面表单验证里面已经有所领略它的强大,7.3小节将详细的更深入的讲解如何利用好Go的正则。Web开发中一个很重要的部分就是MVC分离,在Go语言的Web开发中V有一个专门的包来支持`template`,7.4小节将详细的讲解如何使用模版来进行输出内容。7.5小节讲详细介绍如何进行文件和文件夹的操作。
|
||||||
|
|
||||||
## 目录
|
## 目录
|
||||||
* 1. [XML处理](7.1.md)
|
* 1. [XML处理](7.1.md)
|
||||||
|
|||||||
@@ -40,12 +40,13 @@
|
|||||||
- 6.3 [session存储](6.3.md)
|
- 6.3 [session存储](6.3.md)
|
||||||
- 6.4 [预防session劫持](6.4.md)
|
- 6.4 [预防session劫持](6.4.md)
|
||||||
- 6.5 [小结](6.5.md)
|
- 6.5 [小结](6.5.md)
|
||||||
* 7.[文本处理](7.md)
|
* 7.[文本文件处理](7.md)
|
||||||
- 7.1 [XML处理](7.1.md)
|
- 7.1 [XML处理](7.1.md)
|
||||||
- 7.2 [JSON处理](7.2.md)
|
- 7.2 [JSON处理](7.2.md)
|
||||||
- 7.3 [正则处理](7.3.md)
|
- 7.3 [正则处理](7.3.md)
|
||||||
- 7.4 [模板处理](7.4.md)
|
- 7.4 [模板处理](7.4.md)
|
||||||
- 7.5 [小结](7.5.md)
|
- 7.5 [文件操作](7.5.md)
|
||||||
|
- 7.6 [小结](7.6.md)
|
||||||
* 8.[Web服务](8.md)
|
* 8.[Web服务](8.md)
|
||||||
- 8.1 [Socket编程](8.1.md)
|
- 8.1 [Socket编程](8.1.md)
|
||||||
- 8.2 [WebSocket](8.2.md)
|
- 8.2 [WebSocket](8.2.md)
|
||||||
|
|||||||
Reference in New Issue
Block a user