如何从后往前按行读取文件,有什么思路?
临时写了一个,测试了一下可以,如用于生产环境的话还需要严格测试。
```go
package file
import (
"bytes"
"fmt"
"io"
"os"
)
var (
buffSize = 1 << 20
)
// ReadLineFromEnd --
type ReadLineFromEnd struct {
f *os.File
fileSize int
bwr *bytes.Buffer
swapBuff []byte
}
// NewReadLineFromEnd --
func NewReadLineFromEnd(name string) (rd *ReadLineFromEnd, err error) {
f, err := os.Open(name)
if err != nil {
return nil, err
}
info, err := f.Stat()
if info.IsDir() {
return nil, fmt.Errorf("not file")
}
fileSize := int(info.Size())
rd = &ReadLineFromEnd{
f: f,
fileSize: fileSize,
bwr: bytes.NewBuffer([]byte{}),
swapBuff: make([]byte, buffSize),
}
return rd, nil
}
// Read --
func (c *ReadLineFromEnd) Read(p []byte) (n int, err error) {
err = c.buff()
if err != nil {
return n, err
}
return c.bwr.Read(p)
}
// ReadLine 结尾包含'\n'
func (c *ReadLineFromEnd) ReadLine() (line []byte, err error) {
err = c.buff()
if err != nil {
return nil, err
}
return c.bwr.ReadBytes('\n')
}
// Close --
func (c *ReadLineFromEnd) Close() (err error) {
return c.f.Close()
}
func (c *ReadLineFromEnd) buff() (err error) {
if c.fileSize == 0 {
return nil
}
if c.bwr.Len() >= buffSize {
return nil
}
offset := 0
if c.fileSize > buffSize {
offset = c.fileSize - buffSize
}
_, err = c.f.Seek(int64(offset), 0)
if err != nil {
return err
}
n, err := c.f.Read(c.swapBuff)
if err != nil && err != io.EOF {
return err
}
if n == 0 {
return nil
}
for {
m := bytes.LastIndex(c.swapBuff[:n], []byte{'\n'})
if m == -1 {
break
}
if m < n-1 {
_, err = c.bwr.Write(c.swapBuff[m+1 : n])
if err != nil {
return err
}
_, err = c.bwr.Write([]byte{'\n'})
if err != nil {
return err
}
}
n = m
if n == 0 {
break
}
}
if n > 0 {
_, err := c.bwr.Write(c.swapBuff[:n])
if err != nil {
return err
}
}
c.fileSize = offset
return nil
}
```
测试代码
```go
func TestNewReadLineFromEnd(t *testing.T) {
rd, err := NewReadLineFromEnd("./123.log")
if err != nil {
t.Fatal(err)
}
defer rd.Close()
for {
data, err := rd.ReadLine()
if err != nil {
if err != io.EOF {
t.Fatal(err)
}
break
}
fmt.Print(string(data))
}
}
```
#2
更多评论
非常感激,我试了下,有两个地方改了下,一个是文件大小大于buffSize,二是最后一轮m为-1时需要把n前的打印
```golang
package main
import (
"bytes"
"fmt"
"io"
"os"
)
var (
// buffSize = 1 << 20
buffSize = 1024
buffSizeA = 0
)
// ReadLineFromEnd --
type ReadLineFromEnd struct {
f *os.File
fileSize int
bwr *bytes.Buffer
swapBuff []byte
swapStatus bool
}
// NewReadLineFromEnd --
func NewReadLineFromEnd(name string) (rd *ReadLineFromEnd, err error) {
f, err := os.Open(name)
if err != nil {
return nil, err
}
info, err := f.Stat()
if info.IsDir() {
return nil, fmt.Errorf("not file")
}
fileSize := int(info.Size())
rd = &ReadLineFromEnd{
f: f,
fileSize: fileSize,
bwr: bytes.NewBuffer([]byte{}),
swapBuff: make([]byte, buffSize),
swapStatus: false,
}
return rd, nil
}
// Read --
func (c *ReadLineFromEnd) Read(p []byte) (n int, err error) {
err = c.buff()
if err != nil {
return n, err
}
return c.bwr.Read(p)
}
// ReadLine 结尾包含'\n'
func (c *ReadLineFromEnd) ReadLine() (line []byte, err error) {
err = c.buff()
if err != nil {
return nil, err
}
return c.bwr.ReadBytes('\n')
}
// Close --
func (c *ReadLineFromEnd) Close() (err error) {
return c.f.Close()
}
func (c *ReadLineFromEnd) buff() (err error) {
if c.fileSize == 0 {
return nil
}
if c.bwr.Len() >= buffSize {
return nil
}
// 动态修改swapBuff
if c.swapStatus {
if c.fileSize < buffSize {
c.swapBuff = make([]byte, c.fileSize+buffSizeA)
} else {
c.swapBuff = make([]byte, buffSize+buffSizeA)
}
}
offset := 0
if c.fileSize > buffSize {
offset = c.fileSize - buffSize
c.swapStatus = true
} else {
c.swapStatus = false
}
_, err = c.f.Seek(int64(offset), 0)
if err != nil {
return err
}
n, err := c.f.Read(c.swapBuff)
if err != nil && err != io.EOF {
return err
}
if n == 0 {
return nil
}
for {
m := bytes.LastIndex(c.swapBuff[:n], []byte{'\n'})
if m == -1 {
if !c.swapStatus { // 判断是否为最后一轮
_, err = c.bwr.Write(c.swapBuff[:n-1]) // 到上一轮n-1为止的数据输出
if err != nil {
return err
}
_, err = c.bwr.Write([]byte{'\n'})
if err != nil {
return err
}
}
buffSizeA = n
break
}
if m < n-1 {
_, err = c.bwr.Write(c.swapBuff[m+1 : n])
if err != nil {
return err
}
_, err = c.bwr.Write([]byte{'\n'})
if err != nil {
return err
}
}
n = m //从最后一次出现的位置向前寻找
if n == 0 {
break
}
}
if n > 0 {
_, err := c.bwr.Write(c.swapBuff[:n])
if err != nil {
return err
}
}
c.fileSize = offset
return nil
}
func main() {
rd, err := NewReadLineFromEnd("./security4.txt")
if err != nil {
fmt.Println(err)
}
defer rd.Close()
for {
data, err := rd.ReadLine()
if err != nil {
if err != io.EOF {
fmt.Println(err)
}
break
}
fmt.Print(string(data))
}
}
```
#3