从tcl中的文件解析键控列表?

时间:2013-12-25 04:01:05

标签: list parsing tcl s-expression

我的文件中包含以下格式的记录:

{TOKEN 
    { NAME {name of this token} }
    { GROUPS {Group 1} }
    { VALUE value }
    { REPEATING {
        { MAX 3 }
        { TIME {nmin 30} }
    } }
    { WINDOW */*/*/* }
    { ACTION {
        { EXEC {code to run here} }
    } }
}
{TOKEN 
    { NAME {name of next token} }
    { GROUPS {Group 1} }
    { VALUE value }
    { WINDOW 0/0:30-2:00,3:30-7:30/*/* }
    { HOST {localhost} }
    { ACTION {
        { email {
            { FROM cloverleaf@healthvision.com }
            { TO me@xxxx.org }
            { SUBJ {email subject test} }
            { MSG {this is the email body} }
        } }
    } }

并非所有记录都具有相同的关键字,但它们都是嵌套的键控列表,我需要将它们解析为.csv文件以便于查看。但是,当我读入文件时,它作为单个字符串而不是键控列表列表。在空格或换行符上拆分也无济于事,因为它们也位于键控列表中。我试图在} \ n和{T之间插入一个管道(|)并在管道上拆分,但我仍然最终得到了字符串。

我希望有人可以指出我正确的方向来解析这些s表达式文件。

提前感谢!

Ĵ

4 个答案:

答案 0 :(得分:1)

问题

以下是我了解您的问题的方法。

  • 您有一个充满记录的文本文件。每条记录都是 {TOKEN ...}
  • 每条记录几乎一个键控列表,但不完全:字符串 TOKEN 使其成为无效的键控列表。如果我们删除此字符串,则其余字符串将是有效的键控列表。
  • 每个键控列表可能是嵌套的。也就是说,该值可能是另一个键控列表。
  • 您希望将每条记录作为CSV文件中的一行写入。但是,在CSV文件中,每行应包含相同数量的列,这不是这里的情况。我会留给你找出如何最好地处理它。

解决方案

我建议把它变成一个字典,这是一个扁平的,而不是嵌套的结构。这应该使工作更容易。一旦你有一个平面列表,处理它变得更容易。这是我的解决方案:

# myscript.tcl

package require Tclx

proc makeKey {prefix key} {
    return [string trim "$prefix $key"]
}   

proc keyedlist2dict {klname {keyPrefix ""}} {
    upvar 1 $klname kl
    set d {}
    foreach key [keylkeys kl] {
        set value [keylget kl $key]
        if {[catch {keylkeys value}]} {
            # value is not a nested keyed list
            lappend d [makeKey $keyPrefix $key] $value
        } else {
            # value is a nested keyed list
            set d [concat $d [keyedlist2dict value $key]] ;# TCL 8.4
        }   
    }   

    return $d
}   

set contents [read [open data.txt]]
foreach item $contents { 
    # Each item starts with "TOKEN", which we need to remove otherwise
    # the keyed list is invalid
    set item [lrange $item 1 end]

    # Convert a keyed list to a dict, then to a csv row. We can then 
    # display the row or to write it to a file.
    set rec [keyedlist2dict item]

    # Display it
    foreach {key value} $rec { ;# TCL 8.4
        puts "$key: $value"
    }   
    puts ""
}   

运行脚本

tclsh myscript.tcl

输出

NAME: name of this token
GROUPS: Group 1
VALUE: value
REPEATING MAX: 3
REPEATING TIME: nmin 30
WINDOW: */*/*/*
ACTION EXEC: code to run here

NAME: name of next token
GROUPS: Group 1
VALUE: value
WINDOW: 0/0:30-2:00,3:30-7:30/*/*
HOST: localhost
email FROM: cloverleaf@healthvision.com
email TO: hardej@mmc.org
email SUBJ: email subject test
email MSG: this is the email body

讨论

  • 我假设您的数据是 data.txt
  • 这里的主力是keyedlist2dict,在那里我拿出一个键入的列表并将其展平成为字典。
    • 在此过程中,如果值不是嵌套键控列表,我只需将键和值附加到字典
    • 如果该值确实是嵌套的键控列表,那么我递归调用keyedlist2dict
    • 看看输出,你会看到我如何形成新的密钥
  • 此脚本需要TCL 8.5或更高版本

更新

我对标记为 TCL 8.4 的两行进行了更改。该脚本现在应该适用于TCL 8.4系统。

答案 1 :(得分:1)

这看起来像是一个TclX键控列表的列表,这是早期尝试做现代Tcl对词典的处理。键控列表很好地嵌套 - 这是一棵树,而不是一个表 - 所以映射到CSV将不是最有效的,但它们的语法是最简单的处理它们的方法是使用TclX代码。

预赛:

package require TclX
package require csv;        # From Tcllib

列出我们感兴趣的列。注意名称的.分隔位。

set columns {
    TOKEN.NAME TOKEN.GROUPS TOKEN.VALUE TOKEN.REPEATING.MAX TOKEN.REPEATING.TIME
    TOKEN.WINDOW TOKEN.HOST TOKEN.ACTION.EXEC TOKEN.ACTION.email.FROM
    TOKEN.ACTION.email.TO TOKEN.ACTION.email.SUBJ TOKEN.ACTION.email.MSG
}
# Optionally, put a header row in:
puts [csv::join $columns]

将实际数据加载到Tcl:

set f [open "thefile.dta"]
set data [read $f]
close $f

迭代列表,提取信息,然后以CSV格式发送到stdout:

foreach item $data {
    # Ugly hack to munge data into real TclX format
    set item [list [list [lindex $item 0] [lrange $item 1 end]]]
    set row {}
    foreach label $columns {
        if {![keylget item $label value]} {set value ""}
        lappend row $value
    }
    puts [csv::join $row]
}

或类似的东西。

答案 2 :(得分:1)

我意识到这已经有几个月了,但我发现你正在尝试解析Cloverleaf配置文件(这就是我自己偶然发现的错误)。

对于其他任何尝试做类似事情的人来说,实际上有可用于处理Cloverleaf提供的库,但文档中没有提及它们。

查看$ HCIROOT / tcl / lib / cloverleaf。处理警报配置看起来像是在configIO.tlib中。 NetConfig的内容在nci.tlib和netData.tlib中。

答案 3 :(得分:0)

您可以将数据视为普通列表并逐行读取。 info complete命令有助于此:

set fh [open your.file r]
while {[gets $fh line] != -1} {
    append kl $line
    if {[info complete $kl]} {
        lappend lists $kl
        set kl ""
    }
}
close $fh
puts [llength $lists]                ;# 2
puts [llength [lindex $lists 0]]     ;# 1
puts [llength [lindex $lists 0 0]]   ;# 7
puts $lists
  

{{TOKEN {NAME {此令牌的名称}} {GROUPS {Group 1}} {VALUE value} {REPEATING {{MAX 3} {TIME {nmin 30}}}} {WINDOW / / * / *} {ACTION {{EXEC {code to run here}}}}}} {{TOKEN {NAME {name of next token}} {GROUPS {Group 1}} {VALUE value} {WINDOW 0 / 0:30-2:00,3:30-7:30 / / } {HOST {localhost}} {行动{{email {{FROM cloverleaf@healthvision.com} {TO me @ xxxx .org} {SUBJ {email subject test}} {MSG {this is the email body}}}}}}}}