Skip to content

proposal: strings: add Lines #51039

Description

@icholy

If I already have a string in memory, there's no simple and efficient way of splitting it into lines.

The naïve approach won't handle \r\n.

strings.Split(s, "\n")

Using a bufio.Scanner works but it creates copies instead of slicing the existing string.

var lines []string
sc := bufio.NewScanner(strings.NewReader(s))
for sc.Scan() {
    lines = append(lines, sc.Text())
}

I'd like a new strings.Lines function similar to strings.Fields for this use-case.

Activity

  1. added this to the Proposal milestone on Feb 6, 2022
  2. icholy commented on Feb 6, 2022

    @icholy
    ContributorAuthor

    Here are some other approaches I've seen while searching:

    strings.Split(strings.ReplaceAll(s, "\r\n", "\n"), "\n")
    regexp.MustCompile("\r?\n").Split(s, -1)
  3. ALTree commented on Feb 6, 2022

    @ALTree
    Member

    Assuming a "line" is anything delimited by one of \n, \r, and \r\n, calling strings.FieldsFunc with func(c rune) bool { return c == '\n' || c == '\r' } seems to work.

  4. icholy commented on Feb 6, 2022

    @icholy
    ContributorAuthor

    @ALTree thanks, I'll be using that. However, it doesn't give the correct output for strings with blank lines like "\n\n\n"

  5. ALTree commented on Feb 6, 2022

    @ALTree
    Member

    Oh, right. It's actually just Fields. You want something that returns 4 strings for "text\n\n\ntext`, two of which are empty, which is different from what Fields does.

  6. ALTree commented on Feb 6, 2022

    @ALTree
    Member

    What about adding strings.SplitFunc instead? That would mirror strings.FieldsFunc and let you split on a custom function.

  7. icholy commented on Feb 6, 2022

    @icholy
    ContributorAuthor

    What would the signature of the callback look like? Something like func(rune) bool wouldn't be able to handle the \r\n case.

  8. ALTree commented on Feb 6, 2022

    @ALTree
    Member

    EDIT: right, that's probably why it doesn't exist. The API is less obvious. You'd need to check substrings instead of the single runes.

  9. icholy commented on Feb 6, 2022

    @icholy
    ContributorAuthor

    If something like SplitMulti was added, then you'd also need all the different variations. That gets messy.

    func SplitMulti(s, seps []string) []string
    func SplitMultiAfter(s, seps []string) []string
    func SplitMultiAfterN(s, seps []string, n int) []string
    func SplitMultiN(s, seps []string, n int) []string
  10. changed the title [-]proposal: strings: add Lines func[/-] [+]strings: add Lines[/+] on Feb 15, 2022
  11. changed the title [-]strings: add Lines[/-] [+]proposal: strings: add Lines[/+] on Feb 16, 2022
  12. rsc commented on Feb 16, 2022

    @rsc
    Contributor

    Cut works great.

    for text != "" {
       line, rest, ok := strings.Cut(text, "\n")
       text = rest
       line = strings.TrimSuffix(line, "\r") // if you care
       ... use line ...
    }
    
  13. rsc commented on Feb 16, 2022

    @rsc
    Contributor

    For that matter, strings.SplitAfter(text, "\n") also works.
    There's an extra empty string at the end of the slice if the text ends in a final \n (as most do).

  14. rsc commented on Feb 16, 2022

    @rsc
    Contributor

    This proposal has been added to the active column of the proposals project
    and will now be reviewed at the weekly proposal review meetings.
    — rsc for the proposal review group

  15. 3 remaining items

  16. jimmyfrasche commented on Feb 16, 2022

    @jimmyfrasche
    Member

    Since you have that, how are you ending up with a string in memory that may have windows line endings?

  17. icholy commented on Feb 16, 2022

    @icholy
    ContributorAuthor

    @jimmyfrasche Say you're working with an AST which exposes comments as multiline strings.

  18. rsc commented on Feb 23, 2022

    @rsc
    Contributor

    It sounds like (1) the API is somewhat inefficient since it has to allocate the slice, (2) there are other ways to write the code that are more efficient and just as clean using existing standard library APIs, and (3) this could be in an external library easily instead if the exact operation of 'return a slice of all the lines' is required.

    In that case, it seems like this is heading toward likely decline. Do I have that right?

  19. icholy commented on Feb 23, 2022

    @icholy
    ContributorAuthor

    (1) the API is somewhat inefficient since it has to allocate the slice,

    Given the target use-cases, this would be negligible.

    (2) there are other ways to write the code that are more efficient and just as clean using existing standard library APIs

    I'm not sure about "just as clean". The proposed strings.Lines api is more ergonomic than other solutions.

    (3) this could be in an external library easily instead if the exact operation of 'return a slice of all the lines' is required.

    ofc

    In that case, it seems like this is heading toward likely decline. Do I have that right?

    Ya, I think so. The potential for abuse makes it not worth it IMO.

  20. rsc commented on Mar 2, 2022

    @rsc
    Contributor

    Given the target use-cases, this would be negligible.

    The problem with putting things in the standard library is that it will get used for many uses cases beyond the target. Things in the standard library have to be particularly robust against unexpected uses.

  21. rsc commented on Mar 2, 2022

    @rsc
    Contributor

    Based on the discussion above, this proposal seems like a likely decline.
    — rsc for the proposal review group

  22. rsc commented on Mar 9, 2022

    @rsc
    Contributor

    No change in consensus, so declined.
    — rsc for the proposal review group

  23. moved this to Declined in Proposalson Aug 10, 2022
  24. locked and limited conversation to collaborators on Mar 9, 2023
  25. removed this from Proposalson Mar 15, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions