read.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  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. // LimitDupReadCloser returns two io.ReadCloser that read from the first will be written to the second.
  33. // But the second io.ReadCloser is limited to up to n bytes.
  34. // The first returned reader needs to be read first, because the content
  35. // read from it will be written to the underlying buffer of the second reader.
  36. func LimitDupReadCloser(reader io.ReadCloser, n int64) (io.ReadCloser, io.ReadCloser) {
  37. var buf bytes.Buffer
  38. tee := LimitTeeReader(reader, &buf, n)
  39. return io.NopCloser(tee), io.NopCloser(&buf)
  40. }
  41. // ReadBytes reads exactly the bytes with the length of len(buf)
  42. func ReadBytes(reader io.Reader, buf []byte) error {
  43. var got int
  44. for got < len(buf) {
  45. n, err := reader.Read(buf[got:])
  46. if err != nil {
  47. return err
  48. }
  49. got += n
  50. }
  51. return nil
  52. }
  53. // ReadText reads content from the given file with leading and tailing spaces trimmed.
  54. func ReadText(filename string) (string, error) {
  55. content, err := os.ReadFile(filename)
  56. if err != nil {
  57. return "", err
  58. }
  59. return strings.TrimSpace(string(content)), nil
  60. }
  61. // ReadTextLines reads the text lines from given file.
  62. func ReadTextLines(filename string, opts ...TextReadOption) ([]string, error) {
  63. var readOpts textReadOptions
  64. for _, opt := range opts {
  65. opt(&readOpts)
  66. }
  67. file, err := os.Open(filename)
  68. if err != nil {
  69. return nil, err
  70. }
  71. defer file.Close()
  72. var lines []string
  73. scanner := bufio.NewScanner(file)
  74. for scanner.Scan() {
  75. line := scanner.Text()
  76. if !readOpts.keepSpace {
  77. line = strings.TrimSpace(line)
  78. }
  79. if readOpts.withoutBlanks && len(line) == 0 {
  80. continue
  81. }
  82. if len(readOpts.omitPrefix) > 0 && strings.HasPrefix(line, readOpts.omitPrefix) {
  83. continue
  84. }
  85. lines = append(lines, line)
  86. }
  87. return lines, scanner.Err()
  88. }
  89. // WithoutBlank customizes the reading functions to ignore blank lines.
  90. func WithoutBlank() TextReadOption {
  91. return func(o *textReadOptions) {
  92. o.withoutBlanks = true
  93. }
  94. }
  95. // OmitWithPrefix customizes the reading functions to ignore the lines with given leading prefix.
  96. func OmitWithPrefix(prefix string) TextReadOption {
  97. return func(o *textReadOptions) {
  98. o.omitPrefix = prefix
  99. }
  100. }