我在代码的底部提供了一个简短的XML文档,这正是我需要解析的内容,除了10个参数,以及大约10,000" cmds"。我正在解析文档,并将其写入.txt文件。我将节点的值打印到.txt中:
CMD_ID Param_1_Name Param_1_Value Param_N_Name Param_N_Value ...
脚本似乎只打印最后一个参数名称和值,而不是每个。我假设需要实现一个foreach循环,但我已经失去了它应该在哪里。这是我的TCL脚本:
t1 = getTime();
for(int i = 0; i < 9999999; i++)
{}
t2 = getTime();
输出结果为:
CMD_ID_1 Param_4 ON/OFF CMD_ID_2 Param_4 ON/OFF
答案 0 :(得分:0)
您每次都会在以下代码中覆盖数组
# Here, you are overwriting the 'type' variable index of array
switch -- $type {
id - name - value {set g($type) $str}
}
因此保留了最后的条目。
相反,您可以维护一个数组,其中索引以命令名为前缀,如
CMD_ID_1,name
CMD_ID_1,value
.
.
.
CMD_ID_N,name
CMD_ID_N,value
对于每个索引,追加名称和值......比如,
::g(CMD_ID_1,name) = Param_1 Param_2 Param_3 Param_4
::g(CMD_ID_1,value) = ON/OFF ON/OFF ON/OFF ON/OFF
::g(CMD_ID_2,name) = Param_1 Param_2 Param_3 Param_4
::g(CMD_ID_2,value) = ON/OFF ON/OFF ON/OFF ON/OFF
您应该做的最后一件事是跟踪命令名称。所以,只需要一个变量来保存命令的名称,你就可以使用变量的前缀循环数组。
<强> extractXMLInfo.tcl 强>
package require tdom
proc parse xml {
set ::S {}; # Global stack ::S maintained to track tag hierarchy
set p [expat -elementstartcommand els \
-characterdatacommand ch \
-elementendcommand ele ]
if [catch {$p parse $xml} res] {
puts "Error: $res" ; # Error catch and put
}
}
#---- Callbacks for start, end, character data
proc els {name atts} {
lappend ::S $name ; # PUSH - els pushes current tag name
# No need of the below statement - Dinesh
#if {$name eq "cmd"} {array unset ::g}; # When cmd element ends, g reset
}
proc ele name {
global g
set ::S [lrange $::S 0 end-1] ; # POP - ele pops current tag name
# No need of the below statement - Dinesh
# if {$name eq "cmd"} {
# puts $g(id)\t$g(name)\t$g(value)
# }
}
proc ch str {
global g
set type [lindex $::S end]
set current_cmd_name {}
switch -- $type {
id {
if {[lsearch $::cmd_id $str]==-1} {
lappend ::cmd_id $str
}
}
name -
value {
set current_cmd_name [lindex $::cmd_id end]
if {$current_cmd_name eq {}} {
puts "ERROR : No command name found"
} else {
lappend g($current_cmd_name,$type) $str
}
}
}
}
# Variable to track the command's names.
set ::cmd_id {}
array set ::g {}
parse "
<cmds>
<cmd>
<id>CMD_ID_1</id>
<params>
<param>
<name>Param_1</name>
<value>ON/OFF</value>
</param>
<param>
<name>Param_2</name>
<value>ON/OFF</value>
</param>
<param>
<name>Param_3</name>
<value>ON/OFF</value>
</param>
<param>
<name>Param_4</name>
<value>ON/OFF</value>
</param>
</params>
</cmd>
<cmd>
<id>CMD_ID_2</id>
<params>
<param>
<name>Param_1</name>
<value>ON/OFF</value>
</param>
<param>
<name>Param_2</name>
<value>ON/OFF</value>
</param>
<param>
<name>Param_3</name>
<value>ON/OFF</value>
</param>
<param>
<name>Param_4</name>
<value>ON/OFF</value>
</param>
</params>
</cmd>
</cmds>"
parray ::g
puts "cmd_ids : $::cmd_id"
# Looping through the commands and printing the
# name and value pairs
foreach cmd $::cmd_id {
puts =========================================
puts "Command : $cmd"
foreach name $::g($cmd,name) value $::g($cmd,value) {
puts "$name = $value"
}
}
输出
::g(CMD_ID_1,name) = Param_1 Param_2 Param_3 Param_4
::g(CMD_ID_1,value) = ON/OFF ON/OFF ON/OFF ON/OFF
::g(CMD_ID_2,name) = Param_1 Param_2 Param_3 Param_4
::g(CMD_ID_2,value) = ON/OFF ON/OFF ON/OFF ON/OFF
cmd_ids : CMD_ID_1 CMD_ID_2
=========================================
Command : CMD_ID_1
Param_1 = ON/OFF
Param_2 = ON/OFF
Param_3 = ON/OFF
Param_4 = ON/OFF
=========================================
Command : CMD_ID_2
Param_1 = ON/OFF
Param_2 = ON/OFF
Param_3 = ON/OFF
Param_4 = ON/OFF
答案 1 :(得分:0)
一种可能的方式:
在ele
内的第20行,替换
puts $g(id)\t$g(name)\t$g(value)
与
foreach name $g(name) value $g(value) {
puts $g(id)\t$name\t$value
}
在ch
内的第27行,替换
id - name - value {set g($type) $str}
与
id - name - value {lappend g($type) $str}
set
命令会覆盖之前的值,lappend
会收集它们。然后,您只需要迭代这些值以将其打印出来。
<强>附录强>
以下代码在某种程度上简化了您的逻辑,至少适用于您的测试数据:
proc parse xml {
global data
set p [expat -characterdatacommand ch -elementendcommand ele]
$p parse $xml
dict for {id values} $data {
foreach {name value} $values {
puts $id\t$name\t$value
}
}
}
proc ele name {
global data idx str
switch $name {
id {
set idx $str
}
name -
value {
dict lappend data $idx $str
}
}
}
proc ch data {
global str
set str $data
}
(请注意,此代码取决于始终位于name
元素之前的value
元素。它非常可能,但需要更多参与,以使代码独立于此。 )
附录2
全球比赛很糟糕。面向对象的解决方案更整洁:
package require tdom
oo::class create ParserHelper {
variable p str idx data
constructor args {
set data {}
set p [expat \
-characterdatacommand [namespace code {my characterdata}] \
-elementendcommand [namespace code {my elementend}]]
}
destructor {
catch {$p free}
}
method parse xml {
$p reset
$p parse $xml
}
method characterdata s {
set str $s
}
method elementend {s args} {
switch $s {
id {
set idx $str
}
name -
value {
dict lappend data $idx $str
}
}
}
method puts {} {
dict for {id values} $data {
foreach {name value} $values {
puts $id\t$name\t$value
}
}
}
}
ParserHelper create ph
ph parse $xml
ph puts
我现在停止修补它......
文档:dict,foreach,global,my,namespace,oo::class,oo::define,{{3 }},proc,puts,set