【问题标题】:Tcl: how to print one setTcl:如何打印一组
【发布时间】:2016-02-25 18:52:12
【问题描述】:

我要解析的文件是这样的

Name : John
Pin : 5400
Age : 40
Place: Korea
Amount : 4000
Name : Peter
Pin : 6700
Age : 10
Place : Japan
Amount : 3600

我的 tcl 代码是

set start "Name"
set pn "Pin"
set ag "Age"
set ag_cutoff 15
set amnt "Amount"
foreach line [split $content "\n"] {
   if {[regexp $start $line]} {
      set count 1
      set l1 $line
   }
   if {[regexp $pn $line] && $count ==1} {
       set pin_val [lindex $line 2]
       set l2 $line
   }
   if {[regexp $ag $line] && $count ==1} {
      set ag [lindex $line 2]
      if { $ag > $ag_cutoff} {
          set rep_taken 1
          set l3 $line
      }
      if {[regexp $amnt $line] && $count ==1 && $rep_taken == 1} {
          set age_val [lindex $line 2]
          puts $op1 "$ag $age_val "
          puts $op2 "$l1\n$l2\n$l3\n"
      }

此代码适用于绘图。

但是,我也想在 $ag>$ag_cutoff. 现在用 puts $op3 "$l1\n$l2\n$l3\n" ---> 能够打印到文件。但是如何打印未评估的行位置。有更好的方法来实现这一点。

姓名:约翰 引脚:5400 年龄:40 地点:韩国 金额:4000

【问题讨论】:

  • 那么,Name : 行标志着每条记录的开始?
  • 是的,它以 Amount 结尾。
  • 能够打印行但不完整的设置由 puts $op2 "$l1\n$l2\n$l3\n"。但仍然无法打印全套,包括地点:韩国
  • 您需要在 Place 和 : 之间留一个空格来输出韩国。
  • @PeterLewerin 不明白你的意思。我的疑问是如何打印未在集合中评估的行。我应该为 Place 或任何其他行做虚拟正则表达式吗?

标签: tcl


【解决方案1】:

让解析循环只创建一个字典会简单得多(这会替换您上面的代码):

set data {}
set count 0
foreach line [split $content \n] {
    if {[lindex $line 0] eq "Name"} {
        incr count
    }
    dict set data $count [lindex $line 0] [lindex $line 2]
}

如果第一行不是以“名称”开头,或者冒号和单词之间缺少空格,或者一个值由多个单词组成,这将会崩溃。所有这些都很容易解决。

例如,这里是一个扩展版本,可以解决最后两个问题,如果它们发生:

set data {}
set count 0
foreach line [split $content \n] {
    set keyword [string trimright [lindex $line 0] :]
    set value [string trimleft [lrange $line 1 end] {: }]
    if {$keyword eq "Name"} {
        incr count
    }
    dict set data $count $keyword $value
}

当所有记录都存储完毕后,可以使用字典迭代输出选中的记录:

set ag_cutoff 15
dict for {count record} $data {
    if {[dict get $record Age] > $ag_cutoff} {
        dict for {k v} $record {
            puts "$k : $v"
        }
    }
}

这也意味着您可以继续向记录中添加字段,并且代码仍然可以正常工作。

注意事项

如果content 中的数据在开头或结尾或某些行之间有空行,则这些方法将不起作用。防止开头或结尾出现空行或空行的一种简单方法是替换

foreach line [split $content \n] {

与

foreach line [split [string trim $content] \n] {

如果数据中可能出现空行/空行,可以使用它来跳过它们:

foreach line [split $content \n] {
    if {[string is space $line]} continue

如果 100% 确定所有数据都采用正确的列表形式,则可以直接在其上使用像 lindex 这样的列表命令(但有点代码味道)。如果不太确定,或者想要更正确,则应在处理之前将每一行转换为列表:

foreach line [split $content \n] {
    set line [split $line]

文档:dict、foreach、if、incr、lindex、lrange、puts、set、split、string

【讨论】:

  • 小心在字符串$line 上使用lindex。您应该将split 扔到循环中
  • @glennjackman:由于原始代码假定文件数据是“列表安全的”,我冒昧地让我的代码假定相同的东西。
猜你喜欢
  • 2011-01-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多