Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to split a string into a list of words in TCL, ignoring multiple spaces?

Tags:

string

split

tcl

Basically, I have a string that consists of multiple, space-separated words. The thing is, however, that there can be multiple spaces instead of just one separating the words. This is why [split] does not do what I want:

split "a    b"

gives me this:

{a {} {} {} b}

instead of this:

{a b}

Searching Google, I found a page on the Tcler's wiki, where a user asked more or less the same question.

One proposed solution would look like this:

split [regsub -all {\s+} "a    b" " "]

which seems to work for simple string. But a test string such as [string repeat " " 4] (used string repeat because StackOverflow strips multiple spaces) will result in regsub returning " ", which split would again split up into {{} {}} instead of an empty list.

Another proposed solution was this one, to force a reinterpretation of the given string as a list:

lreplace "a   list   with many   spaces" 0 -1

But if there's one thing I've learned about TCL, it is that you should never use list functions (starting with l) on strings. And indeed, this one will choke on strings containing special characters (namely { and }):

lreplace "test    \{a b\}"

returns test {a b} instead of test \{a b\} (which would be what I want, every space-separated word split up into a single element of the resulting list).

Yet another solution was to use a 'filter':

proc filter {cond list} {
    set res {}
    foreach element $list {if [$cond $element] {lappend res $element}}
    set res
}

You'd then use it like this:

filter llength [split "a   list   with many   spaces"]

Again, same problem. This would call llength on a string, which might contain special characters (again, { and }) - passing it "\{a b\}" would result in TCL complaining about an "unmatched open brace in list".

I managed to get it to work by modifying the given filter function, adding a {*} in front of $cond in the if, so I could use it with string length instead of llength, which seemed to work for every possible input I've tried to use it on so far.

Is this solution safe to use as it is now? Would it choke on some special input I didn't test so far? Or, is it possible to do this right in a simpler way?

like image 493
Jerry Avatar asked Nov 14 '12 14:11

Jerry


People also ask

How do I split a word in TCL?

The simplest way is to read all the data in, split into lines, and then use regexp with each line to extract the pieces. Like that, you can do lookups by name rather than having to manually search. Assuming that keys are unique and order doesn't matter.

How do I split a string with one or more spaces?

To split a string by multiple spaces, call the split() method, passing it a regular expression, e.g. str. trim(). split(/\s+/) . The regular expression will split the string on one or more spaces and return an array containing the substrings.

How do you split a string at a certain character?

To split a string with specific character as delimiter in Java, call split() method on the string object, and pass the specific character as argument to the split() method. The method returns a String Array with the splits as elements in the array.


1 Answers

The easiest way is to use regexp -all -inline to select and return all words. For example:

# The RE matches any non-empty sequence of non-whitespace characters
set theWords [regexp -all -inline {\S+} $theString]

If instead you define words to be sequences of alphanumerics, you instead use this for the regular expression term: {\w+}

like image 198
Donal Fellows Avatar answered Nov 15 '22 09:11

Donal Fellows