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

时间:2014-07-08 05:47:17

标签: tcl

我的剧本中有这个片段:

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

set fh [open $file_name r]

#Read from the file ....

close $fh

现在,此代码片段要求用户输入文件名..然后将其设置为输入文件然后读取。但是当名称为$file_name的文件不存在时,会显示错误

illegal file character

我如何检查fh是否为空(我不认为tcl中有NULL的概念是“每个都是字符串”语言!),如果无效给出了file_name,我可以抛出一个说file doesn't exists!

的印刷品

1 个答案:

答案 0 :(得分:3)

简答:

try {
    open $file_name
} on ok f {
    # do stuff with the open channel using the handle $f
} on error {} {
    error {file doesn't exist!}
}

此解决方案尝试在异常处理程序中打开文件。如果open成功,则处理程序将运行您在on ok子句中提供的代码。如果open失败,处理程序会通过使用您想要的邮件引发新错误来处理该错误(请注意open可能因其他原因而实际失败)。

try命令是Tcl 8.6+,对于Tcl 8.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

in运算符在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

(在{1}}中添加了chan命令,将几个与通道相关的命令组合为子命令。如果您使用的是早期版本的Tcl,则可以在不{{1}的情况下调用close但是你必须为chan推出自己的替代品。)

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

文档:catchchancloseerrorin运营商,fileif,{{ 3}},opensettry

旧答案:

(这个答案的心脏在正确的地方,但这几个月后我不满意。因为它被三个人接受甚至标记为有用我不愿删除它,但上面的答案是恕我直言更好。)

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

null

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

catch

如果要测试文件是否存在,最简单的方法是使用catch {unset fh} set fh [open foo.bar] if {![info exists fh]} { puts "Nope, file wasn't opened." } 命令:

file exists

文档:unsetcatchfileifopenputsset