在Tcl中处理带前导零的数字

6

我在使用Tcl时遇到了一些问题,涉及到带前导零的数字。我正在解析一些可能带有前导零的数字,例如"0012",应该被解释为整数"十二"。

$ tclsh
% set a 8 
8
% set b 08
08
% expr $a - 1
7
% expr $b - 1
expected integer but got "08" (looks like invalid octal number)

如何在Tcl中处理可能具有前导零的数字?

顺便问一下,如果“08”是无效的八进制数,那么在Tcl中什么构成有效的八进制数呢?


实际上,对于你的例子,你很幸运能够得到一个错误消息。如果字符串是011,你将会悄无声息地得到错误的数字。 - lalala
7个回答

9

您需要阅读 Tcl wiki 上的Tcl 和八进制数字。标准方法是将输入视为字符串,并使用 scan 命令提取数字。这会导致下面的多行 proc:

proc forceInteger { x } {
    set count [scan $x %d%s n rest]
    if { $count <= 0 || ( $count == 2 && ![string is space $rest] ) } {
        return -code error "not an integer: \"$x\""
    }
    return $n
}

1
多行代码,但“scan”建议是我见过的最紧凑且易于理解的建议。“set b [scan b %d]”满足我的需求。forceInteger过程的其余部分用于防御性输入验证。 - Christopher Bruns

4
这是最干净的解决方案:
% expr [ scan "08" %d ] - 1
7

此外:
% expr [ scan "09" %d ]
9
% expr [ scan 09 %d ]
9
% set nine 09
09
% expr [ scan $nine %d ]
9
% set success [ scan $nine %d number ]
1
% puts $number
9

扫描这可能是您关心的内容。"09"(即使作为字符串)转换为十进制数9

注意:
1. 如果此值无法转换为十进制值,则scan将返回空字符串({})。
2. 如果在scan命令中提供了变量名(例如上面示例中的number变量),则返回值表示状态0:解析十进制失败,1:成功。

表达式:用于演示expr如何将该值解释为数字,并回答原始问题。它评估一个表达式(例如,expr {2*3}返回6)。


如果你正在处理极大的数字,而不是日期或时间,那么请使用以下语法:scan $nine %11d nine - Sooth

2

我有很多 Tcl/Tk 8.1 版本的程序因为 Tcl/Tk 8.5.10 不能正确处理字符串/数字转换而变得不可用。

在这里,进入 Tcl 的 shell,并输入:

 %  expr {01}
 1

(and so on..)

 %  expr {06}
 6

 %  expr {07}    
 7

然后我们来到了8...

 %  expr {08}
 missing operator at "_@_"

看起来像是无效的八进制数

但情况变得更糟了。在Tcl shell中,尝试执行以下操作:

在我的Tcl8.1 shell中:

 % format "%.0f" {08}
 8

但是在我的新的改进过的Tcl8.5 shell中,我遇到了一个错误:
 % format "%.0f" {08}
 expected floating-point number but got "08" (looks like invalid octal number)

这就太傻了!我有一些代码在Tcl7.6和Tcl8.1中都能正常工作,但是在Tcl8.5中却开始出现奇怪的随机结果,只有当数字08恰好生成或被使用时才会出现这种情况!我已经花了数小时来尝试解决问题。但事实证明,我正在使用一个 很糟糕的代码

因此,我发布这个愤怒的警告。
Tcl/Tk版本8.5.10无法正确处理数字八。如果您期望从格式语句中得到合理的行为,那么这个 不会发生。 您的代码将继续运行,直到遇到{08}字符串时,Tcl 8.5.10解释器将生成错误,因为它将假定{08}是一个特殊的 八进制数,而不管您使用的所有其他小数字是否正常工作!

上述问题的一个可能解决方案是降级回Tcl 8.1 shell。我已经确认该版本至少可以正确处理数字08的格式语句。而Tcl 8.5.10 shell则不能。


1
你应该使用[scan "08" %d]而不是[format "%.0f" "08"]。scan将字符串“08”转换为数字(8)。format将数字“08”转换为字符串,但“08”在tcl中是无效的数字,因此在这种情况下不要使用format。 - Hazem

2
set $clean_number [regsub {^0*(.+)} $troublesome_number {\1}] this doesnt work 
set clean_number "" 
set $troublesome_number 08
% set $clean_number [regsub {^0*(.+)} $troublesome_number {\1}]
wrong # args: should be "regsub ?switches? exp string subSpec varName"

一个更简单的解决方案是:
set x 08
regsub {^[0]} $x {\1} x
puts $x
=>8

1

个人而言,我一直使用:

set $clean_number [regsub {^0*(.+)} $troublesome_number {\1}]

清理代码中的$麻烦数字


这个regsub方法是正确的,但与Mick Sharpe打开的链接中的一个解决方案非常相似。 - Christopher Bruns

1

这个链接应该会对你有所帮助

有效的八进制数只能包含数字0-7,并且必须以0开头


那个链接建议编写一个多行过程来去除零。我本来希望有更简单的方法。 - Christopher Bruns
八进制数字正确。我太傻了,当然“8”不是有效的八进制数字。内部偏移一个错误... - Christopher Bruns

-1
#!/bin/tclsh

#Regsub TCL script to remove the leading zeros from number. 
#Author : Shoeb Masood , Bagalore

puts "Enter the number"
set num [gets stdin]
regsub  {^0*} $num {\1} num
puts $num

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