Python

Python: Split a String into a List (split, re.split, csv, shlex)

Python · Published 2026-10-24

A ball of orange wool with a loose strand on a striped blanket
A ball of orange wool, one loose strand trailing across a striped blanket.

Call text.split() to split a string into a list of words on any run of whitespace, or text.split(",") to split on an exact separator. For several different delimiters use re.split(), for individual characters use list(text), and for quoted fields use the csv or shlex modules.

All examples were run on Python 3.13; the behaviour shown has been stable throughout Python 3.

str.split() with and without a separator

str.split(sep=None, maxsplit=-1) has two distinct modes depending on whether you pass sep.

line = "  alpha beta\tgamma\n delta  "
print(line.split())

csv_row = "red,green,,blue"
print(csv_row.split(","))

print("a--b--c".split("--"))
['alpha', 'beta', 'gamma', 'delta']
['red', 'green', '', 'blue']
['a', 'b', 'c']

split() versus split(" ")

This difference catches people regularly:

s = "a  b c "
print(s.split())
print(s.split(" "))
print("".split())
print("".split(","))
print("   ".split())
['a', 'b', 'c']
['a', '', 'b', 'c', '']
[]
['']
[]

split(" ") treats every single space as a separator, so double spaces and a trailing space create empty items, and tabs are not split at all. Splitting an empty string with an explicit separator returns [''], a one-element list, not an empty one. If you are splitting human-entered text into words, you almost always want the no-argument form. Passing an empty separator, "abc".split(""), raises ValueError: empty separator.

Limiting splits: maxsplit, rsplit and partition

maxsplit caps the number of splits, leaving the remainder intact. rsplit does the same counting from the right.

log = "2026-10-24 12:00:01 ERROR disk full: /var is at 98%"
date, time, level, message = log.split(maxsplit=3)
print(level, "|", message)

path = "/srv/www/site/static/app.min.js"
print(path.rsplit("/", 1))
print("app.min.js".rsplit(".", 1))
print("key=value=more".split("=", 1))
print("a b c".partition(" "))
ERROR | disk full: /var is at 98%
['/srv/www/site/static', 'app.min.js']
['app.min', 'js']
['key', 'value=more']
('a', ' ', 'b c')

Without maxsplit, rsplit gives the same result as split; it only matters when you limit the count. str.partition(sep) and str.rpartition(sep) always return a 3-tuple (head, separator, tail), which is convenient for unpacking because it never raises if the separator is missing. For file paths, prefer pathlib.Path (.parent, .name, .suffix) over manual splitting.

Splitting lines: splitlines()

text = "first line\nsecond line\r\nthird line\n"
print(text.splitlines())
print(text.split("\n"))
print(text.splitlines(keepends=True))
['first line', 'second line', 'third line']
['first line', 'second line\r', 'third line', '']
['first line\n', 'second line\r\n', 'third line\n']

splitlines() recognises \n, \r\n, \r and several Unicode line boundaries, and it does not produce a trailing empty item for a final newline. split("\n") leaves stray \r characters on Windows-style text and adds an empty last element. Use splitlines() for text; for files, iterate over the file object directly instead of reading everything and splitting.

Multiple delimiters with re.split()

When fields can be separated by different characters, use re.split(pattern, string, maxsplit=0) with a character class:

import re

data = "apples, oranges;pears  bananas|kiwis"
print(re.split(r"[,;|\s]+", data))
['apples', 'oranges', 'pears', 'bananas', 'kiwis']

The + makes a run of mixed delimiters (such as ", ") count as one boundary. Always use a raw string (r"...") for patterns.

Keeping the delimiters

If the pattern contains a capturing group, the matched delimiters are included in the result. Put the group around exactly what you want to keep:

print(re.split(r"(\s*[-+*/]\s*)", "3 + 4*2 - 1"))
print(re.split(r"\s*([-+*/])\s*", "3 + 4*2 - 1"))
['3', ' + ', '4', '*', '2', ' - ', '1']
['3', '+', '4', '*', '2', '-', '1']

To keep a delimiter attached to the preceding piece instead, split on a zero-width lookaround:

print(re.split(r"(?<=[.!?])\s+", "One. Two! Three? Four"))
print(re.split(r"(?=[A-Z])", "parseHttpResponseCode"))
['One.', 'Two!', 'Three?', 'Four']
['parse', 'Http', 'Response', 'Code']

Empty strings at the edges

print(re.split(r"\W+", "Hello, world!"))
print([t for t in re.split(r"\W+", "Hello, world!") if t])
print(re.findall(r"\w+", "Hello, world!"))
['Hello', 'world', '']
['Hello', 'world']
['Hello', 'world']

A delimiter at the start or end of the string yields an empty item. Filter it out, or flip the logic and use re.findall to match the tokens you want rather than the separators between them. In the re module, pass maxsplit as a keyword argument; passing it positionally is deprecated since Python 3.13.

Splitting into characters

print(list("héllo"))
print([int(d) for d in "2026"])
print(list(map(int, "10 20 30".split())))
['h', 'é', 'l', 'l', 'o']
[2, 0, 2, 6]
[10, 20, 30]

A string is an iterable of characters, so list(s) splits it into single code points. You usually do not need to: you can index and loop over a string directly. Note that "characters" here means Unicode code points; an emoji with a skin-tone modifier or a letter written with a combining accent can span several.

Quoted fields: use csv

Comma-separated data often contains commas inside quoted fields. split(",") breaks those apart; the csv module handles quoting and escaped quotes correctly:

import csv

row = 'Ada,"Lovelace, Countess",1815,"said ""hi"""'
print(row.split(","))
print(next(csv.reader([row])))
['Ada', '"Lovelace', ' Countess"', '1815', '"said ""hi"""']
['Ada', 'Lovelace, Countess', '1815', 'said "hi"']

csv.reader accepts any iterable of lines, including an open file (open it with newline=""). Use delimiter="\t" for TSV.

Shell-like splitting: shlex.split()

To split a command line the way a POSIX shell would, respecting quotes and backslash escapes, use shlex:

import shlex

cmd = 'grep -r "hello world" --include=*.py ./src'
print(cmd.split())
print(shlex.split(cmd))
print(shlex.join(["echo", "it's here"]))
['grep', '-r', '"hello', 'world"', '--include=*.py', './src']
['grep', '-r', 'hello world', '--include=*.py', './src']
echo 'it'"'"'s here'

The result can be passed straight to subprocess.run(args) without shell=True. shlex.join (Python 3.8+) is the inverse and quotes arguments safely.

Which method to use

InputMethod
Words separated by any whitespaces.split()
Fields with one fixed separators.split(sep)
Only the first / last N piecessplit(sep, n), rsplit(sep, n), partition
Lines of texts.splitlines()
Several delimiters or patternsre.split() or re.findall()
Individual characterslist(s)
CSV with quotescsv.reader
Command linesshlex.split()

Troubleshooting

Summary

s.split() splits on whitespace and never returns empty items; s.split(sep) splits on an exact string and preserves empty fields. Use maxsplit, rsplit or partition to limit splits, splitlines() for lines, re.split() for multiple delimiters (with a capturing group to keep them), list(s) for characters, and csv or shlex whenever quoting is involved.