// 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 } } return } } // explodeSeq returns an iterator over the runes in s. func explodeSeq(s string) iter.Seq[string] { return func(yield func(string) bool) { for len(s) > 0 { _, size := utf8.DecodeRuneInString(s) if !yield(s[:size]) { return } s = s[size:] } } } // 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] { if len(sep) == 0 { return explodeSeq(s) } return func(yield func(string) bool) { 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); { size := 1 r := rune(s[i]) if r >= utf8.RuneSelf { 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:]) } } }