Skip to content

split has sharp edges

Level: 101 · you will hit this in your first week

One line: split always takes a pattern — split ".", "1.2.3" splits on every character and returns nothing — it drops trailing empty fields unless the limit is -1, returns no fields at all for an empty string, treats a single space ' ' as "runs of whitespace", returns captured separators as fields, and quietly limits itself to the number of variables you assign to.

Measured

Verified output of split_pl.pl — regenerated by tools/run_examples.py, never hand-typed.

--- empty fields ---
split /,/, "a,b,,"          2: ("a", "b")
split /,/, "a,b,,", -1      4: ("a", "b", "", "")
split /,/, ",a,b"           3: ("", "a", "b")
split /,/, ""               0: ()

--- the first argument is a pattern ---
split ".", "1.2.3"          0: ()
split /\./, "1.2.3"         3: ("1", "2", "3")
split $sep, "a|b"           3: ("a", "|", "b")   $sep is "|"
split /\Q$sep\E/, "a|b"     2: ("a", "b")

--- one space is special ---
split ' ', "  two  words "   2: ("two", "words")
split / /, "  two  words "   5: ("", "", "two", "", "words")
split /\s+/, "  two  words " 3: ("", "two", "words")

--- groups in the pattern come back as fields ---
split /(,)/, "a,b"          3: ("a", ",", "b")
split /(-)|(,)/, "a-b,c"    7: ("a", "-", undef, "b", undef, ",", "c")

--- the number of fields you asked for ---
my ($key, $value) = split /=/, 'next=/search?q=perl'      $value is "/search?q"
my ($key, $value) = split /=/, 'next=/search?q=perl', 2   $value is "/search?q=perl"
my $count = () = split /,/, "a,b,c,d"                    $count is 1
my $count = split /,/, "a,b,c,d"                         $count is 4

Empty fields

  • At the end they vanish. "a,b,," is four fields, the last two empty; split returns two. A row whose last columns are empty becomes a shorter array. The limit -1 keeps them.
  • At the start they stay. ",a,b" gives an empty first field.
  • An empty string gives no fields — an empty list, not one empty field.

The first argument is a pattern

A string is compiled as a regex, so split "." splits on any character: every field is empty, and then rule one deletes them all. split /\./ is the literal dot. The same trap with a variable is worse, because nothing looks wrong: $sep = '|' is a pattern of two empty alternatives, which matches between every character. \Q...\E quotes a variable's metacharacters.

One space is special

split ' ' — a string holding exactly one space — splits on runs of whitespace and skips leading whitespace. It is what -a uses in a one-liner. split / / splits on each single space and keeps every empty field between them, and split /\s+/ still leaves an empty first field for the leading spaces.

Groups come back

Every capture group in the pattern is returned as a field between the pieces. With two groups of which only one matches, the other is returned as undef.

The limit you did not ask for

When split is assigned to a list of variables, perl gives it a limit of one more than the number of variables. my ($key, $value) = split /=/, 'next=/search?q=perl' splits into at most three fields — next, /search?q, perl — and throws the third away, so $value has lost its =perl. A limit of 2 keeps the rest together.

The same rule makes my $count = () = split ... return 1: an empty list is zero variables, so the limit is one, and one field is the whole string. split in scalar context counts correctly.

What to do instead

my @fields = split /,/, $line, -1;        # a separator you chose: keep every field
my @words  = split ' ', $text;             # words
my ($key, $value) = split /=/, $pair, 2;   # a key, and all the rest
my @parts  = split /\Q$sep\E/, $s, -1;     # a separator held in a variable

For CSV, use none of these — see CSV is not split.

If you are coming from another language

  • Java. String.split copied the trailing-field rule and the regex argument, but not the empty string: "".split(",") has length 1 in Java, where Perl returns nothing. See split has sharp edges ↗.
  • Rust. str::split always returns n + 1 pieces for n matches, keeping every empty one; Splitting on nothing ↗ and Walking a String cover the rest.
  • Python. str.split(',') keeps every field and takes a literal string, not a pattern; str.split() with no argument is split ' '.

See also