rubyguides

String#split with Delimiters and Limits

split(pattern = $;, limit = nil)

#split divides a string into an array of substrings based on a delimiter, returning the result. It is one of the most commonly used string methods in Ruby — essential for parsing CSV data, processing log lines, tokenising natural language, and transforming delimited text into structured data.

Basic syntax

str.split(pattern = $;, limit = nil)
ArgumentDescription
patternDelimiter to split on — a string, regex, or nil (splits on whitespace)
limitMaximum number of splits (controls array length)

When pattern is nil, split treats consecutive whitespace as a single delimiter and removes leading and trailing whitespace from the result. When pattern is an empty string (""), the string is split into individual characters.

Splitting on a string delimiter

"apple,banana,cherry".split(",")
# => ["apple", "banana", "cherry"]

"one::two::three".split("::")
# => ["one", "two", "three"]

"hello world".split(" ")
# => ["hello", "world"]

The delimiter can be any string, not just single characters. When the delimiter appears multiple times in the source string, every occurrence triggers a split. This means consecutive delimiters produce empty strings in the result, which can be useful or surprising depending on what you expect. For CSV-style data where empty fields have meaning, preserving those blanks with a -1 limit is often the right approach.

Splitting on whitespace (nil pattern)

"foo bar baz".split
# => ["foo", "bar", "baz"]

"  foo   bar\nbaz  ".split
# => ["foo", "bar", "baz"]

# Leading and trailing whitespace is trimmed
"   one   two   ".split
# => ["one", "two"]

Using nil as the pattern is the idiomatic way to split on any whitespace without having to call strip first. The nil-pattern mode also collapses consecutive whitespace characters into a single split point, which makes it ideal for cleaning up ragged text input. This behavior differs from splitting on a literal space character, where multiple spaces would produce empty array elements.

Splitting into individual characters (empty string pattern)

"hello".split("")
# => ["h", "e", "l", "l", "o"]

"12345".split("")
# => ["1", "2", "3", "4", "5"]

An empty string as the delimiter splits between every character. The inverse operation is "hello".chars or "hello".chars.join. Splitting into characters is useful for tasks like counting specific letters, checking for repeated characters, or transforming each character individually. The resulting array of single-character strings can then be mapped or filtered with any Enumerable method.

Using a regex pattern

"foo bar baz".split(/\s+/)
# => ["foo", "bar", "baz"]

"apple1banana2cherry".split(/\d+/)
# => ["apple", "banana", "cherry"]

"one,two;three:four".split(/[,;:]/)
# => ["one", "two", "three", "four"]

Using a regex as the pattern gives you flexible delimiters — multiple characters, character classes, optional whitespace, and so on. The regex engine handles the matching, which means you can use anchors, lookahead/lookbehind, and character classes to define precisely where splits should occur. This is far more expressive than a plain string delimiter, especially when the input format varies slightly between records. Capturing groups in the regex are included in the result array:

"hello world".split(/(\s+)/)
# => ["hello", " ", "world"]

When the regex contains a capturing group, each delimiter match inserts the captured text into the result array, interleaving delimiters with the split substrings. This is handy when you need to reconstruct the original string or when the separator itself conveys meaning. To suppress the captured group from appearing in the output, use a non-capturing group with (?:...) syntax instead.

The limit parameter

The limit argument controls how many elements the returned array has:

"a:b:c:d".split(":", 2)
# => ["a", "b:c:d"]

"a:b:c:d".split(":", 3)
# => ["a", "b", "c:d"]

"a:b:c:d".split(":", 4)
# => ["a", "b", "c", "d"]

# limit = 0 means no limit (same as nil)
"a:b:c:d".split(":", 0)
# => ["a", "b", "c", "d"]

When limit is greater than 1, the array contains at most limit - 1 splits, and the last element contains the remainder of the string. When limit = 0, it behaves as if limit was nil (no limit), but trailing empty strings are removed.

Trailing empty strings

Without a limit, the default behavior discards trailing empty strings, which keeps output tidy for quick text processing but can mask the presence of trailing delimiters. This is the behavior you get with split called without arguments, or with limit = 0.

"one::two::".split("::")
# => ["one", "two"]

# With limit = -1, trailing empties are preserved
"one::two::".split("::", -1)
# => ["one", "two", ""]

Passing -1 as the limit behaves like nil (no limit) but preserves trailing empty strings. This is the go-to choice when every position in the output array matters, such as in CSV parsing where column alignment depends on counting array indices correctly.

Preserving empty strings

To always include empty strings (for example, parsing fixed-width fields):

"a,,b,,c".split(",", -1)
# => ["a", "", "b", "", "c"]

# vs default behaviour
"a,,b,,c".split(",")
# => ["a", "", "b", "", "c"]  — trailing empty removed

Passing -1 as the limit preserves all empty strings. This keeps every field position intact even when the data contains consecutive delimiters. For tabular data where empty cells carry meaning, the -1 flag is the only way to avoid silently dropping columns.

Splitting lines

For line-by-line splitting, use String#lines or String#split with "\n":

text = "line one\nline two\nline three"
text.split("\n")
# => ["line one", "line two", "line three"]

# With portable line endings (cross-platform)
text.split(/\r?\n/)
# => ["line one", "line two", "line three"]

split("\n") treats \n as a delimiter — note that trailing \n produces a trailing empty string. lines preserves the line endings as part of each element.

Inverse operation

Use Array#join to recombine. The split/join pair is one of the most common string processing patterns in Ruby: split to manipulate individual components, then join to reassemble the result. This two-step dance appears in URL parsing, template rendering, and data transformation pipelines of all sizes.

parts = "apple,banana,cherry".split(",")
parts.join(", ")
# => "apple, banana, cherry"

["one", "two", "three"].join(" -> ")
# => "one -> two -> three"

See Also