| // Copyright 2024 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package strings |
| |
| import ( |
| "iter" |
| "unicode" |
| "unicode/utf8" |
| ) |
| |
| // Lines returns an iterator over the newline-terminated lines in the string s. |
| // The lines yielded by the iterator include their terminating newlines. |
| // If s is empty, the iterator yields no lines at all. |
| // If s does not end in a newline, the final yielded line will not end in a newline. |
| // It returns a single-use iterator. |
| func Lines(s string) iter.Seq[string] { |
| return func(yield func(string) bool) { |
| for len(s) > 0 { |
| var line string |
| if i := IndexByte(s, '\n'); i >= 0 { |
| line, s = s[:i+1], s[i+1:] |
| } else { |
| line, s = s, "" |
| } |
| if !yield(line) { |
| return |
| } |
| } |
| } |
| } |
| |
| // splitSeq is SplitSeq or SplitAfterSeq, configured by how many |
| // bytes of sep to include in the results (none or all). |
| func splitSeq(s, sep string, sepSave int) iter.Seq[string] { |
| return func(yield func(string) bool) { |
| if len(sep) == 0 { |
| for len(s) > 0 { |
| _, size := utf8.DecodeRuneInString(s) |
| if !yield(s[:size]) { |
| return |
| } |
| s = s[size:] |
| } |
| return |
| } |
| for { |
| i := Index(s, sep) |
| if i < 0 { |
| break |
| } |
| frag := s[:i+sepSave] |
| if !yield(frag) { |
| return |
| } |
| s = s[i+len(sep):] |
| } |
| yield(s) |
| } |
| } |
| |
| // SplitSeq returns an iterator over all substrings of s separated by sep. |
| // The iterator yields the same strings that would be returned by [Split](s, sep), |
| // but without constructing the slice. |
| // It returns a single-use iterator. |
| func SplitSeq(s, sep string) iter.Seq[string] { |
| return splitSeq(s, sep, 0) |
| } |
| |
| // SplitAfterSeq returns an iterator over substrings of s split after each instance of sep. |
| // The iterator yields the same strings that would be returned by [SplitAfter](s, sep), |
| // but without constructing the slice. |
| // It returns a single-use iterator. |
| func SplitAfterSeq(s, sep string) iter.Seq[string] { |
| return splitSeq(s, sep, len(sep)) |
| } |
| |
| // FieldsSeq returns an iterator over substrings of s split around runs of |
| // whitespace characters, as defined by [unicode.IsSpace]. |
| // The iterator yields the same strings that would be returned by [Fields](s), |
| // but without constructing the slice. |
| func FieldsSeq(s string) iter.Seq[string] { |
| return func(yield func(string) bool) { |
| start := -1 |
| for i := 0; i < len(s); { |
| size := 1 |
| r := rune(s[i]) |
| isSpace := asciiSpace[s[i]] != 0 |
| if r >= utf8.RuneSelf { |
| r, size = utf8.DecodeRuneInString(s[i:]) |
| isSpace = unicode.IsSpace(r) |
| } |
| if isSpace { |
| if start >= 0 { |
| if !yield(s[start:i]) { |
| return |
| } |
| start = -1 |
| } |
| } else if start < 0 { |
| start = i |
| } |
| i += size |
| } |
| if start >= 0 { |
| yield(s[start:]) |
| } |
| } |
| } |
| |
| // FieldsFuncSeq returns an iterator over substrings of s split around runs of |
| // Unicode code points satisfying f(c). |
| // The iterator yields the same strings that would be returned by [FieldsFunc](s), |
| // but without constructing the slice. |
| func FieldsFuncSeq(s string, f func(rune) bool) iter.Seq[string] { |
| return func(yield func(string) bool) { |
| start := -1 |
| for i := 0; i < len(s); { |
| r, size := utf8.DecodeRuneInString(s[i:]) |
| if f(r) { |
| if start >= 0 { |
| if !yield(s[start:i]) { |
| return |
| } |
| start = -1 |
| } |
| } else if start < 0 { |
| start = i |
| } |
| i += size |
| } |
| if start >= 0 { |
| yield(s[start:]) |
| } |
| } |
| } |