read.go 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. package iox
  2. import (
  3. "bufio"
  4. "bytes"
  5. "io"
  6. "os"
  7. "strings"
  8. )
  9. type (
  10. textReadOptions struct {
  11. keepSpace bool
  12. withoutBlanks bool
  13. omitPrefix string
  14. }
  15. // TextReadOption defines the method to customize the text reading functions.
  16. TextReadOption func(*textReadOptions)
  17. )
  18. // DupReadCloser returns two io.ReadCloser that read from the first will be written to the second.
  19. // The first returned reader needs to be read first, because the content
  20. // read from it will be written to the underlying buffer of the second reader.
  21. func DupReadCloser(reader io.ReadCloser) (io.ReadCloser, io.ReadCloser) {
  22. var buf bytes.Buffer
  23. tee := io.TeeReader(reader, &buf)
  24. return io.NopCloser(tee), io.NopCloser(&buf)
  25. }
  26. // KeepSpace customizes the reading functions to keep leading and tailing spaces.
  27. func KeepSpace() TextReadOption {
  28. return func(o *textReadOptions) {
  29. o.keepSpace = true
  30. }
  31. }
  32. // ReadBytes reads exactly the bytes with the length of len(buf)
  33. func ReadBytes(reader io.Reader, buf []byte) error {
  34. var got int
  35. for got < len(buf) {
  36. n, err := reader.Read(buf[got:])
  37. if err != nil {
  38. return err
  39. }
  40. got += n
  41. }
  42. return nil
  43. }
  44. // ReadText reads content from the given file with leading and tailing spaces trimmed.
  45. func ReadText(filename string) (string, error) {
  46. content, err := os.ReadFile(filename)
  47. if err != nil {
  48. return "", err
  49. }
  50. return strings.TrimSpace(string(content)), nil
  51. }
  52. // ReadTextLines reads the text lines from given file.
  53. func ReadTextLines(filename string, opts ...TextReadOption) ([]string, error) {
  54. var readOpts textReadOptions
  55. for _, opt := range opts {
  56. opt(&readOpts)
  57. }
  58. file, err := os.Open(filename)
  59. if err != nil {
  60. return nil, err
  61. }
  62. defer file.Close()
  63. var lines []string
  64. scanner := bufio.NewScanner(file)
  65. for scanner.Scan() {
  66. line := scanner.Text()
  67. if !readOpts.keepSpace {
  68. line = strings.TrimSpace(line)
  69. }
  70. if readOpts.withoutBlanks && len(line) == 0 {
  71. continue
  72. }
  73. if len(readOpts.omitPrefix) > 0 && strings.HasPrefix(line, readOpts.omitPrefix) {
  74. continue
  75. }
  76. lines = append(lines, line)
  77. }
  78. return lines, scanner.Err()
  79. }
  80. // WithoutBlank customizes the reading functions to ignore blank lines.
  81. func WithoutBlank() TextReadOption {
  82. return func(o *textReadOptions) {
  83. o.withoutBlanks = true
  84. }
  85. }
  86. // OmitWithPrefix customizes the reading functions to ignore the lines with given leading prefix.
  87. func OmitWithPrefix(prefix string) TextReadOption {
  88. return func(o *textReadOptions) {
  89. o.omitPrefix = prefix
  90. }
  91. }