如何在TCL中将一个字符串按单词拆分为列表,忽略多个空格?

6

基本上,我有一个由多个以空格分隔的单词组成的字符串。然而,问题在于,单词之间可能会有多个空格而不仅仅是一个。这就是为什么[split]不能达到我的要求的原因:

split "a    b"

给我这个:
{a {} {} {} b}

使用以下代码替代:

{a b}

我在谷歌上搜索后发现Tcler's维基页面,其中一个用户提出了几乎相同的问题。
一种提议的解决方案如下所示:
split [regsub -all {\s+} "a    b" " "]

似乎适用于简单字符串的情况。但是测试用的字符串 [string repeat " " 4](使用了 string repeat,因为 StackOverflow 会删除多个空格)将导致 regsub 返回 " ",而 split 再次将其拆分成 {{} {}} 而不是空列表。
另一个提出的解决方案是这个,强制重新解释给定字符串作为列表:
lreplace "a   list   with many   spaces" 0 -1

但是如果有一件事情我从TCL中学到的,那就是你永远不应该在字符串上使用列表函数(以l开头)。实际上,这个函数会在包含特殊字符(即 { 和 })的字符串上产生错误:

lreplace "test    \{a b\}"

返回的结果是test {a b}而不是test \{a b\}(我想要的是每个以空格分隔的单词拆分为结果列表中的单个元素)。

另一个解决方案是使用“过滤器”:

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

然后,您可以像这样使用它:

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

同样的问题又出现了。这会在字符串上调用 llength 函数,如果字符串中包含特殊字符(例如 { 和 }),传入 "\{a b\}" 就会导致 TCL 抱怨 “列表中有无法匹配的开括号”。

我通过修改给定的 filter 函数,并在 if 语句中在 $cond 的前面添加 {*},这样我就可以使用 string length 而不是 llength,它似乎对我尝试使用的每种可能的输入都有效。

这个解决方案现在安全可靠吗?它是否会在我没有测试的某些特殊输入上崩溃?或者,有没有更简单的方法做到这一点?

2个回答

16

最简单的方法是使用regexp -all -inline选择并返回所有单词。例如:

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

如果您将单词定义为字母数字序列,则可以使用以下正则表达式术语:{\w+}


1
дҪҝз”ЁTcllibдёӯзҡ„textutil::splitжҳҜеҸҰдёҖз§ҚйҖүжӢ©гҖӮ - kostix

0

你可以使用正则表达式代替: 来自tcl wiki split:

按空格分割:陷阱

split { abc def  ghi}
{} abc def {} ghi

通常情况下,如果你想按空格分割字符串并且不想要那些空白字段,最好采用以下方法:
regexp -all -inline {\S+} { abc def  ghi}
abc def ghi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接