What is the difference between strings.Split(s, " ") and strings.Fields(s) when splitting a line into words?
answer
- one separator, or a run of them
- empty fields survive only one of them
- unicode.IsSpace defines the other
- Split counts separators; Fields counts words
basics
~20 sstrings.Split cuts at every single separator, so a run of two spaces produces an empty string between them. strings.Fields treats any run of one or more Unicode whitespace characters as one separator and never returns an empty element.
solid answer
~40 s`strings.Split(s, " ")` cuts at every single space: two spaces in a row yield an empty element between them, and a leading or trailing space yields an empty element at the edge. The result length is always the number of separators found plus one, so `strings.Split("a b", " ")` has three elements. `strings.Fields(s)` treats a run of one or more whitespace characters (anything `unicode.IsSpace` accepts — space, tab, newline) as a single separator, ignores leading and trailing whitespace, and never returns an empty element, so `strings.Fields("a b")` has two. Use `Fields` for human-formatted, whitespace-aligned text; use `Split` when the separator is structural and an empty field carries meaning, as in a comma-separated row where `a,,b` really does contain an empty middle value.
code
go · 5 linesline := "a b"
fmt.Println(len(strings.Split(line, " "))) // 3: "a", "", "b"
fmt.Println(len(strings.Fields(line))) // 2: "a", "b"
fmt.Println(len(strings.Fields(" "))) // 0
fmt.Println(len(strings.Split("", ","))) // 1: a single empty stringgo deeper
Be ready to state the rule in one line: Split keeps empty fields between adjacent separators, Fields collapses whitespace runs and never returns an empty element. Have an example ready for each.
An interviewer expects the invariant — with a non-empty separator, Split returns one more element than separators found — and the two edge cases: an empty input string, and a separator that never appears.
Show that you pick by asking what an empty field means in the format. Structural delimiters keep empties because dropping one shifts every later field; presentational whitespace does not.
Own the convention in shared parsing code: a house rule that delimited records use Split with an explicit field-count check, while free text uses Fields, removes a whole class of silent off-by-one bugs from review.
## The two functions ```go func Split(s, sep string) []string func Fields(s string) []string ``` Both return a `[]string`, and both are in the `strings` package (with byte-slice mirrors `bytes.Split` and `bytes.Fields`). They answer different questions, and confusing them is one of the most common early Go parsing bugs. ## strings.Split: cut at every separator `strings.Split(s, sep)` finds every non-overlapping occurrence of `sep` and returns the text between the occurrences. Crucially, **it never collapses anything**. If two separators are adjacent, the text between them is the empty string, and that empty string is a real element of the result. ```go strings.Split("a,b,c", ",") // ["a" "b" "c"] — 2 separators, 3 elements strings.Split("a,,b", ",") // ["a" "" "b"] — 2 separators, 3 elements strings.Split(",a", ",") // ["" "a"] ``` The invariant worth memorising: **len(result) == (number of occurrences of sep) + 1**, always, when `sep` is not empty. Two consequences follow directly. - If `s` does not contain `sep` at all, you get a slice of length 1 whose single element is the whole of `s`. Split never reports "not found"; you have to check `len(result)` yourself. - If `s` is the empty string and `sep` is not, you still get a slice of length 1 containing one empty string — **not** an empty slice. Code that writes `for _, f := range strings.Split(line, ",")` on an empty line therefore runs its body once with an empty field, which is a classic off-by-one in a parser. There is one special case: an **empty separator**. `strings.Split(s, "")` splits after each UTF-8 rune sequence, so `strings.Split("héllo", "")` gives one element per rune. That is a distinct behaviour, not a degenerate one. ## strings.Fields: split on runs of whitespace `strings.Fields(s)` is not parameterised at all. It splits `s` around each run of **one or more** consecutive whitespace characters, where whitespace is whatever `unicode.IsSpace` reports — space, tab, newline, carriage return, vertical tab, form feed, and the Unicode space characters. Because a run counts as one separator and leading and trailing runs are simply skipped, the result never contains an empty element: ```go strings.Fields(" go is fun ") // ["go" "is" "fun"] strings.Fields(" ") // [] — length 0 strings.Fields("") // [] — length 0 ``` So `Fields` is the tokeniser you want for text a human typed or that a tool aligned into columns, where the amount of whitespace is presentational and carries no information. ## Choosing between them Ask what an *empty field* means in your input. - In a delimited record — a comma-separated row, a `PATH`-style list, a colon-separated account line — an empty field is data. `a::b` has a genuine empty middle entry, and dropping it silently shifts every later field by one position. Use `Split`. - In free-form text — a log line, a command a user typed, a whitespace-aligned table — runs of spaces are formatting. Use `Fields`. A parser for a message-broker handshake shows both in one file: the frame is a sequence of header lines, and the *lines* are structural (a blank line terminates the header block, so you must not collapse it), while a value that is a whitespace-separated list of supported versions is naturally tokenised with `Fields`. ## When neither fits: FieldsFunc If you want the collapsing, empty-free behaviour of `Fields` but with your own definition of a separator, use `strings.FieldsFunc(s, f)`, where `f` is a `func(rune) bool` that reports whether a rune separates fields. It splits at each run of separator runes and, like `Fields`, returns an empty slice when there is nothing else. The documentation notes that it makes no guarantees about the order in which it calls `f`, and assumes `f` returns the same answer for the same rune every time — so `f` must be a pure predicate, not something with state. ```go isComma := func(r rune) bool { return r == ',' } strings.FieldsFunc("a,,b", isComma) // ["a" "b"] — runs collapsed strings.Split("a,,b", ",") // ["a" "" "b"] — run preserved ``` That pair is the clearest statement of the whole distinction: same input, same separator, different answer, and only you know which one your format means. ## Cost Both functions allocate one `[]string` to hold the result. The substrings themselves are **not** copies: a Go string is immutable, so each element is a view onto the same underlying bytes as `s`. That makes both cheap in bytes but means retaining one small field keeps the whole original string alive.
- How do you get the collapsing behaviour of strings.Fields but with a separator other than whitespace?Use `strings.FieldsFunc(s, f)` with a `func(rune) bool` predicate. It splits at each run of runes for which `f` returns true and, like `Fields`, drops leading, trailing and repeated separators, so `strings.FieldsFunc("a,,b", isComma)` gives two elements where `strings.Split("a,,b", ",")` gives three. The predicate must be pure — the order of calls to `f` is unspecified.
- What does strings.Split return when the separator is the empty string?It splits after each UTF-8 rune sequence, giving one element per rune rather than per byte. So `strings.Split("héllo", "")` returns five elements even though the string is six bytes long. It is a documented special case, not an error, and it is the only form of `Split` whose element count is not the separator count plus one.
- You are parsing frames as []byte from a network connection. What changes?Use the `bytes` mirrors — `bytes.Split`, `bytes.Fields`, `bytes.Cut`, `bytes.Index`, `bytes.HasPrefix` — with the same semantics, so you never convert to `string` just to search. One difference matters: the returned `[][]byte` elements alias the input buffer and are writable, so mutating one sub-slice is visible through the original.
Split is a knife that cuts wherever it sees the mark, even twice in a row. Fields is a reader picking out words and ignoring however much blank paper sits between them.
saying these in an interview costs you the question
- Claims strings.Split collapses runs of separators
- Thinks strings.Fields can be given a custom separator
- Expects strings.Split("", ",") to return an empty slice
- Assumes strings.Fields only splits on the space character
- Believes the substrings are freshly copied bytes