最近动态

PHP

引用符号'&'在foreach循环中的惊喜

工作中总会遇到一些奇奇怪怪的由前人所写下的不可置疑的代码,如果你仅仅跟着眼前所见的代码去理解他人的思路,Well You have fell into a terrible situation.

贴一段示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?php
$students = [
['name'=>'Jackson','age'=>15],
['name'=>'Jerry','age'=>14],
['name'=>'Amy','age'=>14]
];
$classes = [
'CL001'=>['name'=>'Language','classTime'=>30],
'CL002'=>['name'=>'PE','classTime'=>13]
];

/*
* 循环$students,为其添加一个属性'name-age'
* 这里使用了&$v以获得数组内部元素的引用,使的可以在循环体内修改元素内容
*/
foreach ($students as $k=>&$v){
$v['name-age'] = $v['name'] . '-' .$v['age'];
}

$classString = ''; //定义一个空串
/*
* 循环$classes,将所有课程ID和名称联结成串
*/
foreach ($classes as $k=>$v){
$classString .= 'ClassID:'.$k.'-'.$v['name'].PHP_EOL;
}
echo $classString;
var_dump($students); //Think here!

你也许会回答这样一个输出结果:

阅读剩下更多

默认配图
算法

算法时间复杂度的表达-渐进符号与主定理(转载)

渐进符号是分析算法时间复杂度的常用记号,对于某个规模为n的问题,当n足够大时,就可以忽略掉复杂度表达式中的低阶项和最高次项的系数,由此引出“渐进复杂度”,并且用渐进符号来对“渐进复杂度”进行表达。

一、渐进符号

1、O(大O符号):上界
定义:若存在两个正的常数 c 和 n0 , 对于任意 n≥n0 , 都有 T( n)≤cf( n) ,则称T( n) = O( f( n) )(或称算法在 O( f( n))中)。

大 O 符号用来描述增长率的上限,表示 T( n)的增长最多像 f( n)增长的那样快,也就是说, 当输入规模为 n时, 算法消耗时间的最大值,这个上限的阶越低, 结果就越有价值。上界是对算法效率的一种承诺。

大O符号的含义如下图所示:
大O符号的含义

2、Ω(大Ω符号):下界
定义:若存在两个正的常数 c和 n0 ,对于任意 n≥ n0 , 都有 T( n)≥cg( n) ,则称T( n) = Ω( g( n) )(或称算法在 Ω( g( n) )中)。

大 Ω符号用来描述增长率的下限, 也就是说, 当输入规模为 n 时,算法消耗时间的最小值。与大 O 符号对称, 这个下限的阶越高,结果就越有价值。

阅读剩下更多

默认配图
学习笔记

软考选择题-索引文件地址项

【选择题】

设文件索引节点中有8个地址项,每个地址项大小为4字节,其中5个地址项为直接地址索引,2个地址项是一级间接地址索引,1个地址项是二级间接地址索引,磁盘索引块和磁盘数据块大小为1KB。若要访问文件的逻辑块号分别为5和518,则系统应分别采用____;而且可表示的单个文件最大长度是____KB。

第一空选项:

  1. 直接地址索引和一级间接地址索引
  2. 直接地址索引和二级间接地址索引
  3. 一级间接地址索引和二级间接地址索引
  4. 一级间接地址索引和一级间接地址索引

第二空选项:

  1. 517
  2. 1029
  3. 16513
  4. 66053

解答:

由题所知,索引快大小1KB,每个地址项4Byte,则每个索引块包含地址项为1KB/4Byte=256个。

直接地址索引实际拥有5*1个块,块号0~4

一级间接地址索引实际拥有2*256个块,块号5260,261516

二级间接地址索引实际拥有1*256*256个块,块号517~66052

逻辑块号为5的在一级间接地址索引,逻辑块号为518的在二级间接地址索引。

如上计算,文件索引节点中可以有66053个索引块,即最大文件长度为66053*1KB

阅读剩下更多

默认配图
网络安全

免费获得SSL证书-Let's Encrypt

第一步,安装certbot

MacOS版本:

1
brew install certbot

Ubuntu版本:

1
2
3
4
5
6
sudo apt-get update
sudo apt-get install software-properties-common
sudo add-apt-repository universe
sudo add-apt-repository ppa:certbot/certbot
sudo apt-get update
sudo apt-get install certbot python-certbot-nginx

第二步,运行certbot

type-1.仅仅制作证书

阅读剩下更多

默认配图
PHP

PHP加解密算法使用openssl替换mcrypt扩展

PHP版本从7.2开始不再支持mcrypt扩展,所以我们需要使用OpenSSl对其进行替换。本文仅列出部分算法的替换示例,所以不在本文出现的算法或模式需要自行尝试,顺水推舟。

本文替换案例:

  1. MCRYPT_RIJNDAEL_128 | MCRYPT_MODE_ECB => AES-128-ECB
  2. MCRYPT_DES | MCRYPT_MODE_CBC => DES-CBC
  3. MCRYPT_RIJNDAEL_128 | MCRYPT_MODE_CBC => AES-128-CBC
  4. MCRYPT_XTEA | MCRYPT_MODE_CBC

在使用 MCRYPT_RIJNDAEL_128 的地方,如果秘钥长度分别为16、24、32,则加密算法用 AES-128-ECB、AES-192-ECB、AES-256-ECB,BlockSize为16、24、32。

首先列出需要用到的数据填充方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function ZeroPadding($str, $block = 16) {
$pad = $block - (strlen($str) % $block);
if($pad == $block) return $str;
return $str.str_repeat(chr(0),$pad);
}
function ZeroUnPadding($str) {
return rtrim($str, "\0");
}


function PKCS7Padding($str, $block_size) {
$padding_char = $block_size - (strlen($str) % $block_size);
$padding_str = str_repeat(chr($padding_char),$padding_char);
return $str.$padding_str;
}
function PKCS7UnPadding($str) {
$char=substr($str,-1,1);
$num=ord($char);
if($num>0 && $num <= strlen($str)) {
$str = substr($str, 0, -1 * $num);
}
return $str;
}

阅读剩下更多

默认配图
学习笔记

Go-Server/Client以及PHP-Client之间的GRPC初次尝试

写这篇笔记的目的

为了应对后续开发生涯中可能遇到的种种情况以及分布式计算的趋势(讲白了就是后续对工作会很有帮助)。如若总是依赖http-api/restful编写并提供外部调用接口,当接口数量不断上升,文档内容不断增加,这给设计者和使用者都带来非常不好的体验,而RPC在这体就现出了非常大的优势。我将自己的理解和体会以及学习的过程记录在这里,以便今后遇到问题能够从这儿获得些许的线索以及提供一个参考给同道中人。

前提

不妨思考这样一个情形:作为接口设计者,我早已经定义好的接口的请求方式(RESTFul)和返回结构(json),但是每个接口我还需要另外维护一个文档来说明各个接口的用法(请求参数)和解释返回的结果(字段描述)。而对于接口的调用者而言,不但要去文档中查找自己需要的接口并阅读说明,在实际调用中,还要以防接口提供者返回非既定结构的结果而导致的报错。

介绍

Remote Procedure Call(远程过程调用),简称RPC。它可以使得调用远程服务接口如同调用本地方法一样简单。虽然实质还是通过网络通信,但是相比http请求api的网络开销还是极小的,其原因简单来说,HTTP协议每次请求都需要建立TCP连接,就会涉及3次握手的网络开销问题以及冗余报文,而rpc直接使用TCP多路复用(gRPC基于HTTP/2)无需重复建立连接。就文档方面而言,编写一份开发文档足矣,因为接口的定义由接口定义语言(IDL)来完成,而阅读IDL便可理解所有接口,并且通过编译器可以将IDL编译成不同的语言实现源码(gRPC通过protoc编译器将protobuf编译)。其他优点例如:注册、监控、发布等的这里不做论述。

Go-Server

GRPC的官方资源:go get google.golang.org/grpc

GRPC的镜像资源:https://github.com/grpc/grpc

官方的相关示列可以在grpc/examples/中找到。

定义服务

使用protocol buffers去定义gRPC service和方法 request以及 response 的类型。

新增并编辑文件:something.proto

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
syntax = "proto3";

// 声明包/作用域
package something;

// 定义一个服务,名为Wiwider
service Wiwider {
rpc FindUser (UserRequest) returns (UserReply) {} // 声明一个方法
rpc FindUsers (UserRequest) returns (UsersReply) {}
}

// 定义一个请求消息
message UserRequest {
string name = 1;
}

// 定义一个响应消息
message UserReply {
int64 id = 1;
string name = 2;
int32 age = 3;
sexes sex = 4;

enum sexes {
Male = 0;
Female = 1;
}
}

// 定义一个包含消息的消息
message UsersReply {
repeated UserReply users = 1;
}

生成代码

Go-代码

1
protoc --go_out=plugins=grpc:. something.proto

运行这个命令后,会在当前目录生成一个something.pb.go文件,内容包含:

  • 所有用于填充,序列化和获取我们请求和响应消息类型的 protocol buffer 代码
  • 一个为客户端调用定义在Wiwider服务的方法的接口类型(或者 存根 )
  • 一个为服务器使用定义在Wiwider服务的方法去实现的接口类型(或者 存根 )

PHP-代码

1
protoc --proto_path=./ --php_out=./ --grpc_out=./ --plugin=protoc-gen-grpc=/Users/anthony/git/grpc/bins/opt/grpc_php_plugin ./something.proto

其中/Users/anthony/git/grpc/bins/opt/grpc_php_plugin对应从git中获取的<grpc-git-path>/bins/opt/grpc_php_plugin

运行这个命令后,会在当前目录生成如下:

  • GPBMetadata/something.php
  • Something/UserReply/sexes.php
  • Something/UserReply.php
  • Something/UserReply_sexes.php
  • Something/UserRequest.php
  • Something/UsersReply.php
  • Something/WiwiderClient.php

创建服务器

首先我们需要实现服务定义的服务接口:

something/something.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package something

import (
"Wiwide/cmi"
"context"
)
type UserServer struct {

}

func (s *UserServer) FindUser(ctx context.Context, in *UserRequest) (*UserReply, error) {
name := in.GetName()
rs, err := cmi.Db("local").Table("users").Where("name", name).First() // 从数据库查询
if err != nil || len(rs) == 0 {
return &UserReply{},err
}
rId := rs["id"].(int64)
rName := rs["name"].(string)
rAge := int32(rs["age"].(int64))
rSex := UserReplySexes(rs["sex"].(int64))
rep := &UserReply{Id:rId,Name:rName,Age:rAge,Sex:rSex}
return rep, nil
}

func (s *UserServer) FindUsers(ctx context.Context, in *UserRequest) (*UsersReply, error) {
name := in.GetName()
rs, err := cmi.Db("local").Table("users").Where("name","like","%"+name+"%").Get() // 从数据库查询
if err != nil {
return &UsersReply{}, err
}
list := make([]*UserReply, 0)
for _, v := range rs {
rId := v["id"].(int64)
rName := v["name"].(string)
rAge := int32(v["age"].(int64))
rSex := UserReplySexes(v["sex"].(int64))
list = append(list, &UserReply{Id:rId,Name:rName,Age:rAge,Sex:rSex})
}
rep := &UsersReply{Users: list}
return rep, nil
}

然后运行一个gRPC服务器,注册我们的服务并监听来自客户端的请求:

server.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package main

import (
"Wiwide/rpc/something"
"context"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
"log"
"net"
)

var (
port = ":50051"
)
func main() {
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
something.RegisterWiwiderServer(s, &something.UserServer{})
reflection.Register(s)
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}

运行服务器

简单执行命令:go run server.go,然后等待客户端请求。

创建客户端

Go-Client

创建文件client.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package main

import (
"Wiwide/rpc/something"
"context"
"google.golang.org/grpc"
"log"
"os"
"time"
)

const(
address = "localhost:50051"
defaultName = "World"
)

func main(){
conn, err := grpc.Dial(address, grpc.WithInsecure())
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()

name := defaultName
if len(os.Args) > 1 {
name = os.Args[1]
}
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
s := something.NewWiwiderClient(conn)
u, err := s.FindUser(ctx, &something.UserRequest{Name:name})
if err != nil {
log.Fatalf("could not Find: %v", err)
}
us, err := s.FindUsers(ctx, &something.UserRequest{Name:name})
if err != nil {
log.Fatalf("could not Find users: %v", err)
}
log.Printf("Find user: %v", u)
log.Printf("Find users: %v", us)
}

PHP-Client

PHP首先要安装grpc的php扩展,下载地址:http://pecl.php.net/package/gRPC

直接使用phpize安装:

1
2
3
4
5
6
7
8
tar -zxf grpc-1.17.0.tgz
cd grpc-1.17.0
phpize
./configure --with-php-config=/usr/local/php/bin/php-config
make
make install
# 在php.ini中增加
extension = grpc.so

执行php -m | grep grpc 应该会输出”grpc”,就代表成功了。

创建客户端文件,这里需要用到composer获取两个包,其中composer.json内容为:

1
2
3
4
5
6
7
8
{
"name": "grpc/grpc-demo",
"description": "gRPC example for PHP",
"require": {
"grpc/grpc": "^v1.3.0",
"google/protobuf": "^v3.3.0"
}
}

something.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
require dirname(__FILE__).'/vendor/autoload.php';

include_once dirname(__FILE__).'/Something/UserReply.php';
include_once dirname(__FILE__).'/Something/UsersReply.php';
include_once dirname(__FILE__).'/Something/UserRequest.php';
include_once dirname(__FILE__).'/Something/WiwiderClient.php';
include_once dirname(__FILE__).'/GPBMetadata/Something.php';

$client = new Something\WiwiderClient('127.0.0.1:50051', ['credentials' => Grpc\ChannelCredentials::createInsecure()]);
$req = new Something\UserRequest();
$name = !empty($argv[1]) ? $argv[1] : 'world';
$req->setName($name);
list($reply, $status) = $client->FindUser($req)->wait();
var_dump($reply->getId(), $reply->getName(), $reply->getAge(), $reply->getSex(), $status);

list($reply, $status) = $client->FindUsers($req)->wait();
$uers = $reply->getUsers();
$users = array();
foreach ($uers as $key => $value) {
array_push($users, array('id'=>$value->getId(),'name'=>$value->getName(),'age'=>$value->getAge(),'sex'=>$value->getSex()));
}
var_dump($users);

运行客户端

Go-Client

我们先编译一下:go build client.go,输出可执行文件client,然后直接运行:

  • ./client Anthony
  • ./client ny

分别输出如下:

1
2
2018/12/02 13:40:15 Find user: id:1 name:"Anthony" age:24 sex:Female 
2018/12/02 13:40:15 Find users: users:<id:1 name:"Anthony" age:24 sex:Female >
1
2
2018/12/02 13:40:25 Find user: 
2018/12/02 13:40:25 Find users: users:<id:1 name:"Anthony" age:24 sex:Female > users:<id:4 name:"funny" age:15 sex:2 >

PHP-Client

直接执行PHP文件:php something.php Anthonyphp something.php ny,结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
int(1)
string(7) "Anthony"
int(24)
int(1)
object(stdClass)#8 (3) {
["metadata"]=>
array(0) {
}
["code"]=>
int(0)
["details"]=>
string(0) ""
}
array(1) {
[0]=>
array(4) {
["id"]=>
int(1)
["name"]=>
string(7) "Anthony"
["age"]=>
int(24)
["sex"]=>
int(1)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
int(0)
string(0) ""
int(0)
int(0)
object(stdClass)#8 (3) {
["metadata"]=>
array(0) {
}
["code"]=>
int(0)
["details"]=>
string(0) ""
}
array(2) {
[0]=>
array(4) {
["id"]=>
int(1)
["name"]=>
string(7) "Anthony"
["age"]=>
int(24)
["sex"]=>
int(1)
}
[1]=>
array(4) {
["id"]=>
int(4)
["name"]=>
string(5) "funny"
["age"]=>
int(15)
["sex"]=>
int(2)
}
}

结束

本次笔记接近结尾了,内容很简单,主要记录使用grpc的大体流程。为后续的测试做一点点的准备。

参考资料

阅读剩下更多

默认配图
编程语言

Go语言包-path/filepath

一天一个Golang包,慢慢学习之“path/filepath”

上一篇学习了path包,了解了几个对路径处理的方法。今天继续完成它的子包“filepath”,包中的函数会根据不同平台做不同的处理,如路径分隔符、卷名等。

阅读文档:

官方pkg地址:https://golang.org/pkg/path/filepath

包方法

func Base(path string) string
func Clean(path string) string
func Dir(path string) string
func Ext(path string) string
func IsAbs(path string) bool
func Join(elem ...string) string
func Match(pattern, name string) (matched bool, err error)
func Split(path string) (dir, file string)
// 以上8种方法跟path包同名方法功能类似


func Abs(path string) (string, error)
// 返回相对当前路径的path的绝对路径

1
2
3
4
5
6
7
8
9
10
11
12
fmt.Println(filepath.Abs(""))
fmt.Println(filepath.Abs("name.txt"))
fmt.Println(filepath.Abs("/name.txt"))
fmt.Println(filepath.Abs("../name.txt"))
fmt.Println(filepath.Abs("~/../name.txt"))
/*
/Users/anthony/Workspaces/GoLand/src/application/learn/path/filepath <nil>
/Users/anthony/Workspaces/GoLand/src/application/learn/path/filepath/name.txt <nil>
/name.txt <nil>
/Users/anthony/Workspaces/GoLand/src/application/learn/path/name.txt <nil>
/Users/anthony/Workspaces/GoLand/src/application/learn/path/filepath/name.txt <nil>
*/

func EvalSymlinks(path string) (string, error)
// 返回Path的实际路径(如果path是个软链接的话)

1
2
fmt.Println(filepath.EvalSymlinks("/etc"))		// /private/etc <nil>
fmt.Println(filepath.EvalSymlinks("/usr/local"))// /usr/local <nil>

func ToSlash(path string) string
func FromSlash(path string) string
// 以上两种方法作主要用于Windows平台。
// 将path中的平台相关的路径分隔符(或’/‘)转换为’/‘(或平台相关的路径分隔符)


func Glob(pattern string) (matches []string, err error)
// 列出与指定模式pattern完全匹配的文件或目录,匹配原则同Match一样。

1
2
fmt.Println(filepath.Glob("/usr/*"))
// [/usr/bin /usr/include /usr/lib /usr/libexec /usr/local /usr/sbin /usr/share /usr/standalone] <nil>

func HasPrefix(p, prefix string) bool
// 该方法已弃用,不再被建议使用。


func Rel(basepath, targpath string) (string, error)
// 返回targpath相对于basepath的路径。
// 要求二者必须同为“相对路径”或“绝对路径”

1
2
3
4
fmt.Println(filepath.Rel("/usr/local","/usr/local/bin/go"))			// bin/go <nil>
fmt.Println(filepath.Rel("/usr/local","/usr/local/../local/bin/go"))// bin/go <nil>
fmt.Println(filepath.Rel("/usr/local","/usr/bin/go")) // ../bin/go <nil>
fmt.Println(filepath.Rel("/usr/local","/")) // ../.. <nil>

func SplitList(path string) []string
// 将路径序列path分割为多条独立路径。
// path类似Unix/Linux下的环境变量PATH。

1
2
fmt.Printf("%q\n", filepath.SplitList("/bin:/sbin:/usr/bin:/usr/sbin  : /usr/local/bin"))
// ["/bin" "/sbin" "/usr/bin" "/usr/sbin " " /usr/local/bin"]

func VolumeName(path string) string
// 返回路径字符中的卷名
// Windows 中的 C:\Windows 会返回”C:”
// Linux 中的 //dev/host/dir 会返回 //dev/host


func Walk(root string, walkFn WalkFunc) error
// 遍历指定目录root(包括子目录),对遍历到的项目用walkFn函数进行处理。
// walkFn返回nil,则walkFn继续遍历,如果返回SkipDir,则Walk函数跳过当前目录,继续遍历下一目录。
// 如果返回其他错误,则Walk函数终止。
// 在Walk遍历过程中,如遇到错误,则会将错误通过err传递给walkFn,同时跳过出错的项目,继续处理后续项目。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
// 打印目录及目录下所有文件及大小
fn := func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
fmt.Printf("%q, %q, %d\n", path, info.Name(), info.Size())
return nil
}

filepath.Walk("/Users/anthony/Downloads/conf", fn)
/*
"/Users/anthony/Downloads/conf", "conf", 224
"/Users/anthony/Downloads/conf/0x4f5da2.cn.key", "0x4f5da2.cn.key", 1700
"/Users/anthony/Downloads/conf/0x4f5da2.cn_bundle.crt", "0x4f5da2.cn_bundle.crt", 3323
"/Users/anthony/Downloads/conf/world.cert.key.pem", "world.cert.key.pem", 1675
"/Users/anthony/Downloads/conf/world.dev.bundle.crt", "world.dev.bundle.crt", 2802
"/Users/anthony/Downloads/conf/world.local.bundle.crt", "world.local.bundle.crt", 2863
*/

// WalkFunc 函数:
// 列出含有 *.txt 文件的目录(不是全部,因为会跳过一些子目录)
findTxtDir := func(path string, info os.FileInfo, err error) error {
ok, err := filepath.Match(`*.txt`, info.Name())
if ok {
fmt.Println(filepath.Dir(path), info.Name())
// 遇到 txt 文件则继续处理所在目录的下一个目录
// 注意会跳过子目录
return filepath.SkipDir
}
return err
}
// 列出含有 *.txt 文件的目录(不是全部,因为会跳过一些子目录)
err := filepath.Walk(`/usr`, findTxtDir)
fmt.Println(err)
/*
/usr/local/Cellar/chromaprint/1.4.3 NEWS.txt
/usr/local/Cellar/fontconfig/2.13.1/share/doc/fontconfig fontconfig-devel.txt
/usr/local/Cellar/frei0r/1.6.1 AUTHORS.txt
/usr/local/Cellar/game-music-emu/0.6.2 changes.txt
...
<nil>
*/

// WalkFunc 函数:
// 列出所有以 ab 开头的目录(全部,因为没有跳过任何项目)
findabDir := func(path string, info os.FileInfo, err error) error {
if info.IsDir() {
ok, err := filepath.Match(`[aA][bB]*`, info.Name())
if err != nil {
return err
}
if ok {
fmt.Println(path)
}
}
return nil
}
// 列出所有以 ab 开头的目录(全部,因为没有跳过任何项目)
err = filepath.Walk(`/usr`, findabDir)
fmt.Println(err)
/*
/usr/local/Cellar/node/11.0.0/libexec/lib/node_modules/npm/node_modules/abbrev
/usr/local/Homebrew/Library/Taps/homebrew/homebrew-services/.git/objects/ab
/usr/local/lib/node_modules/gulp/node_modules/resolve/test/dotdot/abc
/usr/local/lib/node_modules/hexo-cli/node_modules/abbrev
...
<nil>
*/


阅读剩下更多

默认配图
编程语言

Go语言包-path

一天一个Golang包,慢慢学习之“path”

上一篇学习了bytes包,内容还是有点多,花的时间也多了一点。所以今天补充点小内容-path包。当然,path还有子包:filepath,这个下次再继续。

阅读文档:

官方pkg地址:https://golang.org/pkg/path/

包方法

func Base(path string) string
// 返回最后一个元素(目录或文件)的路径

1
2
3
4
5
6
7
8
fmt.Println(path.Base("/a/b/"))	// b
fmt.Println(path.Base("/a/b")) // b
fmt.Println(path.Base("./a/b")) // b
fmt.Println(path.Base("../a/b")) // b
fmt.Println(path.Base("/")) // /
fmt.Println(path.Base("./")) // .
fmt.Println(path.Base(".")) // .
fmt.Println(path.Base("")) // .

func Clean(path string) string
// 返回最洁净的路径,在path比较复杂的情况下使用,可以简化path。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
paths := []string{
"a/c",
"a//c",
"a/c/.",
"a/c/b/..",
"/../a/c",
"/../a/b/../././/c",
"",
}
for _, p := range paths {
fmt.Printf("Clean(%q) = %q\n", p, path.Clean(p))
}
// Clean("a/c") = "a/c"
// Clean("a//c") = "a/c"
// Clean("a/c/.") = "a/c"
// Clean("a/c/b/..") = "a/c"
// Clean("/../a/c") = "/a/c"
// Clean("/../a/b/../././/c") = "/a/c"
// Clean("") = "."

func Dir(path string) string
// 返回元素(目录或文件)的目录路径

1
2
3
4
5
6
fmt.Println(path.Dir("/a/b/c"))	// /a/b
fmt.Println(path.Dir("a/b/c")) // a/b
fmt.Println(path.Dir("/a/")) // /a
fmt.Println(path.Dir("a/")) // a
fmt.Println(path.Dir("/")) // /
fmt.Println(path.Dir("")) // .

func Ext(path string) string
// 返回path下文件名的后缀

1
2
3
4
fmt.Println(path.Ext("/a/b/c/bar.css"))	// .css
fmt.Println(path.Ext("/a/b/c/bar.tar.gz")) // .gz
fmt.Println(path.Ext("/")) //
fmt.Println(path.Ext("")) //

func IsAbs(path string) bool
// 判断path是否是绝对路径

1
2
3
fmt.Println(path.IsAbs("/dev/null"))	// true
fmt.Println(path.IsAbs("dev/null")) // false
fmt.Println(path.IsAbs("./dev/null")) // false

func Join(elem ...string) string
// 将多个路径元素连接成一个,空元素会被忽略。

1
2
3
4
5
6
fmt.Println(path.Join("a", "b", "c"))	// a/b/c
fmt.Println(path.Join("a", "b/c")) // a/b/c
fmt.Println(path.Join("a/b", "c")) // a/b/c
fmt.Println(path.Join("", "")) //
fmt.Println(path.Join("a", "")) // a
fmt.Println(path.Join("", "a")) // a

func Match(pattern, name string) (matched bool, err error)
// 判断name是否符合pattern规则,并返回err

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// pattern:
// { term }
// term:
// '*' matches any sequence of non-/ characters
// '?' matches any single non-/ character
// '[' [ '^' ] { character-range } ']'
// character class (must be non-empty)
// c matches character c (c != '*', '?', '\\', '[')
// '\\' c matches character c

// character-range:
// c matches character c (c != '\\', '-', ']')
// '\\' c matches character c
// lo '-' hi matches character c for lo <= c <= hi

fmt.Println(path.Match("abc", "abc")) // true <nil>
fmt.Println(path.Match("a*", "abc")) // true <nil>
fmt.Println(path.Match("a*d", "abcd")) // true <nil>
fmt.Println(path.Match("a*/b", "a/c/b")) // false <nil>
fmt.Println(path.Match("a/[a-f]/b", "a/c/b")) // true <nil>

func Split(path string) (dir, file string)
// 返回path的目录和文件名

1
2
3
fmt.Println(path.Split("static/myfile.css"))	// static/ myfile.css
fmt.Println(path.Split("myfile.css")) // myfile.css
fmt.Println(path.Split("")) //

子包:filepath

下一篇来学习:path/filepath

阅读剩下更多

默认配图
编程语言

Go语言包-bytes

一天一个Golang包,慢慢学习之“bytes”

今天学习bytes包,其实之前就已经使用了bytes包的很多方法了,这次主要就是再次熟悉和认识这个包里面的方法。

阅读文档:

官方pkg地址:https://golang.org/pkg/bytes/

包方法

func Compare(a, b []byte) int
// 比较a和b两个字节数组。
// if a==b return 0;
// if a < b return -1;
// if a > b return 1;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var a, b []byte
a = []byte{65,66,67}
b = []byte{65,66,67}
fmt.Printf("%s, %s, %d\n", a, b, bytes.Compare(a, b))

a = []byte{65,66,67}
b = []byte{65,66,68}
fmt.Printf("%s, %s, %d\n", a, b, bytes.Compare(a, b))

a = []byte{65,66,67}
b = []byte{65,66,66}
fmt.Printf("%s, %s, %d\n", a, b, bytes.Compare(a, b))

a = []byte{65,66,67}
b = []byte{65,66,67,68}
fmt.Printf("%s, %s, %d\n", a, b, bytes.Compare(a, b))

a = []byte{65,66,67,68}
b = []byte{65,66,67}
fmt.Printf("%s, %s, %d\n", a, b, bytes.Compare(a, b))
// ABC, ABC, 0
// ABC, ABD, -1
// ABC, ABB, 1
// ABC, ABCD, -1
// ABCD, ABC, 1

func Contains(b, subslice []byte) bool
// 检查subslice是不是包含在b中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
a := []byte{65, 66, 67, 68, 69, 70}
b := []byte{65, 66, 67, 68, 69, 70}
fmt.Printf("%s, %s, %v\n", a, b, bytes.Contains(a, b))

b = []byte{65, 66, 67, 68, 69}
fmt.Printf("%s, %s, %v\n", a, b, bytes.Contains(a, b))

b = []byte{65, 66, 67, 68, 69, 70, 71}
fmt.Printf("%s, %s, %v\n", a, b, bytes.Contains(a, b))

b = []byte{66, 67, 68, 69, 70, 71}
fmt.Printf("%s, %s, %v\n", a, b, bytes.Contains(a, b))
// ABCDEF, ABCDEF, true
// ABCDEF, ABCDE, true
// ABCDEF, ABCDEFG, false
// ABCDEF, BCDEFG, false

func ContainsAny(b []byte, chars string) bool
// 检查b中是否包含chars中的任意字符

1
2
3
4
5
6
a := []byte{65, 66, 67, 68}
fmt.Printf("%v\n", bytes.ContainsAny(a, "abcd") // false
fmt.Printf("%v\n", bytes.ContainsAny(a, "a")) // false
fmt.Printf("%v\n", bytes.ContainsAny(a, "aB")) // true
fmt.Printf("%v\n", bytes.ContainsAny(a, "aBcd")) // true
fmt.Printf("%v\n", bytes.ContainsAny(a, "abcd ABC")) // true

func ContainsRune(b []byte, r rune) bool
// 判断b中是否包含r字符

1
2
3
a := []byte{65, 66, 67 ,68}
fmt.Printf("%v\n", bytes.ContainsRune(a, 'b')) // false
fmt.Printf("%v\n", bytes.ContainsRune(a, 'B')) //true

func Count(s, sep []byte) int
// sep在s中的重复次数(sep不重叠),即s=ababab,sep=abab则返回1

1
2
3
4
5
a := []byte("ababab")
sep := []byte("ab")
fmt.Printf("%d\n", bytes.Count(a, sep)) // 3
sep = []byte("abab")
fmt.Printf("%d\n", bytes.Count(a, sep)) // 1

func Equal(a, b []byte) bool
// 判断a和b是否相等,nil等同于[]byte()

1
2
3
4
5
a := []byte("ABC")
b := []byte{65, 66, 67}
fmt.Printf("%s, %s, %v\n", a, b, bytes.Equal(a, b)) // ABC, ABC, true
b = []byte{65 ,66}
fmt.Printf("%s, %s, %v\n", a, b, bytes.Equal(a, b)) // ABC, AB, false

func EqualFold(s, t []byte) bool
// 判断s和t是否相似,忽略大小写和标题三种格式

1
2
3
4
5
a := []byte("abc")
b := []byte("ABC")
fmt.Printf("%s, %s, %v\n", a, b, bytes.EqualFold(a, b)) // abc, ABC, true
b = []byte("ABCC")
fmt.Printf("%s, %s, %v\n", a, b, bytes.EqualFold(a, b)) // abc, ABCC, false

func Fields(s []byte) [][]byte
// 以连续空白为分隔符切割s成多个子串(不含分隔符)

1
2
3
a := []byte("abc def  ABC  CBA")
fmt.Printf("%s, %v\n", a, bytes.Fields(a))
// abc def ABC CBA, [[97 98 99] [100 101 102] [65 66 67] [67 66 65]]

func FieldsFunc(s []byte, f func(rune) bool) [][]byte
// 以符合f方法的字符为分隔符切割s成多个子串(不含分隔符)

1
2
3
f := func(r rune) bool { return r == '#'}
a := []byte("This is # and @")
fmt.Printf("%s, %q\n", a, bytes.FieldsFunc(a, f)) // This is # and @, ["This is " " and @"]

func HasPrefix(s, prefix []byte) bool
// 检测s字节切片是否以prefix开头

1
2
3
4
5
6
7
s := []byte("Hello World!")
prefix := []byte("hello")
fmt.Printf("%s, %s, %v\n", s, prefix, bytes.HasPrefix(s, prefix)) // Hello World!, hello, false
prefix = []byte("Hello")
fmt.Printf("%s, %s, %v\n", s, prefix, bytes.HasPrefix(s, prefix)) // Hello World!, Hello, true
prefix = []byte("")
fmt.Printf("%s, %s, %v\n", s, prefix, bytes.HasPrefix(s, prefix)) // Hello World!, , true

func HasSuffix(s, suffix []byte) bool
// 检测s字节切片是否以suffix结尾

1
2
3
4
5
6
7
s := []byte("Hello World!")
suffix := []byte("world!")
fmt.Printf("%s, %s, %v\n", s, suffix, bytes.HasSuffix(s, suffix)) // Hello World!, world!, false
suffix = []byte("Hello World!")
fmt.Printf("%s, %s, %v\n", s, suffix, bytes.HasSuffix(s, suffix)) // Hello World!, World!, true
suffix = []byte("")
fmt.Printf("%s, %s, %v\n", s, suffix, bytes.HasSuffix(s, suffix)) // Hello World!, , true

func Index(s, sep []byte) int
// 返回sep在s中的起始位置,如果sep不在s中则返回-1

1
2
3
4
5
6
7
8
9
s := []byte("Hello World!")
sep := []byte("Hello")
fmt.Printf("'%s' of '%s' is %d\n", sep, s, bytes.Index(s, sep)) // 'Hello' of 'Hello World!' is 0
sep = []byte("World")
fmt.Printf("'%s' of '%s' is %d\n", sep, s, bytes.Index(s, sep)) // 'World' of 'Hello World!' is 6
sep = []byte("Bob")
fmt.Printf("'%s' of '%s' is %d\n", sep, s, bytes.Index(s, sep)) // 'Bob' of 'Hello World!' is -1
sep = []byte("")
fmt.Printf("'%s' of '%s' is %d\n", sep, s, bytes.Index(s, sep)) // '' of 'Hello World!' is 0

func IndexAny(s []byte, chars string) int
// 返回chars中任意字符在s中出现的第一个位置,chars为空或找不到则返回-1.

1
2
3
4
5
6
7
8
9
10
11
s := []byte("Hello World!")
chars := "abcdefg"
fmt.Printf("%s, %s, %d\n", s, chars, bytes.IndexAny(s, chars)) // Hello World!, abcdefg, 1
chars = "aabbcc"
fmt.Printf("%s, %s, %d\n", s, chars, bytes.IndexAny(s, chars)) // Hello World!, aabbcc, -1
chars = "abcd"
fmt.Printf("%s, %s, %d\n", s, chars, bytes.IndexAny(s, chars)) // Hello World!, abcd, 10
chars = ""
fmt.Printf("%s, %s, %d\n", s, chars, bytes.IndexAny(s, chars)) // Hello World!, , -1
chars = " "
fmt.Printf("%s, %s, %d\n", s, chars, bytes.IndexAny(s, chars)) // Hello World!, , 5

func IndexByte(b []byte, c byte) int
// 同Index,sep变为c字节


func IndexFunc(s []byte, f func(r rune) bool) int
// 返回符合f方法的字符首次出现的位置,找不到则返回-1


func IndexRune(s []byte, r rune) int
// 同Index,sep变为r字符


func Join(s [][]byte, sep []byte) []byte
// 以sep为连接符将子串列表连接成一个字节串

1
2
3
s := [][]byte{{65,66},{67,68}}
sep := []byte{124,124}
fmt.Printf("%q, %s, %s\n", s, sep, bytes.Join(s, sep)) // ["AB" "CD"], ||, AB||CD

func LastIndex(s, sep []byte) int
// 查找s中最后一次出现sep的位置,找不到返回-1

1
2
3
4
5
6
7
s := []byte("abcddabcd")
sep := []byte("abc")
fmt.Printf("%s, %s, %d\n", s, sep, bytes.LastIndex(s, sep)) // abcddabcd, abc, 5
sep = []byte("bcd")
fmt.Printf("%s, %s, %d\n", s, sep, bytes.LastIndex(s, sep)) // abcddabcd, bcd, 6
sep = []byte("")
fmt.Printf("%s, %s, %d\n", s, sep, bytes.LastIndex(s, sep)) // abcddabcd, , 9

func LastIndexAny(s []byte, chars string) int
// 查找s中最后一次出现chars中任意字符的位置,找不到返回-1


func LastIndexByte(s []byte, c byte) int
// 查找s中最后一次出现c字节的位置,找不到返回-1


func LastIndexFunc(s []byte, f func(r rune) bool) int
// 查找s中符合f方法的字符位置,找不到返回-1


func Map(mapping func(r rune) rune, s []byte) []byte
// 将s中的r替换成mapping(r)返回的字符,如果mapping返回负值,则丢弃

1
2
3
4
5
6
7
8
9
10
11
rot13 := func(r rune) rune {
switch {
case r >= 'A' && r <= 'Z':
return 'A' + (r-'A'+13)%26
case r >= 'a' && r <= 'z':
return 'a' + (r-'a'+13)%26
}
return r
}
fmt.Printf("%s\n", bytes.Map(rot13, []byte("'Twas brillig and the slithy gopher...")))
// 'Gjnf oevyyvt naq gur fyvgul tbcure...

func Repeat(b []byte, count int) []byte
// 返回子串b重复count次后的串

1
fmt.Printf("%s\n", bytes.Repeat([]byte("Abc"), 3))	// AbcAbcAbc

func Replace(s, old, new []byte, n int) []byte
// 将s中的前n个old替换成new,如果n<0则替换全部

1
2
3
4
s := []byte("Bob takes you to Bob's house.")
fmt.Printf("%s\n", bytes.Replace(s, []byte("Bob"), []byte("Lily"), 0)) // Bob takes you to Bob's house.
fmt.Printf("%s\n", bytes.Replace(s, []byte("Bob"), []byte("Lily"), 1)) // Lily takes you to Bob's house.
fmt.Printf("%s\n", bytes.Replace(s, []byte("Bob"), []byte("Lily"), -1)) // Lily takes you to Lily's house.

func Runes(s []byte) []rune
// 将s转换成[]rune型


func Split(s, sep []byte) [][]byte
// 以sep作为分隔符将s拆分成多个子串。
// 如果sep为空,则将s拆分成Unicode字符列表


func SplitN(s, sep []byte, n int) [][]byte
// 同Split,但可指定拆分次数n,超出n的部分不进行拆分


func SplitAfter(s, sep []byte) [][]byte
// 同Split,但是每个子串都包含分隔符sep


func SplitAfterN(s, sep []byte, n int) [][]byte
// 同SplitAfter,但可指定拆分次数n。


func Title(s []byte) []byte
// 将s中所有单词的首字符修改为Title格式返回
// Bug: 不能很好的处理Unicode标点符号分隔的单词。


func ToLower(s []byte) []byte
// 将s中的字符全部替换成小写并返回


func ToUpper(s []byte) []byte
// 将s中的字符全部替换成大小并返回


func ToTitle(s []byte) []byte
// 将s中的字符全部替换成标题并返回


func ToLowerSpecial(c unicode.SpecialCase, s []byte) []byte
// 使用指定的映射表将 s 中的所有字符修改为小写格式返回。


func ToUpperSpecial(c unicode.SpecialCase, s []byte) []byte
// 使用指定的映射表将 s 中的所有字符修改为大写格式返回。


func ToTitleSpecial(c unicode.SpecialCase, s []byte) []byte
// 使用指定的映射表将 s 中的所有字符修改为标题格式返回。


func Trim(s []byte, cutset string) []byte
func TrimLeft(s []byte, cutset string) []byte
func TrimRight(s []byte, cutset string) []byte
// 以上方法返回去掉s两边(左边、右边)包含在cutset中字符的切片


func TrimFunc(s []byte, f func(r rune) bool) []byte
func TrimLeftFunc(s []byte, f func(r rune) bool) []byte
func TrimRightFunc(s []byte, f func(r rune) bool) []byte
// 以上方法返回去掉s两边(左边、右边)符合f方法字符的切片


func TrimPrefix(s, prefix []byte) []byte
// 去掉s的前缀prefix


func TrimSuffix(s, suffix []byte) []byte
// 去掉s的后缀suffix


func TrimSpace(s []byte) []byte
// 去掉两边的空白


func NewBuffer(buf []byte) *Buffer
// 将buf包装成bytes.Buffer对象


func NewBufferString(s string) *Buffer
// 将s转换成[]byte后,包装成bytes.Buffer对象


func NewReader(b []byte) *Reader
// 将b包装成bytes.Reader对象


Buffer结构体

1
2
3
4
5
6
7
8
9
10
11
// A Buffer is a variable-sized buffer of bytes with Read and Write methods.
// The zero value for Buffer is an empty buffer ready to use.
type Buffer struct {
buf []byte // contents are the bytes buf[off : len(buf)]
off int // read at &buf[off], write at &buf[len(buf)]
bootstrap [64]byte // memory to hold first slice; helps small buffers avoid allocation.
lastRead readOp // last read operation, so that Unread* can work correctly.

// FIXME: it would be advisable to align Buffer to cachelines to avoid false
// sharing.
}

Buffer的方法:

func (b *Buffer) Bytes() []byte
// 引用未读取部分的数据切片(不移动读取位置)


func (b *Buffer) Cap() int
// 返回缓冲区容量


func (b *Buffer) Grow(n int)
// 自动增加缓存容量,以保证有n字节的剩余空间
// 如果n<0或者无法增加容量则会报Panic


func (b *Buffer) Len() int
// 返回未读缓冲区的字节长度,b.Len()==len(b.Bytes())


func (b *Buffer) Next(n int) []byte
// 返回缓冲区中的下n个字节数据的切片,并推进读取位置(类似于返回的字节数据已经被读取)。
// 如果n大于缓冲区长度,则返回整个缓冲区数据。
// 返回的切片仅在下一次调用Read或Write方法前有效。


func (b *Buffer) Read(p []byte) (n int, err error)
// 从缓冲区读取len(p)个字节或整个缓冲区到p中,返回读取的字节数n,如果没有数据可返回,则err=io.EOF。


func (b *Buffer) ReadByte() (byte, error)
// 从缓冲区读取一个字节并返回,如果没有可返回的数据,err=io.EOF。


func (b *Buffer) ReadBytes(delim byte) (line []byte, err error)
// 从缓存中读取数据直到遇到delim时停止并返回包含delim的切片。
// 如果期间遇到错误,则返回遇到错误之前读取的数据并返回err。


func (b *Buffer) ReadFrom(r io.Reader) (n int64, err error)
// 从r中读取数据直到遇到EOF并且写入缓存区,必要时会增大缓存。
// 返回读取到的字节数量n,遇到除了EOF的其他错误也会返回结果。
// 如果缓存变的太大将会报一个ErrTooLarge的Panic错误。


func (b *Buffer) ReadRune() (r rune, size int, err error)
// 从缓存中读取并返回下一个UTF8编码字符,没有合适的字节则返回io.EOF。


func (b *Buffer) ReadString(delim byte) (line string, err error)
// 从缓存中读取直到第一次遇到delim字节,并作为string返回这个包含delim的数据。


func (b *Buffer) Reset()
// 将b重置并清空数据,如同Truncate(0)。


func (b *Buffer) String() string
// 将缓冲区所有未读数据作为string返回,如果b是nil,则返回”“。


func (b *Buffer) Truncate(n int)
// 从缓冲区中丢弃第n个未读数据之后数据,如果n<0或n>Cap则报Panic。


func (b *Buffer) UnreadByte() error
// 撤销最近一次的读操作中的最后字节数据。
// 如果在这之前执行了write或者最后一次读取返回了错误或者最后读取了空字节,则该方法会返回一个错误。


func (b *Buffer) UnreadRune() error
// 类似UnreadByte(),但是严格要求最后一次读取是Rune,否则报错。


func (b *Buffer) Write(p []byte) (n int, err error)
// 将p中的数据写入缓存,并返回写入的字节数n和可能发生的错误。


func (b *Buffer) WriteByte(c byte) error
// 往缓存中写入一个字节c并返回可能发生的错误(通常是nil)。
// 如果缓存扩容太大,则会报一个ErrTooLarge的Panic。


func (b *Buffer) WriteRune(r rune) (n int, err error)
// 同WriteByte,返回写入长度和可能发生的错误(通常是nil)。


func (b *Buffer) WriteString(s string) (n int, err error)
// 同WriteByte,将s的内容写入缓存,返回写入长度和可能发生的错误。


func (b *Buffer) WriteTo(w io.Writer) (n int64, err error)
// 将缓存数据写入w直到写入完毕或发生错误。


Buffer的示列:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
package bytes

import (
"bytes"
"fmt"
)

func Buffer(){
bt := make([]byte, 0)
buffer := bytes.NewBuffer(bt)
buffer.Write([]byte("Hello"))
fmt.Printf("%s, %d, %d\n", buffer, buffer.Cap(), buffer.Len()) // Hello, 5, 5

buffer.WriteString(" World")
fmt.Printf("%s, %d, %d\n", buffer, buffer.Cap(), buffer.Len()) // Hello World, 16, 11

buffer.WriteByte('!')
fmt.Printf("%s, %d, %d\n", buffer, buffer.Cap(), buffer.Len()) // Hello World!, 16, 12

buffer.WriteRune('$')
fmt.Printf("%s, %d, %d\n", buffer, buffer.Cap(), buffer.Len()) // Hello World!$, 16, 13

r, size, _ := buffer.ReadRune()
fmt.Printf("%q, %d\n", r, size) // 'H', 1
fmt.Printf("%s, %d, %d\n", buffer, buffer.Cap(), buffer.Len()) // ello World!$, 16, 12

buffer.UnreadRune()
fmt.Printf("%s, %d, %d\n", buffer, buffer.Cap(), buffer.Len()) // Hello World!$, 16, 13

b , _ :=buffer.ReadByte()
fmt.Printf("%q\n", b) // 'H'
fmt.Printf("%s, %d, %d\n", buffer, buffer.Cap(), buffer.Len()) // ello World!$, 16, 12

buffer.UnreadByte()
fmt.Printf("%s, %d, %d\n", buffer, buffer.Cap(), buffer.Len()) // Hello World!$, 16, 13

line, _ := buffer.ReadBytes('l')
fmt.Printf("%q\n", line) // "Hel"
fmt.Printf("%s, %d, %d\n", buffer, buffer.Cap(), buffer.Len()) // lo World!$, 16, 10

buffer.UnreadByte()
fmt.Printf("%s, %d, %d\n", buffer, buffer.Cap(), buffer.Len()) // llo World!$, 16, 11

str, _ := buffer.ReadString('o')
fmt.Printf("%q\n", str) // "llo"
fmt.Printf("%s, %d, %d\n", buffer, buffer.Cap(), buffer.Len()) // World!$, 16, 8

buffer.UnreadByte()
fmt.Printf("%s, %d, %d\n", buffer, buffer.Cap(), buffer.Len()) // o World!$, 16, 9

next := buffer.Next(2)
fmt.Printf("%q\n", next) // "o "
fmt.Printf("%s, %d, %d\n", buffer, buffer.Cap(), buffer.Len()) // World!$, 16, 7

btRead := make([]byte, 2)
n, _ := buffer.Read(btRead)
fmt.Printf("%d\n", n) // 2
fmt.Printf("%s, %d, %d\n", buffer, buffer.Cap(), buffer.Len()) // rld!$, 16, 5

buffer.Grow(3)
fmt.Printf("%s, %d, %d\n", buffer, buffer.Cap(), buffer.Len()) // rld!$, 16, 5
buffer.Grow(4)
fmt.Printf("%s, %d, %d\n", buffer, buffer.Cap(), buffer.Len()) // rld!$, 36, 5

buffer.Truncate(4)
fmt.Printf("%s, %d, %d\n", buffer, buffer.Cap(), buffer.Len()) // rld!, 36, 4

buffer.Reset()
fmt.Printf("%s, %d, %d\n", buffer, buffer.Cap(), buffer.Len()) // , 36, 0

bt1 := []byte("I see you.")
bf1 := bytes.NewReader(bt1)
n64, _ :=buffer.ReadFrom(bf1)
fmt.Printf("%d\n", n64) // 10
fmt.Printf("%s, %d, %d\n", buffer, buffer.Cap(), buffer.Len()) // I see you., 584, 10

bt2 := make([]byte, 0)
bf2 := bytes.NewBuffer(bt2)
n64, _ = buffer.WriteTo(bf2)
fmt.Printf("%d, %q, %q\n", n64, bf2, bt2) // 10, "I see you.", ""
fmt.Printf("%s, %d, %d\n", buffer, buffer.Cap(), buffer.Len()) // , 584, 0
}

Reader结构体

1
2
3
4
5
6
7
8
9
10
11
// bytes.Reader 实现了如下接口:
// io.ReadSeeker
// io.ReaderAt
// io.WriterTo
// io.ByteScanner
// io.RuneScanner
type Reader struct {
s []byte
i int64 // current reading index
prevRune int // index of previous rune; or < 0
}

Reader的方法:

func (r *Reader) Len() int
// 返回未读部分的数据长度。


func (r *Reader) Size() int64
// 返回底层数据的总长度。


func (r *Reader) Reset(b []byte)
// 将底层数据切换为b,同时复位所有标记。


func (r *Reader) Read(b []byte) (n int, err error)
func (r *Reader) ReadAt(b []byte, off int64) (n int, err error)
func (r *Reader) ReadByte() (byte, error)
func (r *Reader) ReadRune() (ch rune, size int, err error)
func (r *Reader) Seek(offset int64, whence int) (int64, error)
func (r *Reader) UnreadByte() error
func (r *Reader) UnreadRune() error
func (r *Reader) WriteTo(w io.Writer) (n int64, err error)

Reader的示列:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package bytes

import (
"bytes"
"fmt"
)

func Reader(){
b1 := []byte("你好,世界!")
b2 := []byte("Hello World!")
buf := make([]byte, 9)
rd := bytes.NewReader(b1)
n, _ := rd.Read(buf)
fmt.Printf("%q, %d, %d, %d\n", buf, rd.Len(), rd.Size(), n) // "你好,", 9, 18, 9
n, _ = rd.Read(buf)
fmt.Printf("%q, %d, %d, %d\n", buf, rd.Len(), rd.Size(), n) // "世界!", 0, 18, 9

rd.Reset(b2)
buf = make([]byte, 6)
n, _ = rd.Read(buf)
fmt.Printf("%q, %d, %d, %d\n", buf, rd.Len(), rd.Size(), n) // "Hello ", 6, 12, 6
n, _ = rd.Read(buf)
fmt.Printf("%q, %d, %d, %d\n", buf, rd.Len(), rd.Size(), n) // "World!", 0, 12, 6

rd.UnreadByte()
n, _ = rd.Read(buf)
fmt.Printf("%q, %d, %d, %d\n", buf, rd.Len(), rd.Size(), n) // "!orld!", 0, 12, 1
// UnreadByte使得最后一个字符'!'回到了rd中,在rd.Read写入buf时,则将'!'写入了buf[0]中。
}

阅读剩下更多

默认配图
编程语言

Go语言包-bufio

一天一个Golang包,慢慢学习之“bufio”

今天学习带缓存的I/O操作,基础包“bufio”中有bufio.go和scan.go两个文件

阅读文档:

官方pkg地址:https://golang.org/pkg/bufio/

包方法

func NewReadWriter(r *Reader, w *Writer) *ReadWriter
// 将r和w封装为一个bufio.Readwriter对象

func NewReader(rd io.Reader) *Reader
// 将rd封装为一个bufio.Reader对象(缓存大小默认4096)

func NewReaderSize(rd io.Reader, size int) *Reader
// 将rd封装成一个拥有size大小缓存的bufio.Reader对象

func NewScanner(r io.Reader) *Scanner
// 创建一个Scanner来扫描r

func NewWriter(w io.Writer) *Writer
// 将w封装成一个bufio.Writer对象(缓存大小默认4096)

func NewWriterSize(w io.Writer, size int) *Writer
//将w封装成一个拥有size大小缓存的bufio.Writer对象

Reader结构体

1
2
3
4
5
6
7
8
9
// Reader 实现了对io.Reader对象的缓冲功能
type Reader struct {
buf []byte
rd io.Reader // reader provided by the client
r, w int // buf read and write positions
err error
lastByte int
lastRuneSize int
}

func (b *Reader) Buffered() int
// 返回缓存中的数据长度

func (b *Reader) Discard(n int) (discarded int, err error)
// 丢弃接下来的n个字节,返回丢弃的字节长度

func (b *Reader) Peek(n int) ([]byte, error)
// 返回前n字节的缓存切片

func (b *Reader) Read(p []byte) (n int, err error)
// 读取len(p)个字节到p中。
// 如果len(p)>缓存大小,且缓存不为空,则读取全部缓存。
// 若缓存为空,则从底层io.Reader直接读取

func (b *Reader) ReadByte() (byte, error)
// 读出一个字节并返回

func (b *Reader) ReadBytes(delim byte) ([]byte, error)
// 查找delim并读取delim及其之前的所有数据(byte切片)
// 如果在找到delim之前发生错误,则返回发生错误之前的数据和error。
// 当找不到delim的时候,err!=nil

func (b *Reader) ReadLine() (line []byte, isPrefix bool, err error)
// 返回一个单行数据(切片),不包括行尾标记。
// 如果在缓存中找不到行尾标记,isPrefix为true,表示查找未完成,否则isPrefix为false

func (b *Reader) ReadRune() (r rune, size int, err error)
// 读取一个UTF8字符和字符的编码长度。
// 如果UTF8序列无法解码出一个正确的Unicode字符,则只读出一个字节,并返回U+FFFD字符,size返回1。

func (b *Reader) ReadSlice(delim byte) (line []byte, err error)
// 查找delim并读取delim及其之前的所有数据(byte切片-引用)

func (b *Reader) ReadString(delim byte) (string, error)
// 查找delim并读取delim及其之前的所有数据(字符串)

func (b *Reader) Reset(r io.Reader)
// 丢弃任何缓存数据,重置所有状态并切换io.Reader到r

func (r *Reader) Size() int
// 返回(底层)缓存的字节长度

func (b *Reader) UnreadByte() error
// 撤销最后一次读出的一个字节

func (b *Reader) UnreadRune() error
// 撤销最后一次读出的Unicode字符
// 如果最后一次执行的不是ReadRune(),则返回一个错误

func (b *Reader) WriteTo(w io.Writer) (n int64, err error)
// 实现了io.WriteTo接口。
// 可以(可能)对底层Reader的Read方法进行多次调用。
// 如果底层Reader支持WriteTo方法,则直接调用底层方法,无需缓存。

Reader的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
package bufio

import (
"bufio"
"bytes"
"fmt"
)

func Reader(){
testB := bytes.NewReader([]byte("abcdefg\nABCDEFG\nh i j k\nH I J K\n\nline."))
bufRd := bufio.NewReader(testB)
fmt.Println(bufRd.Buffered(), bufRd.Size())
// 0 4096

b, _ := bufRd.ReadByte()
fmt.Println(bufRd.Buffered(), b, string(b))
// 37 97 a

bs, _ := bufRd.ReadBytes('d')
fmt.Println(bufRd.Buffered(), bs, string(bs))
// 34 [98 99 100] bcd

discard, _ := bufRd.Discard(1)
fmt.Println("discard:", discard, "bufferd:", bufRd.Buffered())
// discard: 1 bufferd: 33

b, _ = bufRd.ReadByte()
fmt.Println(bufRd.Buffered(), b, string(b))
// 32 102 f

bs, _ = bufRd.Peek(7)
fmt.Println(bufRd.Buffered(), bs, string(bs))
// 32 [103 10 65 66 67 68 69] g
// ABCDE

line, isPrefix, _ := bufRd.ReadLine()
fmt.Println(bufRd.Buffered(), line, string(line), isPrefix)
// 30 [103] g false

r, size, _ := bufRd.ReadRune()
fmt.Println(bufRd.Buffered(), r, string(r), size)
// 29 65 A 1

slice, _ := bufRd.ReadSlice('D')
fmt.Println(bufRd.Buffered(), slice, string(slice))
// 26 [66 67 68] BCD

str, _ := bufRd.ReadString('G')
fmt.Println(bufRd.Buffered(), str)
// 23 EFG

bufRd.Reset(bytes.NewReader([]byte("Hello World.\nMy World.")))

str, _ = bufRd.ReadString('r')
fmt.Println(bufRd.Buffered(), str)
// 13 Hello Wor

bufRd.UnreadByte()
fmt.Println(bufRd.Buffered())
// 14

str, _ = bufRd.ReadString('\n')
fmt.Println(bufRd.Buffered(), str)
// 9 rld.
//

r, size, _ = bufRd.ReadRune()
fmt.Println(bufRd.Buffered(), r, string(r), size)
// 8 77 M 1

bufRd.UnreadRune()
fmt.Println(bufRd.Buffered())
// 9

str, _ = bufRd.ReadString('\n')
fmt.Println(bufRd.Buffered(), str)
// 0 My World.

}

Writer结构体

1
2
3
4
5
6
7
8
9
// Writer 实现了对io.Writer对象的缓冲功能
// 如果在写入Writer时发生错误,则会停止继续写入和后续操作并返回这个错误。
// 在所有数据被写入后,应当调用Flush方法来保证所有数据都被写入底层Writer。
type Writer struct {
err error
buf []byte
n int
wr io.Writer
}

func NewWriter(w io.Writer) *Writer
// 将w封装成一个bufio.Writer对象,缓存大小为4096.

func NewWriterSize(w io.Writer, size int) *Writer
// 将w封装成一个拥有size大小缓存的bufio.Writer对象

func (b *Writer) Available() int
// 返回缓存中的可用空间

func (b *Writer) Buffered() int
// 返回缓存中未提交的数据长度

func (b *Writer) Flush() error
// 将缓存中的数据提交到底层io.Writer中

func (b *Writer) ReadFrom(r io.Reader) (n int64, err error)
// 实现了io.ReaderFrom接口。
// 如果底层writer支持ReadFrom方法且b的缓存数据为空,则直接调用底层的ReadFrom方法,不使用缓存。

func (b *Writer) Reset(w io.Writer)
// 丢弃所有未被提交的缓存数据,重置所有状态并切换io.Writer到w

func (b *Writer) Size() int
// 以字节为单位返回(底层)缓冲区大小

func (b *Writer) Write(p []byte) (nn int, err error)
// 将p中的数据写入b中,返回写入的字节数
// 如果写入的字节数小于p的长度,则返回错误

func (b *Writer) WriteByte(c byte) error
// 写入一个字节

func (b *Writer) WriteRune(r rune) (size int, err error)
// 写入一个UTF8编码字符

func (b *Writer) WriteString(s string) (int, error)
// 写入一个字符串,返回写入的字节数

Writer的示列:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package bufio

import (
"bufio"
"bytes"
"fmt"
)

func Writer(){
bt := make([]byte, 0)
Bt := bytes.NewBuffer(bt)
bufWt := bufio.NewWriter(Bt)

fmt.Println(bufWt.Available(), bufWt.Buffered()) // 4096 0

// Write
nn, _ := bufWt.Write([]byte{65, 66})
fmt.Println(bufWt.Available(), bufWt.Buffered()) // 4094 2
fmt.Println(nn, Bt) // 2 "空"
bufWt.Flush()
fmt.Println(bufWt.Available(), bufWt.Buffered()) // 4096 0
fmt.Println(nn, Bt) // 2 AB

// WriteByte
bufWt.WriteByte('C')
fmt.Println(bufWt.Available(), bufWt.Buffered()) // 4095 1
fmt.Println(Bt) // AB
bufWt.Flush()
fmt.Println(bufWt.Available(), bufWt.Buffered()) // 4096 0
fmt.Println(Bt) // ABC

// WriteRune
size, _ := bufWt.WriteRune('D')
fmt.Println(bufWt.Available(), bufWt.Buffered()) // 4095 1
fmt.Println(size, Bt) // 1 ABC
bufWt.Flush()
fmt.Println(bufWt.Available(), bufWt.Buffered()) // 4096 0
fmt.Println(Bt) // ABCD

// WriteString
n, _ := bufWt.WriteString("EFG")
fmt.Println(bufWt.Available(), bufWt.Buffered()) // 4093 3
fmt.Println(n, Bt) // 3 ABCD
bufWt.Flush()
fmt.Println(bufWt.Available(), bufWt.Buffered()) // 4096 0
fmt.Println(Bt) // ABCDEFG

fmt.Println(bufWt.Size()) // 4096

// ReadFrom without buffered
some := bytes.NewReader([]byte("HIJKLMN"))
nChar, _ := bufWt.ReadFrom(some)
fmt.Println(bufWt.Available(), bufWt.Buffered()) //4096 0
fmt.Println(nChar, Bt) //7 ABCDEFGHIJKLMN
// 这里不用调用flush,因为在ReadForm之前缓存中没有数据,直接调用底层的ReadFrom写入了数据
// 下面看下有缓存的情况,先写入一个换行
bufWt.WriteByte('\n')
fmt.Println(bufWt.Available(), bufWt.Buffered()) // 4095 1
fmt.Println(Bt) // ABCDEFGHIJKLMN
// 紧接着不Flash而是直接ReadForm
some2 := bytes.NewReader([]byte("OPQRST"))
nChar, _ = bufWt.ReadFrom(some2)
fmt.Println(bufWt.Available(), bufWt.Buffered()) //4089 7
fmt.Println(nChar, Bt) //6 ABCDEFGHIJKLMN
// 现在Flush然后看各个变量
bufWt.Flush()
fmt.Println(bufWt.Available(), bufWt.Buffered()) // 4096 0
fmt.Println(Bt) // ABCDEFGHIJKLMN"换行"OPQRST
}

Scanner结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Scanner为读取数据提供了便捷的接口,这些数据包括但不限于有换行分界符的文本文件。
// 连续调用Scan方法将会遍历文件的'Token'(指定部分)且跳过指定部分之间的字节。
// 指定部分的格式由SplitFunc类型的函数定义,默认的切分函数根据行尾将输入拆分成行但不包括行尾
// 本包下定义并提供的切分函数用于将文件扫描成line,bytes,UTF-8Encoded runes和空格拆分的单词。用户也可以使用自己的切分函数来替换它。
// 当扫描遇到EOF、第一次I/O错误或'指定部分'太大无法存入缓冲区时停止。
// 当一次扫描停止时,读取器有可能早已超出'指定部分'很远。如果程序想要对数据进行更多的控制,如错误处理或扫描更大的'指定部分'或顺序扫描,则应当使用bufio.reader来代替。
type Scanner struct {
r io.Reader // 用户提供的reader.
split SplitFunc // 拆分token的方法.
maxTokenSize int // token的上限
token []byte // split返回的最后token.
buf []byte // 作为参数给split的缓冲区.
start int // 缓冲区起始位.
end int // 缓冲区终止位.
err error // Sticky error.
empties int // 连续出现空token的次数.
scanCalled bool // 扫描是否开始.
done bool // 扫描完毕.
}

func (s *Scanner) Buffer(buf []byte, max int)
// 用于设置自定义缓存以及可扩展范围,如果max小于len(buf),则buf的尺寸将固定不可调。
// Buffer必须在第一次Scan之前设置,否则引发Panic。
// 默认情况下,Scanner将会使用一个4096-bufio.MaxScanTokenSize大小的内部缓存。

func (s *Scanner) Bytes() []byte
// 将最后一次扫描出的‘指定部分’作为切片(引用)返回
// 下一次的扫描会覆盖本结果

func (s *Scanner) Err() error
// 返回扫描过程中遇到的非EOF错误

func (s *Scanner) Scan() bool
// 扫描数据中的‘指定部分’

func (s *Scanner) Split(split SplitFunc)
// 设置Scanner的分切函数,必须在Scan方法之前调用

func (s *Scanner) Text() string
// 最后一次扫描出的‘指定部分’作为String返回

Scanner的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
package bufio

import (
"bufio"
"bytes"
"fmt"
)

func Scan(){
b1 := bytes.NewReader([]byte("ABC\nDEF\r\nGHI\nJKL"))
bs := bufio.NewScanner(b1)
for bs.Scan() {
fmt.Printf("%s %v\n", bs.Bytes(), bs.Text())
}
// ABC ABC
// DEF DEF
// GHI GHI
// JKL JKL
b2 := bytes.NewReader([]byte("ABC\nDEF GHI JKL"))
bs = bufio.NewScanner(b2)
bs.Split(bufio.ScanWords)
for bs.Scan() {
fmt.Println(bs.Text())
}
// ABC
// DEF
// GHI
// JKL
b3 := bytes.NewReader([]byte("Hello 世界!"))
bs = bufio.NewScanner(b3)
bs.Split(bufio.ScanRunes)
for bs.Scan() {
fmt.Printf("%v ", bs.Text())
}
// H e l l o 世 界 !
fmt.Println()
b4 := bytes.NewReader([]byte("我的天哪!"))
bs = bufio.NewScanner(b4)
bs.Split(bufio.ScanBytes)
for bs.Scan() {
fmt.Printf("%v ", bs.Bytes())
}
// [230] [136] [145] [231] [154] [132] [229] [164] [169] [229] [147] [170] [33]
fmt.Println()
}

另外的参考:

阅读剩下更多

默认配图
返回顶部