F5's reference page for the matches_regex operator carries a short note: it is more efficient to use a string command than a regular expression, and most scenarios can be handled with string match or scan. scan takes a value apart with a format, the way C's sscanf does, and stores the pieces in variables. iRules run 8.4.6 (F5 K6091), so its scan is the one described here.
Reading a format
A format is a sequence of three kinds of directive:
- white space skips any amount of white space in the input, including none;
- literal text must match the input character for character;
- conversions start with
%and read a value:%da decimal integer,%xhexadecimal,%ooctal,%ian integer whose base comes from its prefix,%scharacters up to white space,%cone character as its code,%[...]characters from a set (%[^&]is everything up to an ampersand), and%nthe amount of input consumed so far. A*after the%converts without storing, and a number sets a maximum width.
% scan "10:30" "%d:%d" hours minutes
2
% scan "192.168.1.20" "%d.%d.%d.%d" a b c d
4
% scan "user=abc&x=1" {user=%[^&]} user
1
% set user
abc
These are tclsh sessions: each command follows the % prompt and its result is on the next line. The third format is braced: in double quotes, Tcl would run [^&] as a command before scan ever saw it (Tcl 8.4.6 stops with invalid command name "^&"), so brace any format that contains [.
What the result means
With variable names, scan returns the number of conversions that stored a value, which is not the same as "the input matched":
-1: the input ran out before the first conversion (an empty value, for example);0: a conversion was tried and did not match (scan "abc" "%d" n);- a smaller number than you expected: scanning stopped part-way, and every variable after that point was not set by this scan, so it keeps whatever value it had before.
Without variable names, scan returns the converted values as a list instead.
Because the count only counts conversions, a literal that fails after the last conversion does not change the result. F5's matches_regex page shows this check as a scan alternative to a regular expression for "www., then a name, then .com":
scan [string tolower [HTTP::host]] {www.%[a-z0-9.:-].com} host_token
It returns 1 for www.example.com, and also 1 for www.example.org: the set includes the dot, so it consumes example.org and the literal .com is never reached. Even with the dot taken out of the set, the failed .com would not lower the count. To check what comes after the last conversion, end the format with %n and require it to be stored:
if { [scan [string tolower [HTTP::host]] {www.%[a-z0-9-].com%n} name used] == 2
&& $used == [string length [HTTP::host]] } {
pool www_pool
}
Tcl 8.4.6 details the manual does not spell out
- Integers go through a C int.
scan "3000000000" "%d" nstores-1294967296, and thelsize modifier does not change that in 8.4.6. To keep a large number intact, read it as text with%[0-9]and letexprinterpret it, sinceexprworks with 64-bit integers. %ncounts bytes. The manual says characters, but Tcl 8.4.6 counts the bytes of its internal , so each character outside counts 2 or 3.%ifollows the prefix rules, so08read with%igives 0 and stops at the 8. Use%dfor zero-padded decimal values such as times.
When scan is the right tool
scan shines when the shape of the value is fixed: an address, a time, a version string, a request line, a known prefix followed by a value. When the value is free-form, string first and string range, F5's getfield, or a switch -glob are often clearer. For a value with a fixed separator, compare scan with findstr, substr and getfield.
Try it
The scan explainer lines every directive up against the characters it consumed, shows each variable's value, and says exactly why scanning stopped, including the Tcl 8.4.6 behaviours above.
Sources
The efficiency note and the www./.com example are from F5's matches_regex reference; the conversions and result codes are from the Tcl 8.4 manual; the int storage and byte counting were confirmed by running Tcl 8.4.6 itself.