-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathfileinput.go
176 lines (160 loc) · 3.85 KB
/
fileinput.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
169
170
171
172
173
174
175
176
// An analogous article of fileinput module in Python
//
// Examples:
//
// // reverse.go
// // print file lines in reverse order.
// package main
//
// import (
// "fmt"
// "os"
//
// "github.com/doloopwhile/go-fileinput"
// )
//
// func main() {
// sc := fileinput.Lines(os.Args[1:])
// lines := []string{}
// for sc.Scan() {
// l := sc.Text()
// lines = append(lines, l)
// }
// if sc.Err() != nil {
// os.Stderr.WriteString(sc.Err().Error() + "\n")
// os.Exit(1)
// }
// for i := len(lines) - 1; i >= 0; i-- {
// fmt.Printf("%02d: %s\n", i+1, lines[i])
// }
// }
package fileinput
import (
"bufio"
"io"
"os"
)
// Lines returns a new Scanner to read lines of files in args.
// If args is empty, it return a Scanner which scans os.Stdin.
func Lines(args []string) *Scanner {
if len(args) == 0 {
args = []string{"-"}
}
return &Scanner{
Args: args,
Open: StdOpen,
}
}
type (
// Scanner provides a interface like bufio.Scanner
// to reading data from multiple files.
//
// It is not expected that members of Scanner is modified after first call of .Scan()
// If it was, it is undefined what happen.
Scanner struct {
Args []string // Names of files. It should be os.Args[1:] in typical use case.
Open func(name string) (io.ReadCloser, error) // Function to open files.
SplitFunc bufio.SplitFunc // Argument of Split() of bufio.Split.
sc *bufio.Scanner
rc io.ReadCloser
icurr int
err error
filename string
lineNo int
fileLineNo int
closed bool
}
)
// Scan advances internal scanner to the next token like Scan method of bufio.Scanner.
// It automatically open/close files specified in Args.
func (s *Scanner) Scan() bool {
if s.closed {
return false
}
for s.icurr < len(s.Args) {
if s.err != nil {
return false
}
if s.sc == nil {
s.rc, s.err = s.Open(s.Args[s.icurr])
if s.err != nil {
return false
}
s.sc = bufio.NewScanner(s.rc)
if s.SplitFunc != nil {
s.sc.Split(s.SplitFunc)
}
s.filename = s.Args[s.icurr]
s.fileLineNo = 0
}
r := s.sc.Scan()
s.err = s.sc.Err()
if r {
s.lineNo++
s.fileLineNo++
return true
}
s.Next()
}
return false
}
// Next close current file and so that the next iteration will read the first line from the next file (if any).
func (s *Scanner) Next() {
if s.closed {
return
}
if s.rc != nil {
err := s.rc.Close()
if s.err == nil {
s.err = err
}
s.rc = nil
s.sc = nil
}
s.icurr++
}
// Text returns the most recent token generated by a call to Scan as a newly allocated string holding its bytes.
func (s *Scanner) Text() string {
if s.err != nil || s.closed || s.sc == nil {
return ""
}
return s.sc.Text()
}
// Err returns the first non-EOF error
// that was encountered by the Scanner or was returned by Open.
func (s *Scanner) Err() error {
return s.err
}
// Filename return the name of the file currently being read.
// Before the first line has been read, returns empty string.
func (s *Scanner) Filename() string {
return s.filename
}
// LineNo returns the cumulative line number of the line just read.
func (s *Scanner) LineNo() int {
return s.lineNo
}
// FileLineNo returns the line number in the file of the line just read.
func (s *Scanner) FileLineNo() int {
return s.fileLineNo
}
// IsFirstLine returns the line number in the file of the line just read.
func (s *Scanner) IsFirstLine() bool {
return s.fileLineNo == 1
}
// Close closes the Scanner.
func (s *Scanner) Close() error {
alreadyErr := (s.err != nil)
s.Next()
if alreadyErr {
return nil
}
return s.err
}
// StdOpen open file with os.Open. However, it returns os.Stdin for "-".
func StdOpen(name string) (io.ReadCloser, error) {
if name == "-" {
return os.Stdin, nil
}
return os.Open(name)
}