TCL /将列表列表写入文件

时间:2018-02-28 14:00:02

标签: tcl

我有一个变量,让我们说xx,带有索引0和索引1值的列表。我想修改一个先前定义了函数pptable的脚本(不是我的),即

proc pptable {l1 l2} {
    foreach i1 $l1 i2 $l2 {
        puts " [format %6.2f $i1]\t[format %6.2f $i2]"
    }
}

以便使用

将输出显示为两列
pptable [lindex $xx 1] [lindex $xx 0]

但是,我想将输出直接写入文件。你能告诉我如何将数据发送到文件而不是显示器吗?

2 个答案:

答案 0 :(得分:2)

执行此操作的最佳方法之一是stack上的channel transformstdout重定向到您希望的位置。即使对stdout的写入发生在C代码或不同的线程中,因为它插入到通道机制中,这也会起作用。代码有点长(并且需要Tcl 8.6)但是可靠且实际上非常简单。

package require Tcl 8.6;  # *REQUIRED* for [chan push] and [chan pop]

proc RedirectorCallback {targetHandle op args} {
    # The switch/lassign pattern is simplest way of doing this in one procedure
    switch $op {
        initialize {
            lassign $args handle mode
            # Sanity check
            if {$mode ne "write"} {
                close $targetHandle
                error "this is just a write transform"
            }
            # List of supported subcommands
            return {initialize finalize write}
        }
        finalize {
            lassign $args handle
            # All we need to do here is close the target file handle
            close $targetHandle
        }
        write {
            lassign $args handle buffer
            # Write the data to *real* destination; this does the redirect
            puts -nonewline $targetHandle $buffer
            # Stop the data going to *true* stdout by returning empty string
            return ""
            # If we returned the data instead, this would do a 'tee'
        }
        default {
            error "unsupported subcommand"
        }
    }
}

# Here's a wrapper to make the transform easy to use
proc redirectStdout {file script} {
    # Stack the transform onto stdout with the file handle to write to
    # (which is going to be $targetHandle in [redirector])
    chan push stdout [list RedirectorCallback [open $file "wb"]]

    # Run the script and *definitely* pop the transform after it finishes
    try {
        uplevel 1 $script
    } finally {
        chan pop stdout
    }
}

我们如何实际使用它?在实践中它真的很容易:

# Exactly the code you started with
proc pptable {l1 l2} {
    foreach i1 $l1 i2 $l2 {
        puts " [format %6.2f $i1]\t[format %6.2f $i2]"
    }
}

# Demonstrate that stdout is working as normal
puts "before"

# Our wrapped call that we're capturing the output from; pick your own filename!
redirectStdout "foo.txt" {
    pptable {1.2 1.3 1.4} {6.9 6.8 6.7}
}

# Demonstrate that stdout is working as normal again
puts "after"

当我运行该代码时,我得到了这个:

bash$ tclsh8.6 stdout-redirect-example.tcl 
before
after
bash$ cat foo.txt 
   1.20   6.90
   1.30   6.80
   1.40   6.70

我相信这正是您所寻找的。

如果您使用Tcllib和TclOO帮助处理机器,则可以使用更少的代码执行此操作:

package require Tcl 8.6
package require tcl::transform::core

oo::class create WriteRedirector {
    superclass tcl::transform::core

    variable targetHandle
    constructor {targetFile} {
        set targetHandle [open $targetFile "wb"]
    }
    destructor {
        close $targetHandle
    }

    method write {handle buffer} {
        puts -nonewline $targetHandle $buffer
        return ""
    }

    # This is the wrapper, as a class method
    self method redirectDuring {channel targetFile script} {
        chan push $channel [my new $targetFile]
        try {
            uplevel 1 $script
        } finally {
            chan pop $channel
        }
    }
}

用法示例:

proc pptable {l1 l2} {
    foreach i1 $l1 i2 $l2 {
        puts " [format %6.2f $i1]\t[format %6.2f $i2]"
    }
}

puts "before"
WriteRedirector redirectDuring stdout "foo.txt" {
    pptable {1.2 1.3 1.4 1.5} {6.9 6.8 6.7 6.6}
}
puts "after"

答案 1 :(得分:0)

我认为你不想或不能修改现有的脚本和proc pptable,对吗?

如果是这样,根据您的具体情况,有不同的选项:

  • 重定向stdouttclsh yourscript.tcl > your.out
  • 重新定义puts(对于明确定义的范围):

    rename ::puts ::puts.orig 
    proc puts args { 
        set fh [open your.out w]; 
        ::puts.orig $fh $args; 
        close $fh
    }
    # run pptable, source the script
    

    之前已涵盖此主题,例如tcl stop all output going to stdout channel?

  • 重新连接Tcl的stdout频道(不一定推荐):

    close stdout
    open your.out w
    # run pptable, source the script
    

    之前已经详细阐述过,例如: Tracing stdout and stderr in Tcl