Hint :use regsub to replace all your "begin foobar"s by "foobar {",
and all your "end"s by "}".
Then use [eval].
-Alex
It's just that I'm not used to it; I like the genericity and power of
the re engine.
Moreover, I understand there are optimizations under the hood for the
most common cases.
So quite possibly the [regsub] variant would run just as fast as the
[string map]...
(no flames please, I didn't check).
In addition, the added power of regexps can be put to good use by the
OP in more complicated situations (like when the number of spaces
between words is not guaranteed constant).
So, as you can see, no single good reason, just a vector of them ;-)
-Alex
I'm glad I raised the question, because it's appropriate to
mention that Tcl's REs are particularly trustworthy <URL:
http://www.unixreview.com/documents/s=10121/ur0702e/ >; while
I hadn't thought of it when I first wrote, this is a nice
example of how focus on the more general style--RE, in this
case--is good programming, because the implementation itself
can take care of salient optimizations. Good point.
On the other hand, REs intimidate some newcomers, who find
[string map] (for example) friendlier. I don't know if that's
the case for sowmya.
My immediate motivation, though, was that you counseled *two*
substitutions; for my use of Tcl, [string map] is the more
idiomatic approach when substitutions are multiple.
Oh, you're quite right. Running several [regsub]'s in sequence tends
to be largely slower than the equivalent [string map] (though you can
offset that effect with the "|" operator inside regexps).
However, there's an alternative: approximate by a superlanguage, IOW
be overgenerative.
Loosely speaking, the larger the language, the smaller the automaton.
For the example at hand, the pair of calls
regsub -all {Begin([A-Z][A-Za-z0-9]+)} $s "my_\\1\{" s
regsub -all {End[A-Z][A-Za-z0-9]+} $s "\}" s
handles any number of Begin/End varieties in just two scans...
Of course doing this delays some checking (the superlanguage *is* an
approximation), but it's trivial to do the checks afterwards (e.g.
with [unknown] to catch unexpected BeginXYZ, mapped to my_XYZ above).
-Alex
It should be noted at this point that [string map] will generate a
slightly different looking list. So the choice also in part depends on
how easy it is to process the resulting list:
regsub -all {Begin([A-Z][A-Za-z0-9]+)} $s "\\1 \{" s
regsub -all {End[A-Z][A-Za-z0-9]+} $s "\}" s
result:
Case:1 {
List:1 {
Stress
}
List:3 {
Stress|(Min)|VonMises
}
List:8 {
Stress
}
List:9 {
Stress
}
List:12 {
Stress
}
}
set s [string map [list Begin "{" End* "}"] $s]
result:
{Case:1
{List:1
Stress
}
{List:3
Stress|(Min)|VonMises
}
{List:8
Stress
}
{List:9
Stress
}
{List:12
Stress
}
}
The regsub solution generates a clean looking two-level nested dict.
The string map solution on the other hand, due to not being able to
extract \1, is slightly messier but managable if we use:
Here first i should list all the Begincase Ids, then all the BeginList
ids for the each Begincase, then the contents between each beginlist
and endlist in one variable. so then i need to pass all these to
another procedure...
say...
case list type entity
method ids
1 1 stress -
- -
3 Stress|(Min)|VonMises -
- -
8 stress element ByID
12,14, 67
9 stress -
- -
12 stress -
- -
2 all displacement nodes ByID
1,45,67,104-106
Strain Energy|Strain Energy -
- -
train Energy|Energy Density -
- -
3 1 Displacement -
- -
Please anyone help me in this regards... can i first store all case,
list, type, entity,method and ids in a different list (each list for
each variable to store its contents) and then all in the array!!! Its
ver urgent.. If anyone could me help out in this!!!
set data {BeginCase:1
## read the file
proc retrieve { file } {
set filename ""
set ch [open $filename]
set data [read $ch]
close $ch
return $file
}
## returns a list like:
##
## {
## caseid0
## {
## listid0
## { contentline0 contentline1 ... }
## listid1
## { contentline0 }
## }
## ...
## }
##
proc parse { data } {
## the thing
set result [list ]
## temp
set sublist [list ]
set contentlist [list ]
set case 0 ; ## 0 begincase, 1 begincase, 2 content
foreach line [split $data "\n"] {
set line [string trimleft $line]
if {"EndCase"eq$line} then {
lappend result $sublist
set sublist [list ]
incr case -1
continue
}
if {"EndList"eq$line} then {
lappend sublist $contentlist
set contentlist [list ]
incr case -1
continue
}
## data handling
::switch $case {
0 {
## i should be a BeginCase
## assertion
set splited [split $line ":" ]
if {"BeginCase"ne[lindex $splited 0]} then {
## silently ignore lines,
## until we find something useful
continue
}
## in case we have an id with collon
set caseId [join [lrange $splited 1 end ] ":" ]
## adding the data
lappend result $caseId
incr case
}
1 {
## i should be a BeginList
## assertion
set splited [split $line ":" ]
if {"BeginList"ne[lindex $splited 0]} then {
## die hard
return -code error "EXPECTED_BEGINLIST"
}
## in case we have an id with collon
set listId [join [lrange $splited 1 end ] ":" ]
lappend sublist $listId
::incr case
}
2 {
## ignore empty lines
::if {""eq$line} then {
continue
}
## i'm a Contentline
lappend contentlist $line
}
default {
::return -code error "NO_SUCH_CASE"
}
}
}
return $result
}
##
## testing if parse works and to show how to access
## the parsed data
##
proc test { data } {
## here goes your handling
foreach [list id sub ] [parse $data] {
foreach [list lid contents ] $sub {
::foreach line $contents {
::puts "$id :: $lid :: $line"
}
}
}
}
##
## Or you could spawn your calls in case 2 directly e.g.
## like this.
##
## just pass write a callback that better fits your need
##
proc out { case list type } {
puts "$case :: $list :: $type "
}
proc pass { data { callback out } } {
set case 0 ; ## 0 begincase, 1 begincase, 2 content
foreach line [split $data "\n"] {
set line [string trimleft $line]
if {"EndCase"eq$line} then {
incr case -1
continue
}
if {"EndList"eq$line} then {
incr case -1
continue
}
## data handling
::switch $case {
0 {
## i should be a BeginCase
## assertion
set splited [split $line ":" ]
if {"BeginCase"ne[lindex $splited 0]} then {
## silently ignore lines,
## until we find something useful
continue
}
## in case we have an id with collon
set caseId [join [lrange $splited 1 end ] ":" ]
incr case
}
1 {
## i should be a BeginList
## assertion
set splited [split $line ":" ]
if {"BeginList"ne[lindex $splited 0]} then {
## die hard
return -code error "EXPECTED_BEGINLIST"
}
## in case we have an id with collon
set listId [join [lrange $splited 1 end ] ":" ]
incr case
}
2 {
## ignore empty lines
if {""eq$line} then {
continue
}
## i'm a Contentline
## here goes your calling
$callback $caseId $listId $line
}
default {
::return -code error "NO_SUCH_CASE"
}
}
}
return
}
> Please anyone help me in this regards... can i first store all case,
> list, type, entity,method and ids in a different list (each list for
> each variable to store its contents) and then all in the array!!! Its
> ver urgent.. If anyone could me help out in this!!!
You didn't like the other replies, did you ?
Okay, let's do it for you.. this time :-)
regsub -all {Begin([A-Z][a-z]*):([0-9A-Za-z()]+)} $data "my_\\1 \
\2 \{" data
regsub -all {End[A-Z][a-z]*} $data "\}" data
set ids {}
proc my_Case {id code} {lappend ::ids $id;set ::thecase $id;eval
$code}
proc my_List {id code} {lappend ::lists($::thecase)
$id;set ::codes($::thecase,$id) $code}
eval $data
Simple, eh ?
To convince yourself that it works:
puts "ids=$ids"
parray lists
parray codes
-Alex
I tried working like this, Its giving an error for incr case -1, could
U please check and let me know.. which is the way to do this.. Its
very urgent.
> I tried working like this, Its giving an error for incr case -1, could
> U please check and let me know.. which is the way to do this.. Its
> very urgent.
Two suggestions:
1. provide us the output from the following:
a. parray tcl_platform
b. info patchlevel
2. provide us the exact error msg you are getting .