bitbucket.org/ai69/amoy@v0.2.3/line.go (about) 1 package amoy 2 3 import ( 4 "bufio" 5 "errors" 6 "fmt" 7 "io" 8 "os" 9 ) 10 11 // LineFunc stands for a handler for each line string. 12 type LineFunc func(line string) (err error) 13 14 //revive:disable:error-naming It's not a real error 15 var ( 16 // QuitRead indicates the arbitrary error means to quit from reading. 17 QuitRead = errors.New("amoy: quit read by line") 18 ) 19 20 // ReadFileByLine iterates the given file by lines (the line ending chars are not included). 21 func ReadFileByLine(path string, callback LineFunc) (err error) { 22 var file *os.File 23 if file, err = os.Open(path); err != nil { 24 return 25 } 26 defer file.Close() 27 return ReadByLine(file, callback) 28 } 29 30 // ReadByLine iterates the given Reader by lines (the line ending chars are not included). 31 func ReadByLine(rd io.Reader, callback LineFunc) (err error) { 32 readLine := func(r *bufio.Reader) (string, error) { 33 var ( 34 err error 35 line, ln []byte 36 isPrefix = true 37 ) 38 for isPrefix && err == nil { 39 line, isPrefix, err = r.ReadLine() 40 ln = append(ln, line...) 41 } 42 return string(ln), err 43 } 44 r := bufio.NewReader(rd) 45 s, e := readLine(r) 46 for e == nil { 47 if err = callback(s); err != nil { 48 break 49 } 50 s, e = readLine(r) 51 } 52 53 if err == QuitRead { 54 err = nil 55 } 56 return 57 } 58 59 // ReadFileLines reads all lines from the given file (the line ending chars are not included). 60 func ReadFileLines(path string) (lines []string, err error) { 61 err = ReadFileByLine(path, func(l string) error { 62 lines = append(lines, l) 63 return nil 64 }) 65 return 66 } 67 68 // ReadLines reads all lines from the given reader (the line ending chars are not included). 69 func ReadLines(rd io.Reader) (lines []string, err error) { 70 err = ReadByLine(rd, func(l string) error { 71 lines = append(lines, l) 72 return nil 73 }) 74 return 75 } 76 77 // CountFileLines counts all lines from the given file (the line ending chars are not included). 78 func CountFileLines(path string) (count int, err error) { 79 err = ReadFileByLine(path, func(l string) error { 80 count++ 81 return nil 82 }) 83 return 84 } 85 86 // CountLines counts all lines from the given reader (the line ending chars are not included). 87 func CountLines(rd io.Reader) (count int, err error) { 88 err = ReadByLine(rd, func(l string) error { 89 count++ 90 return nil 91 }) 92 return 93 } 94 95 // WriteLines writes the given lines to a Writer. 96 func WriteLines(wr io.Writer, lines []string) error { 97 w := bufio.NewWriter(wr) 98 defer w.Flush() 99 for _, line := range lines { 100 if _, err := fmt.Fprintln(w, line); err != nil { 101 return err 102 } 103 } 104 return nil 105 } 106 107 // WriteFileLines writes the given lines as a text file. 108 func WriteFileLines(path string, lines []string) error { 109 return openFileWriteLines(path, createFileFlag, lines) 110 } 111 112 // AppendFileLines appends the given lines to the end of a text file. 113 func AppendFileLines(path string, lines []string) error { 114 return openFileWriteLines(path, appendFileFlag, lines) 115 } 116 117 func openFileWriteLines(path string, flag int, lines []string) error { 118 file, err := os.OpenFile(path, flag, filePerm) 119 if err != nil { 120 return err 121 } 122 defer file.Close() 123 return WriteLines(file, lines) 124 } 125 126 // ExtractTopLines 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. 127 func ExtractTopLines(rd io.Reader, n int) ([]string, error) { 128 if n <= 0 { 129 return nil, errors.New("amoy: n should be greater than 0") 130 } 131 result := make([]string, 0) 132 if err := ReadByLine(rd, func(line string) error { 133 result = append(result, line) 134 n-- 135 if n <= 0 { 136 return QuitRead 137 } 138 return nil 139 }); err != nil { 140 return nil, err 141 } 142 return result, nil 143 } 144 145 // ExtractFirstLine extracts the first line from the given stream (the line ending chars are not included). 146 func ExtractFirstLine(rd io.Reader) (string, error) { 147 lines, err := ExtractTopLines(rd, 1) 148 if err != nil { 149 return EmptyStr, err 150 } 151 if len(lines) < 1 { 152 return EmptyStr, nil 153 } 154 return lines[0], nil 155 } 156 157 // ExtractBottomLines 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. 158 func ExtractBottomLines(rd io.Reader, n int) ([]string, error) { 159 if n <= 0 { 160 return nil, errors.New("amoy: n should be greater than 0") 161 } 162 var ( 163 result = make([]string, n, n) 164 cnt int 165 ) 166 if err := ReadByLine(rd, func(line string) error { 167 result[cnt%n] = line 168 cnt++ 169 return nil 170 }); err != nil { 171 return nil, err 172 } 173 if cnt <= n { 174 return result[0:cnt], nil 175 } 176 pos := cnt % n 177 return append(result[pos:], result[0:pos]...), nil 178 } 179 180 // ExtractLastLine extracts the last line from the given stream (the line ending chars are not included). 181 func ExtractLastLine(rd io.Reader) (string, error) { 182 lines, err := ExtractBottomLines(rd, 1) 183 if err != nil { 184 return EmptyStr, err 185 } 186 if len(lines) < 1 { 187 return EmptyStr, nil 188 } 189 return lines[len(lines)-1], nil 190 }