【发布时间】:2013-07-29 16:13:47
【问题描述】:
例如我有一句话:你的房子号码是多少? Is it 26. 我必须将每个单词的所有第一个字母都转换为大写并以小写形式保留。我想使用所有 lsearch、lindex lreplace 和东西并形成代码。有人可以告诉我该怎么做吗?
【问题讨论】:
例如我有一句话:你的房子号码是多少? Is it 26. 我必须将每个单词的所有第一个字母都转换为大写并以小写形式保留。我想使用所有 lsearch、lindex lreplace 和东西并形成代码。有人可以告诉我该怎么做吗?
【问题讨论】:
将命令应用于字符串的一堆正则表达式选择的子字符串的常用模型(在 8.6 和之前)是这样的:
subst [regsub -all $REtoFindTheSubstrings [MakeSafe $input] {[TheCommandToApply &]}]
MakeSafe 是必需的,因为subst 不只是做你想要的。即使禁用了一些替换类(例如,使用-novariables)选项,您仍然需要最棘手的一个——命令替换——这意味着像hello[pwd]goodbye 这样的字符串可以让你措手不及。为了解决这个问题,您可以通过用反斜杠版本替换每个 Tcl 元字符(或至少在subst 中重要的那些)来使字符串“安全”。这是MakeSafe 的经典版本(您经常会看到内联):
proc MakeSafe {inputString} {
regsub -all {[][$\\{}"" ]} $inputString {\\&}
}
交互式演示:
% MakeSafe {hello[pwd]goodbye}
hello\[pwd\]goodbye
使用该版本,subst 中无需关闭任何替换类,尽管您可以关闭变量,并且当您应用该命令时不会有任何意外,因为替换参数字符串中可能出现的内容有被逃脱了。但是有一个很大的缺点:您可能需要更改转换中的正则表达式以考虑现在存在的额外反斜杠。问题的 RE 不需要它(因为它只是选择单词字符序列),而且确实可以安全地使用这个简化版本:
subst [regsub -all {\w+} [regsub -all {[][\\$]} $input {\\&}] {[string totitle &]}]
从 8.7 开始,regsub 有一个 -command 选项,可以避免所有这些混乱。它也快了很多,因为subst 通过将其转换编译为字节码来工作(这对于一次性替换来说不是一个好的胜利!)而regsub -command 使用直接命令调用,更有可能更快。
regsub -all -command {\w+} $input {string totitle}
regsub -all -command 使用的内部方法可以在 8.6 中模拟(或更早版本带有更多额外的 shim),但这并不重要:
proc regsubAllCommand {RE input command} {
# I'll assume there's no sub-expressions in the RE in order to keep this code shorter
set indices [regexp -all -inline -indices -- $RE $input]
# Gather the replacements first to make state behave right
set replacements [lmap indexPair $indices {
# Safe version of: uplevel $command [string range $input {*}$indexPair]
uplevel 1 [list {*}$command [string range $input {*}$indexPair]]
}]
# Apply the replacements in reverse order
set output $input
foreach indexPair [lreverse $indices] replacement [lreverse $replacements] {
set output [string replace $output {*}$indexPair $replacement]
}
return $output
}
regsub 的 C 实现在内部使用工作缓冲区等,但在 Tcl 级别上不太方便。
【讨论】:
-command 选项。这远太长了,无法发表评论!
-nobackslashes -novariables 之前所做的事情上投入了太多。
string totitle 命令关闭:它将整个字符串小写,但第一个字符为大写。
set s {whAT is yOur hoUSe nUmBer ? Is iT 26.}
string totitle $s
What is your house number ? is it 26.
将每个单词大写有点复杂:
proc CapitalizeEachWord {sentence} {
subst -nobackslashes -novariables [regsub -all {\S+} $sentence {[string totitle &]}]
}
set s {whAT is yOur hoUSe nUmBer ? Is iT 26.}
CapitalizeEachWord $s
What Is Your House Number ? Is It 26.
regsub 命令获取每个以空格分隔的单词并将其替换为文字字符串“[string totitle word]”:
"[string totitle whAT] [string totitle is] [string totitle yOur] [string totitle hoUSe] [string totitle nUmBer] [string totitle ?] [string totitle Is] [string totitle iT] [string totitle 26.]"
我们使用subst 命令来评估所有单独的“string totitle”命令。
当 Tcl 8.7 出来时,我们就能做到:
proc CapitalizeEachWord {sentence} {
regsub -all -command {\S+} $sentence {string totitle}
}
【讨论】:
-nobackslashes -novariables opts 删除的元字符的例子,或者一个有问题的字符串吗?
set sentence {hello[pwd] world} 然后CapitalizeEachWord $sentence 产生Hello/users/glennjackman World
subst来做命令替换,所以我们需要转义原句中的任何开括号:regsub -all {\[} $sentence {\\&}
您可以使用 Initcap 函数将第一个字母大写,其余字母小写。
【讨论】:
Initcap fooBar 并获得 invalid command name "Initcap",所以你在说什么?