Skip to content
VLSI Mentor

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

Azvya Education Pvt. Ltd.VLSI Mentor
equal.tcl
puts [string equal "abc" "abc"]
puts [string equal "abc" "abd"]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
1
0

Same 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

Azvya Education Pvt. Ltd.VLSI Mentor
nocase.tcl
puts "plain:   [string equal "ABC" "abc"]"
puts "-nocase: [string equal -nocase "ABC" "abc"]"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
plain:   0
-nocase: 1

There 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

Azvya Education Pvt. Ltd.VLSI Mentor
length.tcl
puts [string equal -length 2 "abcdef" "abxxxx"]
puts [string equal -length 3 "abcdef" "abxxxx"]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
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:

Azvya Education Pvt. Ltd.VLSI Mentor
prefix.tcl
set name "fifo_tb"

puts "with -length: [string equal -length 4 "fifo" $name]"
puts "with match:   [string match "fifo*" $name]"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
with -length: 1
with match:   1

The 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

Azvya Education Pvt. Ltd.VLSI Mentor
compare.tcl
puts "abc vs abd -> [string compare "abc" "abd"]"
puts "abd vs abc -> [string compare "abd" "abc"]"
puts "abc vs abc -> [string compare "abc" "abc"]"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
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:

Azvya Education Pvt. Ltd.VLSI Mentor
compare-options.tcl
puts [string compare -nocase "ABC" "abc"]
puts [string compare -length 2 "abcdef" "abxxxx"]
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
0
0

5. Which One to Use

You wantUse
plain equality in a conditioneq / ne
equality ignoring casestring equal -nocase
equality of a prefixstring match "prefix*"
ordering for a sortstring 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:

Azvya Education Pvt. Ltd.VLSI Mentor
normalise.tcl
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}]"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
compare each time:  1
normalise once:     1

Both 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

Azvya Education Pvt. Ltd.VLSI Mentor
ordering.tcl
puts "\"Apple\" vs \"apple\" -> [string compare "Apple" "apple"]"
puts "\"Z\" vs \"a\"         -> [string compare "Z" "a"]"
puts "\"10\" vs \"9\"        -> [string compare "10" "9"]"
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
"Apple" vs "apple" -> -1
"Z" vs "a"         -> -1
"10" vs "9"        -> -1

Uppercase 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 equal is the command form of eq, and string compare gives -1, 0 or 1.
  • The commands exist for their options — -nocase and -length — which the operators cannot express.
  • Default to eq and ne; reach for the command when you need an option.
  • -nocase has no operator equivalent.
  • For a prefix test, string match "prefix*" beats -length.
  • string compare returning 0 means equal, so using it directly in a condition inverts the meaning.
  • Its three-way answer is what lsort -command wants.
  • Comparison is by character code: uppercase before lowercase, and 10 before 9.
  • Normalising case once beats repeating -nocase. Next chapter.

Where this fits

Part of the Tcl curriculum.