反向读取文件

w1281472252 · 2018-12-19 16:11:01 · 1336 次点击

非常感激,我试了下,有两个地方改了下,一个是文件大小大于buffSize,二是最后一轮m为-1时需要把n前的打印

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
更多评论

请问这样做的需求是什么?可以试试:先读取文件,然后转为string后,反转一下?

#1

临时写了一个,测试了一下可以,如用于生产环境的话还需要严格测试。

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
}

测试代码

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