如何检查tcl中文件句柄的值是否为空

如何检查tcl中文件句柄的值是否为空,tcl,Tcl,我的脚本中有以下代码片段: puts "Enter Filename:" set file_name [gets stdin] set fh [open $file_name r] #Read from the file .... close $fh 现在,此代码段要求用户输入文件名。。然后将其设置为输入文件,然后读取。但是当名为$file\u name的文件不存在时,它会显示错误消息 illegal file character 如何检查fh是否不为null(我认为tcl中没有nul

我的脚本中有以下代码片段:

puts "Enter Filename:"
set file_name [gets stdin]

set fh [open $file_name r]

#Read from the file ....

close $fh
现在,此代码段要求用户输入文件名。。然后将其设置为输入文件,然后读取。但是当名为
$file\u name
的文件不存在时,它会显示错误消息

illegal file character
如何检查
fh
是否不为null(我认为tcl中没有
null
的概念是“everyting is a string”语言!),这样,如果给出了无效的文件名,我就可以抛出一个打印,说
文件不存在

简短回答: 此解决方案尝试在异常处理程序中打开文件。如果
open
成功,处理程序将在
on ok
子句中运行您给它的代码。如果
open
失败,处理程序将通过使用您想要的消息引发新错误来处理该错误(请注意,
open
也可能由于其他原因而实际失败)

try
命令是tcl8.6+,对于tcl8.5或更早版本,请参阅详细答案

长答覆: 打开一个文件可能会因为几个原因而失败,包括缺少文件或权限不足。某些语言允许文件打开函数返回指示失败的特殊值。包括Tcl在内的其他公司则通过根本不让
open
返回而引发异常来发出故障信号。在最简单的情况下,这意味着可以编写脚本,而不必考虑这种可能性:

set f [open nosuchf.ile]
# do stuff with the open channel using the handle $f
# run other code
在执行
open
命令时,此脚本将以错误消息终止

脚本不必因此而终止。只有在
open
命令成功时,才能截获异常并使使用文件句柄的代码执行:

if {![catch {open nosuchf.ile} f]} {
    # do stuff with the open channel using the handle $f
}
# run other code
(catch
命令是Tcl 8.5及更早版本中使用的一个不太复杂的异常处理程序。)

即使
open
失败,此脚本也不会过早终止,但在这种情况下,它也不会尝试使用
$f
。不管发生什么,“其他代码”都将运行

如果希望“其他代码”知道
open
操作是否失败或成功,可以使用以下构造:

if {![catch {open nosuchf.ile} f]} {
    # do stuff with the open channel using the handle $f
    # run other code in the knowledge that open succeeded
} else {
    # run other code in the knowledge that open failed
}
# run code that doesn't care whether open succeeded or failed
或者可以检查变量
f
的状态:

catch {open nosuchf.ile} f
if {$f in [file channels $f]} {
    # do stuff with the open channel using the handle $f
    # run other code in the knowledge that open succeeded
} else {
    # run other code in the knowledge that open failed
}
# run code that doesn't care whether open succeeded or failed
(Tcl 8.5+中有
操作符;如果您有早期版本,则需要以另一种方式编写测试。无论如何,您不应该使用早期版本,因为它们不受支持。)

此代码检查
f
的值是否是解释器知道的开放通道之一(如果不是,该值可能是错误消息)。这不是一个优雅的解决方案

确保频道关闭

这与问题无关,但却是一个良好的实践

try {
    open nosuchf.ile
} on ok f {
    # do stuff with the open channel using the handle $f
    # run other code in the knowledge that open succeeded
} on error {} {
    # run other code in the knowledge that open failed
} finally {
    catch {chan close $f}
}
# run code that doesn't care whether open succeeded or failed
(Tcl 8.5中添加了
chan
命令,以将多个与频道相关的命令分组为子命令。如果您使用的是早期版本的Tcl,您可以在不使用
chan
的情况下调用
close
,但您必须自行替换
try…finally

finally
子句确保在执行
on ok
on error
子句期间,无论文件是否打开或是否发生任何错误,当我们离开
try
构造时,通道都保证不存在(已销毁或从未创建)(变量
f
将保留一个不可用的值,除非我们取消设置它。由于我们不确定它是否存在,我们需要使用
catch{unset f}防止unset操作引发错误
取消设置-nocomplain f
。我通常不麻烦:如果我再次使用名称
f
,我只是将其设置为一个新值。)

文档:,,运算符

旧答案: (这个答案的核心是正确的,但几个月后我对它并不满意。因为它被三个人接受,甚至被标记为有用,我不愿意删除它,但上面的答案更好。)

如果试图打开不存在的文件并将通道标识符分配给变量,则会引发错误,变量的内容将保持不变。如果变量不存在,则不会通过
set
命令创建该变量。因此,虽然没有
null
值,但可以选择1)在打开文件之前,将变量设置为您知道不是通道标识符的值:

set fh {} ;# no channel identifier is the empty string
set fh [open foo.bar]
if {$fh eq {}} {
    puts "Nope, file wasn't opened."
}
或2)
unset
变量,然后测试它是否存在(使用
catch
处理变量不存在时引发的错误):

如果要测试文件是否存在,最简单的方法是使用
file exists
命令:

file exists $file_name

if {![file exists $file_name]} {
    puts "No such file"
}

文档:,,,

请注意,我在您接受答案后添加到了答案中。
catch {unset fh}
set fh [open foo.bar]
if {![info exists fh]} {
    puts "Nope, file wasn't opened."
}
file exists $file_name

if {![file exists $file_name]} {
    puts "No such file"
}