'Split a string only by the first element in golang

I'm trying to parse git branch names and split them so I can seperate the remote and the branch name

Previously I just split on the first slash:

func ParseBranchname(branchString string) (remote, branchname string) {
    branchArray := strings.Split(branchString, "/")
    remote = branchArray[0]
    branchname = branchArray[1]
    return
}

But I forgot that some folks use slashes in git branch names as well, multiple even!

Right now I'm taking the first element in the slice from the split, then moving every element one done and merging back on the slash:

func ParseBranchname(branchString string) (remote, branchname string) {
    branchArray := strings.Split(branchString, "/")
    remote = branchArray[0]

    copy(branchArray[0:], branchArray[0+1:])
    branchArray[len(branchArray)-1] = ""
    branchArray = branchArray[:len(branchArray)-1]

    branchname = strings.Join(branchArray, "/")
    return
}

Is there a cleaner way to do this?



Solution 1:[1]

For Go >= 1.18 see this answer.


For Go < 1.18:

Use strings.SplitN with n=2 to limit the result to two substrings.

func ParseBranchname(branchString string) (remote, branchname string) {
    branchArray := strings.SplitN(branchString, "/", 2)
    remote = branchArray[0]
    branchname = branchArray[1]
    return
}

Solution 2:[2]

Go 1.18

Use strings.Cut:

[This function] slices s around the first instance of sep, returning the text before and after sep. The found result reports whether sep appears in s. If sep does not appear in s, cut returns s, "", false.

func ParseBranchname(branchString string) (remote, branchname string) {
    remote, branchname, _ = strings.Cut(branchString, "/")
    return
}

(Note that this code snippet is ignoring the third return value, a boolean, that tells whether the separator was found in the input string or not.)

As mentioned in Go 1.18 release notes, Cut "can replace and simplify many common uses of Index, IndexByte, IndexRune, and SplitN". In particular, SplitN with n=2.

Playground — originally posted by @mkopriva in a comment — modified to include an example with Cut: https://go.dev/play/p/bjBhnr3Hg5O

Solution 3:[3]

Use strings.Index to find the index of the first / and you split manually with that information:

func ParseBranchnameNew(branchString string) (remote, branchName string) {
        firstSlash := strings.Index(branchString, "/")
        remote = branchString[:firstSlash]
        branchName = branchString[firstSlash+1:]
        return
}

Comparing to your original code:

goos: linux
goarch: amd64
BenchmarkParseBranchname-12         10000000           131 ns/op
BenchmarkParseBranchnameNew-12      300000000            5.56 ns/op
PASS

Solution 4:[4]

One more way could be:

branchArray := strings.Split(branchString, "/")
branchArray = []string{branchArray[0], strings.Join(branchArray[1:], "/")}

remote = branchArray[0]
branchname = branchArray[1]

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 blackgreen
Solution 3 cd1
Solution 4 Amit Upadhyay