-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathline.go
168 lines (152 loc) · 4.41 KB
/
line.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
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
package file
import (
"bufio"
"errors"
"fmt"
"io"
"os"
)
// LineFunc stands for a handler for each line string.
type LineFunc func(line string) (err error)
//revive:disable:error-naming It's not a real error
var (
// QuitRead indicates the arbitrary error means to quit from reading.
QuitRead = errors.New("file: quit read by line")
)
// ReadFileLines reads all lines from the given file (the line ending chars are not included).
func ReadFileLines(path string) (lines []string, err error) {
err = readFileByLine(path, func(l string) error {
lines = append(lines, l)
return nil
})
return
}
// CountFileLines counts all lines from the given file (the line ending chars are not included).
func CountFileLines(path string) (count int, err error) {
err = readFileByLine(path, func(l string) error {
count++
return nil
})
return
}
// WriteFileLines writes the given lines as a text file.
func WriteFileLines(path string, lines []string) error {
return openFileWriteLines(path, createFileFlag, lines)
}
// AppendFileLines appends the given lines to the end of a text file.
func AppendFileLines(path string, lines []string) error {
return openFileWriteLines(path, appendFileFlag, lines)
}
// ReadFirstLines reads the top n lines from the given file (the line ending chars are not included), or lesser lines if the given file doesn't contain enough line ending chars.
func ReadFirstLines(path string, n int) (lines []string, err error) {
var f *os.File
if f, err = os.Open(path); err != nil {
return
}
defer f.Close()
return extractIOTopLines(f, n)
}
// ReadLastLines reads the bottom n lines from the given file (the line ending chars are not included), or lesser lines if the given file doesn't contain enough line ending chars.
func ReadLastLines(path string, n int) (lines []string, err error) {
var f *os.File
if f, err = os.Open(path); err != nil {
return
}
defer f.Close()
return extractIOBottomLines(f, n)
}
// extractIOTopLines extracts the top n lines from the given stream (the line ending chars are not included), or lesser lines if the given stream doesn't contain enough line ending chars.
func extractIOTopLines(rd io.Reader, n int) ([]string, error) {
if n <= 0 {
return nil, errors.New("n should be greater than 0")
}
result := make([]string, 0)
if err := readIOByLine(rd, func(line string) error {
result = append(result, line)
n--
if n <= 0 {
return QuitRead
}
return nil
}); err != nil {
return nil, err
}
return result, nil
}
// extractIOBottomLines extracts the bottom n lines from the given stream (the line ending chars are not included), or lesser lines if the given stream doesn't contain enough line ending chars.
func extractIOBottomLines(rd io.Reader, n int) ([]string, error) {
if n <= 0 {
return nil, errors.New("n should be greater than 0")
}
var (
result = make([]string, n, n)
cnt int
)
if err := readIOByLine(rd, func(line string) error {
result[cnt%n] = line
cnt++
return nil
}); err != nil {
return nil, err
}
if cnt <= n {
return result[0:cnt], nil
}
pos := cnt % n
return append(result[pos:], result[0:pos]...), nil
}
// readFileByLine iterates the given file by lines (the line ending chars are not included).
func readFileByLine(path string, callback LineFunc) (err error) {
var file *os.File
if file, err = os.Open(path); err != nil {
return
}
defer file.Close()
return readIOByLine(file, callback)
}
func openFileWriteLines(path string, flag int, lines []string) error {
file, err := os.OpenFile(path, flag, filePerm)
if err != nil {
return err
}
defer file.Close()
return writeIOLines(file, lines)
}
// writeIOLines writes the given lines to a Writer.
func writeIOLines(wr io.Writer, lines []string) error {
w := bufio.NewWriter(wr)
defer w.Flush()
for _, line := range lines {
if _, err := fmt.Fprintln(w, line); err != nil {
return err
}
}
return nil
}
// readIOByLine iterates the given Reader by lines (the line ending chars are not included).
func readIOByLine(rd io.Reader, callback LineFunc) (err error) {
readLine := func(r *bufio.Reader) (string, error) {
var (
err error
line, ln []byte
isPrefix = true
)
for isPrefix && err == nil {
line, isPrefix, err = r.ReadLine()
ln = append(ln, line...)
}
return string(ln), err
}
r := bufio.NewReader(rd)
s, e := readLine(r)
for e == nil {
if err = callback(s); err != nil {
break
}
s, e = readLine(r)
}
if err == QuitRead {
err = nil
}
return
}