Tcl Part I · Module 5 · Strings
split and join: The Bridge Between Strings and Lists
These two commands connect this module to the next one. Almost every list you work with in real scripting arrives as text — a comma-separated row, a path, a line of output — and split is how it becomes a list you can index and iterate. join takes it back the other way. The mechanism is simple and there are exactly three places it surprises people, all of which come from the same root: the separator argument does not mean quite what it looks like, and the default behaviour is not the one other languages chose.
Foundation13 min readTclPart IStringsListsParsing
Part I · Module 5 · Chapter 5.13 · split and join
1. The Two Directions
set line "alpha,beta,gamma"
set fields [split $line ","]
puts "as a list: $fields"
puts "elements: [llength $fields]"
puts "rejoined: [join $fields " | "]"as a list: alpha beta gamma
elements: 3
rejoined: alpha | beta | gammasplit turns text into a list by cutting at a separator. join turns a list back into text by putting a separator between the elements.
2. Why This Chapter Matters
Module 6 teaches lists — indexing them, sorting them, searching them. Almost none of the lists you work with will be written out by hand in your source. They will arrive as text and come through split.
So this is the hinge. Getting it right means the next module's commands operate on the data you think they do.
3. An Empty Separator Splits Into Characters
puts [split "abc" ""]
puts [llength [split "hello" ""]]a b c
5The empty string as a separator means "between every character". That is a genuine and occasionally useful feature — it is how you get a list of characters — but it catches people who pass an empty variable by accident:
set sep ""
puts [split "a,b,c" $sep]a , b , cSeven elements, including the commas. If a separator comes from a variable, check it is not empty.
4. A Multi-Character Separator Is a Set
puts [split "a,b;c" ",;"]a b c",;" does not mean "split on the two-character sequence ,;". It means "split at a comma or a semicolon".
This is the same shape of trap as the second argument to string trim — Chapter 5.8 §4. In both commands, a string of characters means any one of these, not this exact sequence.
So you cannot split on ", " — comma-space — the way you might expect:
puts [split "a, b, c" ", "]
puts [llength [split "a, b, c" ", "]]a {} b {} c
5Five elements, with empty ones where the comma and the space both counted as separators.
The fix is to split on the comma alone and trim each piece — which needs Module 9's loops, or:
set line "a, b, c"
puts [split [string map {", " ","} $line] ","]a b cNormalise the separator with string map first, then split on a single character. That is Chapter 5.9 doing exactly the job it is good at.
5. The Default Does Not Collapse Whitespace
The biggest surprise in this module.
With no separator argument, split cuts on whitespace. You would reasonably expect consecutive spaces to be treated as one. They are not:
set s "a b c"
puts "split: [split $s]"
puts "elements: [llength [split $s]]"split: a b {} c
elements: 4Four elements, and the third is empty. The two spaces between b and c produced an empty element between them.
If you have written Python, this is the opposite of what "a b c".split() does.
Now compare with how Tcl reads the same text as a list:
set s "a b c"
puts "llength: [llength $s]"
puts "llength of split: [llength [split $s]]"llength: 3
llength of split: 4Three versus four, from the same text. List parsing collapses runs of whitespace; split does not.
6. Empty Elements Are Data
Adjacent separators produce an empty element, and that is correct rather than a bug:
set row "alpha,,gamma"
set fields [split $row ","]
puts "fields: $fields"
puts "count: [llength $fields]"
puts "second: <[lindex $fields 1]>"fields: alpha {} gamma
count: 3
second: <>Three fields, the middle one empty. In a CSV row that is exactly right — the column exists and has no value, which is different from the column being absent.
The braces in the printed output are how Tcl shows an empty element in a list. They are not characters in the data.
And it round-trips:
set row "alpha,,gamma"
puts [join [split $row ","] ","]alpha,,gamma7. join Is Simpler
set items {red green blue}
puts [join $items ", "]
puts [join $items "\n"]
puts [join $items]red, green, blue
red
green
blue
red green blueThe separator can be any text, including a newline — which is the neat way to turn a list into lines of output.
With no separator argument, join uses a single space.
Note the asymmetry: join takes a separator string used literally, while split takes a set of separator characters. They are not exact inverses, which is §4's point from the other direction.
8. A Complete Example
Parsing a configuration line:
set lines {
"timeout = 30"
"retries = 3"
"mode = fast"
}
foreach line $lines {
set line [string trim $line]
if {$line eq ""} { continue }
set i [string first "=" $line]
if {$i < 0} { continue }
set key [string trim [string range $line 0 $i-1]]
set value [string trim [string range $line $i+1 end]]
puts [format "%-10s = %s" $key $value]
}timeout = 30
retries = 3
mode = fastNote what is not used here: split on "=". Because a value might itself contain an equals sign, finding the first one — Chapter 5.4 §5 — is safer.
Where split is right is when every separator is a real boundary:
set row "clk,1,input"
lassign [split $row ","] name width direction
puts "name=$name width=$width direction=$direction"name=clk width=1 direction=inputlassign unpacks a list into variables in one line; Module 6 covers it.
9. Common Mistakes
Expecting bare split to collapse whitespace. §5. The biggest one.
Treating a multi-character separator as a sequence. §4. It is a set.
Passing an empty separator by accident. §3. You get characters.
Treating an empty element as an error. §6. In delimited data it is a real empty field.
Using split when the value may contain the separator. §8. Find the first one instead.
Expecting split and join to be exact inverses. §7.
10. Exercises
Exercise 1 — both directions. Split "one:two:three" on the colon, print the element count, then rejoin with " -> ".
Exercise 2 — the whitespace surprise. Predict the element count of split "x y z" before running it. Then print llength "x y z" and explain why the two differ.
Exercise 3 — a separator set. Predict the result of split "a-b_c" "-_" and of split "a-_b" "-_". Run both.
Exercise 4 — empty fields. Split the CSV row ",alpha,,beta," and print the count and each element in angle brackets. How many are empty, and where?
Exercise 5 — comma-space. Turn "red, green, blue" into a clean three-element list, using the string map approach from §4. Confirm no element has a leading space.
Exercise 6 — characters. Produce a list of the individual characters of "Tcl", then rejoin them with dashes.
Exercise 7 — pick the tool. For each, say split or string first: a CSV row; a key=value line where the value is a URL; a path separated by slashes; a line of name: description text.
11. What You Can Now Do
That closes Module 5. Across thirteen chapters you have the whole of Tcl's ordinary text handling:
- the
stringcommand's shape, and how to discover its subcommands - measuring, indexing and slicing
- finding literal text, and handling the
-1 - matching shapes with glob patterns
- comparing, with and without case
- normalising case and whitespace at the boundary
- replacing by position and by content
- building text up with
append - formatting it for a person to read, and scanning it back
- and converting between text and lists
Not one regular expression has appeared. That is deliberate: the overwhelming majority of real text handling is covered by what you now know, and Module 13 opens by saying so. When you reach it, you will be able to tell the cases that genuinely need it from the ones where a glob or a string map is clearer.
Module 6 is Lists, which is where the output of split goes next — indexing, searching, sorting, and the list commands that make the data you have just parsed useful.
12. Remember
splitmakes a list from text;joinmakes text from a list. This is the hinge into Module 6.- An empty separator splits into individual characters.
- A multi-character separator is a SET — any one of those characters — not a sequence. Same trap as
string trim. - The default splits on whitespace and does NOT collapse runs of it.
split "a b c"gives four elements. - Reading the same text as a list gives three, because list parsing does collapse whitespace.
- Adjacent separators produce an empty element, which in delimited data is a real empty field.
jointakes a literal separator string, so the two are not exact inverses.- Use
string firstinstead when the value may contain the separator. - Normalise with
string mapbefore splitting when the separator is more than one character.
Where this fits
Part of the Tcl curriculum.
