read.go 2.5 KB

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