Skip to content
VLSI Mentor

Tcl Part I · Module 5 · Strings

Indexing and Ranges: string index and string range

Two subcommands get at the characters inside a string: one for a single character, one for a span. Both are straightforward, and both come with a feature that makes Tcl noticeably pleasanter than languages where you have to subtract from a length — you can write end and end-3 directly as positions. They also share a behaviour worth knowing before you rely on it: asking for a position that does not exist gives you an empty string rather than an error, which is convenient until it quietly hides a mistake.

Foundation12 min readTclPart IStringsIndexingSlicing

Part I · Module 5 · Chapter 5.3 · Indexing and Ranges

1. Positions Start at Zero

Azvya Education Pvt. Ltd.VLSI Mentor
positions.tcl
set line "report_2024.txt"

puts "index 0  -> [string index $line 0]"
puts "index 6  -> [string index $line 6]"
puts "index 11 -> [string index $line 11]"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
index 0  -> r
index 6  -> _
index 11 -> .

The first character is at 0, as in most languages.

Counting positions by hand is error-prone, which is what the next section is for.

2. Counting From the Right

end is the last position, and end-N counts back from it:

Azvya Education Pvt. Ltd.VLSI Mentor
end.tcl
set line "report_2024.txt"

puts "index end   -> [string index $line end]"
puts "index end-3 -> [string index $line end-3]"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
index end   -> t
index end-3 -> .

These are index keywords, not arithmetic you perform. You never have to write this:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
string index $line [expr {[string length $line] - 1}]

That is noisier and is where off-by-one errors come from. end is exact and says what you mean.

3. Spans With string range

Azvya Education Pvt. Ltd.VLSI Mentor
range.tcl
set line "report_2024.txt"

puts "range 0 5       -> [string range $line 0 5]"
puts "range 7 end     -> [string range $line 7 end]"
puts "range end-2 end -> [string range $line end-2 end]"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
range 0 5       -> report
range 7 end     -> 2024.txt
range end-2 end -> txt

Both ends are inclusive. 0 5 gives six characters, positions 0 through 5.

That is worth stating because many languages use an exclusive upper bound. In Tcl, string range $s 0 5 and a Python slice s[0:5] give different results — Tcl's includes position 5.

4. Arithmetic Inside a Position

Positions accept simple arithmetic, which is what makes these commands useful after a search:

Azvya Education Pvt. Ltd.VLSI Mentor
arithmetic.tcl
set s "key:value"
set i 3

puts "before the colon -> [string range $s 0 $i-1]"
puts "after the colon  -> [string range $s $i+1 end]"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
before the colon -> key
after the colon  -> value

$i-1 and $i+1 are written directly in the position, with no expr and no brackets.

This is the shape you will use constantly once you can find a position, which is the next chapter. Splitting a line at its first colon becomes two readable lines rather than four noisy ones.

5. Out of Range Is Empty, Not an Error

Azvya Education Pvt. Ltd.VLSI Mentor
out-of-range.tcl
set s "abc"

puts "index 99   -> <[string index $s 99]>"
puts "index -1   -> <[string index $s -1]>"
puts "range 5 10 -> <[string range $s 5 10]>"
puts "range 2 0  -> <[string range $s 2 0]>"
puts "range -5 1 -> <[string range $s -5 1]>"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
index 99   -> <>
index -1   -> <>
range 5 10 -> <>
range 2 0  -> <>
range -5 1 -> <ab>

None of these is an error. Every out-of-range request gives an empty string, and a range whose end is before its start gives an empty string too.

The last line is the interesting one: a range starting before the string is clamped to the beginning, so -5 1 gives the first two characters.

6. A Worked Example

Taking a fixed-width record apart:

Azvya Education Pvt. Ltd.VLSI Mentor
fixed-width.tcl
set record "RX  00421  OK   "

set code   [string trim [string range $record 0 3]]
set number [string trim [string range $record 4 10]]
set status [string trim [string range $record 11 end]]

puts "code=<$code> number=<$number> status=<$status>"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
code=<RX> number=<00421> status=<OK>

Each field is a known span, pulled out by position and trimmed — Chapter 5.8.

Fixed-width records are common in tool output and legacy formats. When the fields are separated by a character instead, Chapter 5.13 is the better approach.

7. Building a String by Position

string range reads; it does not write. To change part of a string you use string replace, which is Chapter 5.9:

Azvya Education Pvt. Ltd.VLSI Mentor
no-write.tcl
set s "hello"

puts "pieces: [string range $s 0 0][string toupper [string range $s 1 end]]"
puts "original unchanged: $s"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
pieces: hELLO
original unchanged: hello

Nothing modified s — Chapter 5.1 §7.

8. Common Mistakes

Expecting an exclusive end. Both ends are inclusive. §3.

Computing the last position with string length. Use end. §2.

Assuming out-of-range raises an error. It gives "". §5.

Using a position from a search without checking it. Chapter 5.4.

Expecting string range to modify the string. It returns a new one. §7.

Using positions where a separator would be simpler. If the data has delimiters, use Chapter 5.13.

9. Exercises

Exercise 1 — read positions. For set s "measurement", predict then check: string index $s 0, string index $s 4, string index $s end, string index $s end-1.

Exercise 2 — spans. From the same string, extract measure, ment, and the middle sure, using string range three times.

Exercise 3 — inclusive ends. How many characters does string range $s 2 6 return? Work it out before running, then confirm.

Exercise 4 — out of range. Predict the output of all five lines in §5 before running them. Which one did you expect to error?

Exercise 5 — split at a position. With set s "name=value" and set i 4, produce name and value using index arithmetic and no expr.

Exercise 6 — a fixed-width line. Given "ERR 0042 timeout ", extract the three fields by position and trim each. Then say what would break if a field were one character wider.

10. Remember

  • Positions start at 0. string index gives one character, string range gives a span.
  • Both ends of a range are inclusive. 0 5 is six characters.
  • end and end-N are index keywords, so you never subtract from a length.
  • Simple arithmetic works inside a position: $i-1, $i+1.
  • Out of range gives an empty string, never an error — including a reversed range.
  • A range starting before the string is clamped to the beginning.
  • That convenience hides mistakes, so check positions that came from a search or a calculation.
  • Neither command modifies the string. string replace is for changing text.

Next: finding where something is, so you have a position worth using.

Where this fits

Part of the Tcl curriculum.