TCL /将列表列表写入文件

问题描述 投票:0回答:2

我有一个变量,比方说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]

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

tcl
2个回答
2
投票

这样做的最好方法之一是stackchannel transform上将stdout重定向到你想要去的地方。即使对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"

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
© www.soinside.com 2019 - 2024. All rights reserved.