github.com/bingoohuang/gg@v0.0.0-20240325092523-45da7dee9335/pkg/filex/filex.go (about)

     1  package filex
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"errors"
     7  	"io"
     8  	"log"
     9  	"os"
    10  	"strings"
    11  
    12  	"github.com/bingoohuang/gg/pkg/iox"
    13  )
    14  
    15  // LinesChan read file into lines.
    16  func LinesChan(filePath string, chSize int) (ch chan string, err error) {
    17  	f, err := os.Open(filePath)
    18  	if err != nil {
    19  		return nil, err
    20  	}
    21  
    22  	s := bufio.NewScanner(f)
    23  	s.Split(ScanLines)
    24  	ch = make(chan string, chSize)
    25  	go func() {
    26  		defer iox.Close(f)
    27  		defer close(ch)
    28  
    29  		for s.Scan() {
    30  			t := s.Text()
    31  			t = strings.TrimSpace(t)
    32  			if len(t) > 0 {
    33  				ch <- t
    34  			}
    35  		}
    36  
    37  		if err := s.Err(); err != nil {
    38  			log.Printf("E! scan file %s lines  error: %v", filePath, err)
    39  		}
    40  	}()
    41  
    42  	return ch, nil
    43  }
    44  
    45  // ScanLines is a split function for a Scanner that returns each line of
    46  // text, with end-of-line marker. The returned line may
    47  // be empty. The end-of-line marker is one optional carriage return followed
    48  // by one mandatory newline. In regular expression notation, it is `\r?\n`.
    49  // The last non-empty line of input will be returned even if it has no
    50  // newline.
    51  func ScanLines(data []byte, atEOF bool) (advance int, token []byte, err error) {
    52  	if atEOF && len(data) == 0 {
    53  		return 0, nil, nil
    54  	}
    55  	if i := bytes.IndexByte(data, '\n'); i >= 0 {
    56  		// We have a full newline-terminated line.
    57  		return i + 1, data[0 : i+1], nil
    58  	}
    59  	// If we're at EOF, we have a final, non-terminated line. Return it.
    60  	if atEOF {
    61  		return len(data), data, nil
    62  	}
    63  	// Request more data.
    64  	return 0, nil, nil
    65  }
    66  
    67  // Lines read file into lines.
    68  func Lines(filePath string) (lines []string, err error) {
    69  	f, err := os.Open(filePath)
    70  	if err != nil {
    71  		return nil, err
    72  	}
    73  	defer f.Close()
    74  
    75  	s := bufio.NewScanner(f)
    76  	for s.Scan() {
    77  		lines = append(lines, s.Text())
    78  	}
    79  
    80  	return lines, s.Err()
    81  }
    82  
    83  // Open opens file successfully or panic.
    84  func Open(f string) *os.File {
    85  	r, err := os.Open(f)
    86  	if err != nil {
    87  		panic(err)
    88  	}
    89  
    90  	return r
    91  }
    92  
    93  type AppendOptions struct {
    94  	BackOffset int64
    95  }
    96  
    97  type AppendOptionsFn func(*AppendOptions)
    98  
    99  func WithBackOffset(backOffset int64) AppendOptionsFn {
   100  	return func(o *AppendOptions) {
   101  		o.BackOffset = backOffset
   102  	}
   103  }
   104  
   105  func Append(name string, data []byte, options ...AppendOptionsFn) (int, error) {
   106  	option := &AppendOptions{}
   107  	for _, fn := range options {
   108  		fn(option)
   109  	}
   110  
   111  	// If the file doesn't exist, create it, or append to the file
   112  
   113  	f, err := os.OpenFile(name, os.O_CREATE|os.O_WRONLY, 0o644)
   114  	if err != nil {
   115  		return 0, err
   116  	}
   117  	defer f.Close()
   118  
   119  	if _, err := f.Seek(option.BackOffset, io.SeekEnd); err != nil {
   120  		return 0, err
   121  	}
   122  
   123  	n, err := f.Write(data)
   124  	if err != nil {
   125  		return n, err
   126  	}
   127  
   128  	return n, nil
   129  }
   130  
   131  func Exists(name string) bool {
   132  	ok, _ := ExistsErr(name)
   133  	return ok
   134  }
   135  
   136  func ExistsErr(name string) (bool, error) {
   137  	if _, err := os.Stat(name); err == nil {
   138  		return true, nil
   139  	} else if errors.Is(err, os.ErrNotExist) {
   140  		return false, nil
   141  	} else {
   142  		// Schrodinger: file may or may not exist. See err for details.
   143  		// Therefore, do *NOT* use !os.IsNotExist(err) to test for file existence
   144  		return false, err
   145  	}
   146  }