Tcl Part I · Module 5 · Strings
Comparing Strings: string equal and string compare
Chapter 4.4 gave you eq and ne, which compare text and never change their minds. This chapter covers the command forms of the same idea, and the question worth answering is why both exist. The answer is that the commands accept options the operators have no room for — ignoring case, and comparing only the first few characters — and that one of them gives a three-way answer rather than yes or no, which is what sorting needs.
Foundation10 min readTclPart IStringsComparison
Part I · Module 5 · Chapter 5.6 · Comparing Strings: string equal and string compare
1. string equal
puts [string equal "abc" "abc"]
puts [string equal "abc" "abd"]1
0Same question as eq from Chapter 4.4, same answer. Characters are compared; nothing is read as a number.
So why does it exist? Options.
2. Ignoring Case
puts "plain: [string equal "ABC" "abc"]"
puts "-nocase: [string equal -nocase "ABC" "abc"]"plain: 0
-nocase: 1There is no operator form of this. eq is always case-sensitive, so when you need case-insensitive equality the command is the only direct way.
This matters for anything a human typed or any value that arrives from a tool with inconsistent capitalisation — YES against yes, ERROR against Error.
3. Comparing Only the First Few Characters
puts [string equal -length 2 "abcdef" "abxxxx"]
puts [string equal -length 3 "abcdef" "abxxxx"]1
0-length 2 compares only the first two characters, which are equal. -length 3 includes the third, which differs.
This is occasionally useful for prefix tests, though Chapter 5.5 usually says it better:
set name "fifo_tb"
puts "with -length: [string equal -length 4 "fifo" $name]"
puts "with match: [string match "fifo*" $name]"with -length: 1
with match: 1The string match version does not require you to count the prefix, so it survives the prefix changing. Prefer it.
4. string compare Gives Three Answers
puts "abc vs abd -> [string compare "abc" "abd"]"
puts "abd vs abc -> [string compare "abd" "abc"]"
puts "abc vs abc -> [string compare "abc" "abc"]"abc vs abd -> -1
abd vs abc -> 1
abc vs abc -> 0- -1 — the first sorts before the second
- 0 — they are identical
- 1 — the first sorts after the second
Three answers rather than two, and that is the whole reason it exists: ordering, not just equality.
It takes the same options:
puts [string compare -nocase "ABC" "abc"]
puts [string compare -length 2 "abcdef" "abxxxx"]0
05. Which One to Use
| You want | Use |
|---|---|
| plain equality in a condition | eq / ne |
| equality ignoring case | string equal -nocase |
| equality of a prefix | string match "prefix*" |
| ordering for a sort | string compare |
| ordering in a condition | < and > |
Default to the operators. if {$mode eq "fast"} reads better than if {[string equal $mode "fast"]}, and it is shorter.
Reach for the command when you need an option — which in practice means -nocase nearly every time.
6. Case-Insensitive Comparison, Two Ways
There is a second route, and it is often the better one:
set a "ERROR"
set b "error"
puts "compare each time: [string equal -nocase $a $b]"
set a_norm [string tolower $a]
set b_norm [string tolower $b]
puts "normalise once: [expr {$a_norm eq $b_norm}]"compare each time: 1
normalise once: 1Both give the right answer. The second one scales better: if a value is compared in several places, converting it once on the way in means every later comparison is a plain eq, and you cannot forget -nocase on one of them.
That is Chapter 5.7's subject, and it is the next chapter for exactly this reason.
7. Comparison Is by Character Code
puts "\"Apple\" vs \"apple\" -> [string compare "Apple" "apple"]"
puts "\"Z\" vs \"a\" -> [string compare "Z" "a"]"
puts "\"10\" vs \"9\" -> [string compare "10" "9"]""Apple" vs "apple" -> -1
"Z" vs "a" -> -1
"10" vs "9" -> -1Uppercase letters sort before lowercase ones, because their character codes are lower. And 10 sorts before 9 because the comparison is character by character — 1 before 9 — which is Chapter 4.3 §5 again.
If you want numeric ordering, you want numeric comparison, or lsort -integer in Module 6.
8. Common Mistakes
Using the command where an operator reads better. §5.
Forgetting -nocase on one comparison out of several. Normalise once instead. §6.
Using -length for a prefix test. string match is clearer. §3.
Expecting string compare to return a truth value. It returns -1, 0 or 1, and 0 means equal — so if {[string compare $a $b]} is true when they differ. That inversion catches people.
Expecting dictionary-style ordering. It compares character codes, so case and digits behave as in §7.
9. Exercises
Exercise 1 — equality four ways. Compare "Fast" and "fast" using eq, string equal, string equal -nocase, and by normalising both with string tolower first.
Exercise 2 — three answers. Predict then check string compare for the pairs (a,b), (b,a), (a,a), (abc,ab).
Exercise 3 — the inversion. Write if {[string compare "x" "x"]} { puts "same" } and explain why nothing prints. Then fix it.
Exercise 4 — prefix, two ways. Test whether "fifo_tb" starts with "fifo" using -length and using string match. Which keeps working if the prefix becomes "async_fifo"?
Exercise 5 — normalise once. Write a script that reads three mixed-case status words and counts how many are ok, converting each once rather than using -nocase three times.
Exercise 6 — ordering surprise. Sort out by prediction which of these are negative: string compare "B" "a", string compare "2" "10", string compare "" "a".
10. Remember
string equalis the command form ofeq, andstring comparegives -1, 0 or 1.- The commands exist for their options —
-nocaseand-length— which the operators cannot express. - Default to
eqandne; reach for the command when you need an option. -nocasehas no operator equivalent.- For a prefix test,
string match "prefix*"beats-length. string comparereturning 0 means equal, so using it directly in a condition inverts the meaning.- Its three-way answer is what
lsort -commandwants. - Comparison is by character code: uppercase before lowercase, and
10before9. - Normalising case once beats repeating
-nocase. Next chapter.
Where this fits
Part of the Tcl curriculum.
