-
Notifications
You must be signed in to change notification settings - Fork 0
/
第 12.00.04、bufio 包
443 lines (293 loc) · 15.6 KB
/
第 12.00.04、bufio 包
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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
总览
bufio 包实现了带缓存的 I/O
它包裹了 io.Reader 和 io.Writer 对象,来创建另一个对象(Reader 或者 Writer),
这个对象同样实现了该接口,但提供了缓存和字符(textual) I/O 的一些帮助功能
常量
const (
// MaxScanTokenSize 是用来缓存单个 token 最大的大小
// 除非用户通过 Scan.Buffer 明确指定了缓存的大小
// 实际的最大 token 大小应该小于缓存,
// 由于缓存可能需要包括,举例来说,一个 newline
MaxScanTokenSize = 64 * 1024
)
变量
var (
ErrInvalidUnreadByte = errors.New("bufio: invalid use of UnreadByte")
ErrInvalidUnreadRune = errors.New("bufio: invalid use of UnreadRune")
ErrBufferFull = errors.New("bufio: buffer full")
ErrNegativeCount = errors.New("bufio: negative count")
)
Scanner 返回的错误
var (
ErrTooLong = errors.New("bufio.Scanner: token too long")
ErrNegativeAdvance = errors.New("bufio.Scanner: SplitFunc returns negative advance count")
ErrAdvanceTooFar = errors.New("bufio.Scanner: SplitFunc returns advance count beyond input")
)
ErrFinalToken 是一个特殊的守卫错误值
它的意义是,被 Split 函数返回,来指示现在发出的错误是因为,已经读到了最后一个 token,扫描操作应该在这次操作之后停止
在 Scan 收到这个错误之后,会停止扫描,并且不会返回错误
这个值常用于提前停止过程,或当必须要发出一个最终的空 token 时
也可以通过一个自定义的错误达到相同的行为,但这里提供的更简洁
查看 emptyFinalToken 例子来了解该值的使用
var ErrFinalToken = errors.New("final token")
ScanBytes(data []byte, atEOF bool) (advance int, token []byte, err error)
本函数对于一个 Scanner 来说是一个分离函数,将每个字节都作为一个 token 返回
ScanLines(data []byte, atEOF bool) (advance int, token []byte, err error)
本函数对于一个 Scanner 来说是一个分离函数,返回文本的每一行,并且移除任何 EOL 标志
返回的行可能为空
EOL 标志是一个可选的回车符后跟随一个强制新行符
用正则表达式,表示为 `\r?\n`
输入的最后一个非空行将被返回,即使其没有换行符
ScanRunes(data []byte, atEOF bool) (advance int, token []byte, err error)
本函对于一个 Scanner 来说是一个分离函数,将每一个 UTF-8 编码的 rune 作为一个 token 返回
返回的 rune 等价于,对以字符串形式呈现的输入,做一个 range 循环,
也就说,错误的 UTF-8 编码将被替换为 U+FFFD = "\xef\xbf\xbd"
由于 Scan 接口,客户端无法分辨这是正常编码的替代字符(replacement runes)还是编码错误导致的结果
ScanWords(data []byte, atEOF bool) (advance int, token []byte, err error)
本函对于一个 Scanner 来说是一个分离函数,返回文本中以空格间隔的每个单词,其周边的空格会被删除
它永远不会返回一个空字符串
空格的定义来自 unicode.IsSpace
ReadWriter struct 类型
ReadWriter 存储了指向一个 Reader 和一个 Writer 的指针
它实现了 io.ReadWriter
type ReadWriter struct {
*Reader
*Writer
}
NewReadWriter(r *Reader, w *Writer) *ReadWriter
分配一个新的 ReadWriter 来分发至 r 和 w
Reader struct 类型
Reader 实现了一个具有缓存的 io.Reader 对象
type Reader struct {
// 包含过滤或未导出字段
}
NewReader(rd io.Reader) *Reader
返回一个新的 Reader,其具有默认的缓存大小
NewReaderSize(rd io.Reader, size int) *Reader
返回一个新的 Reader,其大小最少有指定的 size
若作为参数的 io.Reader 已经具有足够大的大小,它返回底层的 Reader
(b *Reader) Buffered() int
返回从当前的缓存中可以读取的字节数
(b *Reader) Discard(n int) (discarded int, err error)
跳过 n 个字节,返回掉过的字节数
若本函数跳过的少于 n 字节,它同样会返回一个错误
若 0 <= n <= b.Buffered(),本函数保证成功,而不去读取底层的 io.Reader
(b *Reader) Peek(n int) ([]byte, error)
返回下 n 个字节的数据,而不移动 reader(的读取位置)
这些字节在下次读取调用前都是有效的
若本函数返回少于 n 字节的数据,它同样会返回一个错误,解释为什么读取比较短
若 n 比 b 的缓存空间要大,则返回错误 ErrBufferFull
(b *Reader) Read(p []byte) (n int, err error)
读取数据至 p 中
它返回读取至 p 的字节数量
字节从作用于底层 Reader 的最多一个 Read 中读取
因此 n 可能会少于 len(p)
在 EOF 处,计数为 0, err 为 io.EOF
(b *Reader) ReadByte() (byte, error)
返回单个字节
若没有字节可用,返回一个错误
(b *Reader) ReadBytes(delim byte) ([]byte, error)
读取直到第一次遇到输入中的 delim,返回直到且包含 delimiter 的 []byte
若本方法在找到一个 delimiter 之前遇见了一个错误,则它返回遇到错误之前的数据,以及错误本身(通常为 io.EOF)
当且仅当返回的数据不以 delim 作为结尾时,本方法返回 err != nil
就简单使用而言,一个 Scanner 可能更为方便
(b *Reader) ReadLine() (line []byte, is Prefix bool, err error)
本方法是一个底层的行读取原函数
大多数调用者应该使用 ReadBytes('\n') 或者 ReadString('\n') 或者 Scanner 代替
本方法尝试返回一个单行,且不包含 EOF 字节
若行过长,超出了缓存的容量,则 isPrefix 被设置,且行的开头被返回
该行的剩余部分会在其后的调用中返回
当该行的最后一部分返回时,isPrefix 被设置为 false
返回的缓存直到下次调用不问你方法时才可用
本方法要么返回非 nil 行,要么返回一个错误,绝不会同时返回
从本方法返回的文本都不包含行尾("\r\n" 或者 "\n")
若输入没有以行尾作为结尾,它也不会发出指示或者报错
在调用本方法之后,再调用 UnreadByte,将永远 unread 最后一个读取的字节(可能是属于行尾的一个字符),
尽管那个字节并不在本函数返回的部分中
(b *Reader) ReadRune() (r rune, size int, err error)
读取单个 UTF-8 编码的 Unicode 字符,并返回该 rune 以及对应的字节大小
若编码的 rune 是非法的,它消耗一个字节,并返回 unicode.ReplacementChar(U+FFFD)和 size = 1
(b *Reader) ReadSlice(delim byte) (line []byte, err error)
读取直到遇见输入中的第一个 delim,返回一个 slice,其指向缓存中的字节
在下次读取前,缓存的数据都是可用的
若本方法在找到 delimiter 之前就遇到了一个错误,它返回所有在缓存中的数据,以及错误本身(通常为 io.EOF)
若缓存填满却不包含 delim,则本方法返回 ErrBufferFull
由于从本方法返回的数据将被下次 I/O 覆写,大多数客户端应该使用 ReadBytes 或者 ReadString 替代
当且仅当行不以 delim 结尾时,本函数的 err != nil
(b *Reader) ReadString(delim byte) (string, error)
读取直到遇到输入中的第一个 delim,返回一个字符串,包含读取的数据以及 delimiter 本身
若本方法在找到 delimeter 之前就遇到的了一个错误,它返回所有错误之前读取的数据以及错误本身(通常为 io.EOF)
当且仅当返回的数据不以 delim 作为结尾时,本函数的 err != nil
就简单使用而言,一个 Scanner 可能更为方便
(b *Reader) Reset(r io.Reader)
取消任何缓存的数据,重置所有状态,并切换 buffered reader 来从 r 中读取
(b *Reader) UnreadByte() error
unread 最后一个字节
仅有最近读取的一个字节才可以被 unread
(b *Reader) UnreadRune() error
unread 最后一个 rune
若最近一次缓存的读取操作并非 ReadRune,本方法返回错误
(在这层面它比 UnreadByte 要严格,UnreadByte 会从任何读取操作中 unread 最后一个读取的字节)
(b *Reader) WriteTo(w io.Writer) (n int64, err error)
本方法实现了 io.WriteTo
这可以对底层的 Reader 多重调用 Read 方法
Scanner struct 类型
Scanner 提供了一个读取数据的便捷的接口,比如用 newline 分隔的文本文件
顺次调用 Scan 方法将会遍历整个文件的 “token”,跳过 token 之间的字节
token 的指定来自于类型为 SplitFunc 的分离函数
默认的分离函数将输入切开为行,并去除行终止符
本包中提供的分离函数用于将文件扫描分解为 行、字节、UTF-8 编码的字符、空格分隔的词
客户端可以自定义分离函数
扫描在 EOF、第一个 I/O 错误、以及 token 大小大于缓冲的情况下不可恢复的停下
当一个扫描停下,reader可能已经提前了任意源的位置,越过了最后一个 token
若程序需要控制错误处理、使用更大的 token,或者必须在 reader 上运行扫描,则应该使用 bufio.Reader
type Scanner struct {
// 包含过滤或者未导出的字段
}
EG.【自定义】
使用自定义的分离函数(通过包裹 ScanWords)的 Scanner,来验证 32 bit 十进制输入
// 人造输入源
const input = "1234 5678 1234567901234567890"
scanner := bufio.NewScanner(strings.NewReader(input))
// 通过包裹现有 ScanWord 函数创造的自定义分离函数
split := func(data []byte, atEOF bool) (advance int, token []byte, err error) {
advance, token, err = bufio.ScanWords(data, atEOF)
if err == nil && token != nil {
_, err = strconv.ParseInt(string(token), 10, 32)
}
return
}
// 为扫描操作设置分离函数
scanner.Split(split)
// 验证输入
for scanner.Scan() {
fmt.Printf("%s\n", scanner.Text())
}
if err := scanner.Err(); err != nil {
fmt.Printf("Invalid input: %s", err)
}
EG.【空的结尾Token】
使用自定义的分离函数的 Scanner,解析具有空结束值的逗号分隔的列表
// 逗号分隔列表,最后一项为空
const input = "1,2,3,4,"
scanner := bufio.NewScanner(strings.NewReader(input))
// 定义一个逗号分隔的分离函数
onComma := func(data []byte, atEOF bool) (advance int, token []byte, err error) {
for i := 0; i < len(data); i++ {
if data[i] == ',' {
return i + 1, data[:i], nil
}
}
// 剩余最后一个 token 待发送,其值可能为空字符串
// 这里返回的 bufio.ErrFinalToken 告诉 Scan 这个 token 之后没有更多 token 了
// 但并不要触发 Scan 自身返回一个错误
return 0, data, bufio.ErrFinalToken
}
scanner.Split(onComma)
// 扫描
for scanner.Scan() {
fmt.Printf("%q ", scanner.Text())
}
if err := scanner.Err(); err != nil {
fmt.Fprintln(os.Stderr, "reading input:", err)
}
EG.【行】
最简单的 Scanner 用法,将标准输入解析为一些列行
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
fmt.Println(scanner.Text()) // Println 将加回最后的 '\n'
}
if err := scanner.Err(); err != nil {
fmt.Fprintln(os.Stderr, "reading standard input:", err)
}
EG.【词】
使用 Scanner 实现一个简单的词计数器工具,通过将输入扫描为一系列空格间隔的 token
// 人工输入源
const input = "Now is the winter of our discontent,\nMade glorious summer by this sun of York.\n"
scanner := bufio.NewScanner(strings.NewReader(input))
// 设置扫描操作使用的分离函数
scanner.Split(bufio.ScanWords)
// 词计数
count := 0
for scanner.Scan() {
count++
}
if err := scanner.Err(); err != nil {
fmt.Fprintln(os.Stderr, "reading input:", err)
}
fmt.Printf("%d\n", count)
NewScanner(r io.Reader) *Scanner
返回从 r 中读取数据的新 Scanner
分离函数默认使用 ScanLines
(s *Scanner) Buffer(buf []byte, max int)
设置扫描式初始的缓存,以及扫描过程中可以分配的最大缓存大小
最大 token 大小是 max 和 cap(buf) 的较大者
若 max <= cap(buf), Scan 将仅使用这个缓存,不会分配更多的缓存
默认情况下,Scan 使用一个内部的缓存,并将最大 token 大小设置为 MaxScanTokenSize
若扫描开始后调用本函数,本函数产生 panic
(s *Scanner) Bytes() []byte
返回最近一次调用 Scan 时产生的 token
底层数组指向的数组可能会在其后调用 Scan 时被覆写
它不会进行分配
(s *Scanner) Err() error
返回 Scanner 遇到的第一个非 EOF 错误的错误
(s *Scanner) Scan() bool
本方法移动 Scanner 至下一个 token,该 token 将通过 Bytes 或者 Text 方法变得可用
当扫描终止时,它返回 false,这有可能是因为到达输入的尾部,或者遇见一个错误
在本函数返回 false 之后, Err 方法将返回任何在扫描过程中遇见的错误,但,若错误是 io.EOF,则 Err 返回 nil
若分离函数返回了 100 个空 token 而并没有在输入中前进,则本函数 panic
这是一个对于 scanner 通用的错误模式
(s *Scanner) Split(split SplitFunc)
为 Scanner 设置分离函数
默认的分离函数是 ScanLines
若扫描开始后调用本函数,本函数产生 panic
(s *Scanner) Text() string
返回最近一次调用 Scan 产生的 token,该 token 使用新分配的字符串持有其数据
SplitFunc func(data []byte, atEOF bool) (advance int, token []byte, err error) 类型
SplitFunc 是用于将输入 token 化的分离函数的签名
参数是一个仍未处理的数据的子字符串,以及一个 flag,tEOF,其报告了 Reader 是没有更多数据来提供
返回值为输入中前移的字节数,以及返回给用户的下一个 token,以及任何产生的错误
若数据不含有完整的 token,比如若它在扫描行中不含有 newline,
SplitFunc 可以返回 (0, nil, nil) 来提示 Scanner 读入更多数据至 slice 中,
并使更长的,但起始点认为输入的原位置的 slice 来重试
若返回的错误非 nil,扫描停止,错误返回客户端
函数调用时,永远不会使用空数据 slice,除非 atEOF 为真
若 atEOF 为真,但,数据可能为非空,也就是说,起具有未处理的文本
type SplitFunc func(data []byte, atEOF bool) (advance int, token []byte, err error)
Writer struct 类型
Writer 实现了对于 io.Writer 对象的缓存
若写入 Writer 时遇到了一个错误,就不会接受更多的数据,且其后任何写入、Flush 都会返回错误
在所有数据都写入后,客户端应该调用 Flush 方法,保证所有的数据都被传入底层的 io.Writer
type Writer struct {
// 包含过滤或未导出的字段
}
EG.
w := bufio.NewWriter(os.Stdout)
fmt.Fprint(w, "Hello, ")
fmt.Fprint(w, "world!")
w.Flush() // 勿忘刷写
NewWriter(w io.Writer) *Writer
返回一个新的 Writer,其具有默认大小的缓存
NewWriterSize(w io.Writer, size int) *Writer
返回一个新的 Writer,其具有至少指定大小的缓存
若参数 io.Writer 已经是一个足够大的 io.Writer,则返回底层的 Writer
(b *Writer) Available() int
返回缓存中未使用的字节数
(b *Writer) Buffered() int
返回已经写入缓存中的字节数
(b *Writer) Flush() err
将任何缓存的数据写入底层的 io.Writer
(b *Writer) Reset(w io.Writer)
取消任何未 flush 的缓存的数据,清除任何错误,并重置 b,将其输出写入 w 中
(b *Writer) Write(p []byte) (nn int, err error)
将 p 中的内容写入缓存中
返回写入的字节数
若 nn < len(p),它同时返回一个错误,来解释为什么写入短了
(b *Writer) WriteByte(c byte) error
写入单个字节
(b *Writer) WriteRune(r rune) (size int, err error)
写入单个 Unicode 码点,返回写入的字节数,以及任何出现的错误
(b *Writer) WriterString(s string) (int, error)
写入一个字符串
返回写入的字节数
若 计数小于 len(p),它同时返回一个错误,来解释为什么写入短了