Assaf Mohaban
Assaf Mohaban

Reputation: 13

how to split a file to list of lists TCL

I'm coding TCL and I would like to split a file into two lists of lists, the file contain:

(1,2) (3,4) (5,6)
(7,8) (9,10) (11,12)

and I would like to get two list one for each line, that contain lists that each one contain to two number

for example:

puts $list1                 #-> {1 2} {3 4} {5 6}
puts [lindex $list1 0]      #-> 1 2
puts [lindex $list2 2]      #-> 11 12

I tried to use regexp and split but no success

Upvotes: 1

Views: 2802

Answers (3)

M. D. P
M. D. P

Reputation: 764

The code works for windows :

TCL file code is :

proc captureImage {} {

	#open the image config file.
	set configFile [open "C:/main/image_config.txt" r] 
	
	#To retrive the values from the config file.
	while {![eof $configFile]} {
		set part [split [gets $configFile] "="]
		set props([string trimright [lindex $part 0]]) [string trimleft [lindex $part 1]]
	}
	close $configFile 

	set time [clock format [clock seconds] -format %Y%m%d_%H%M%S]
	set date [clock format [clock seconds] -format %Y%m%d]
	
	#create the folder with the current date
	set folderPath $props(folderPath)
	append folderDate $folderPath "" $date "/"
	set FolderCreation [file mkdir $folderDate]
	while {0} {
    if { [file exists $date] == 1} {               
    }
   	break
    }
	
	#camera selection to capture image.
	set camera "video"
	append cctv $camera "=" $props(cctv)
	
	#set the image resolution (XxY).	
	set resolutionX $props(resolutionX)
	set resolutionY $props(resolutionY)
	append resolution $resolutionX "x" $resolutionY
	
	#set the name to the save image
	set imagePrefix $props(imagePrefix)
	set imageFormat $props(imageFormat)
	append filename $folderDate "" $imagePrefix "_" $time "." $imageFormat
	
	set logPrefix "Image_log"
	append logFile $folderDate "" $logPrefix "" $date ".txt"
	
	
 
	#ffmpeg command to capture image in background
	exec ffmpeg -f dshow -benchmark -i $cctv -s $resolution $filename >& $logFile &
	
	after 3000
	
	}
  
}
captureImage

thext file code is :

cctv=Integrated Webcam

resolutionX=1920
resolutionY=1080

imagePrefix=ImageCapture
imageFormat=jpg
folderPath=c:/test/

//camera=video=Integrated Webcam,Logitech HD Webcam C525

This code works for me me accept the code from text file were list of parameters are passed.

Upvotes: -1

Peter Lewerin
Peter Lewerin

Reputation: 13282

You have an answer already, but it can actually be done a little bit simpler (or at least without regexp, which is usually a good thing).

Like Donal, I'll assume this to be the text read from a file:

set lines "(1,2) (3,4) (5,6)\n(7,8) (9,10) (11,12)\n"

Clean it up a bit, removing the parentheses and any white space before and after the data:

% set lines [string map {( {} ) {}} [string trim $lines]]
1,2 3,4 5,6
7,8 9,10 11,12

One way to do it with good old-fashioned Tcl, resulting in a cluster of variables named lineN, where N is an integer 1, 2, 3...:

set idx 0
foreach lin [split $lines \n] {
    set res {}
    foreach li [split $lin] {
        lappend res [split $li ,]
    }
    set line[incr idx] $res
}

A doubly iterative structure like this (a number of lines, each having a number of pairs of numbers separated by a single comma) is easy to process using one foreach within the other. The variable res is used for storing result lines as they are assembled. At the innermost level, the pairs are split and list-appended to the result. For each completed line, a variable is created to store the result: its name consists of the string "line" and an increasing index.

As Donal says, it's not a good idea to use clusters of variables. It's much better to collect them into an array (same code, except for how the result variable is named):

set idx 0
foreach lin [split $lines \n] {
    set res {}
    foreach li [split $lin] {
        lappend res [split $li ,]
    }
    set line([incr idx]) $res
}

If you have the results in an array, you can use the parray utility command to list them in one fell swoop:

% parray line
line(1) = {1 2} {3 4} {5 6}
line(2) = {7 8} {9 10} {11 12}

(Note that this is printed output, not a function return value.)

You can get whole lines from this result:

% set line(1)
{1 2} {3 4} {5 6}

Or you can access pairs:

% lindex $line(1) 0
1 2
% lindex $line(2) 2
11 12

If you have the lmap command (or the replacement linked to below), you can simplify the solution somewhat (you don't need the res variable):

set idx 0
foreach lin [split $lines \n] {
    set line([incr idx]) [lmap li [split $lin] {
        split $li ,
    }]
}

Still simpler is to let the result be a nested list:

set lineList [lmap lin [split $lines \n] {
    lmap li [split $lin] {
        split $li ,
    }
}]

You can access parts of the result similar to above:

% lindex $lineList 0
{1 2} {3 4} {5 6}
% lindex $lineList 0 0
1 2
% lindex $lineList 1 2
11 12

Documentation: array, foreach, incr, lappend, lindex, lmap (for Tcl 8.5), lmap, parray, set, split, string

Upvotes: 1

Donal Fellows
Donal Fellows

Reputation: 137787

The idea of using regexp is good, but you'll need to do some post-processing on its output.

# This is what you'd read from a file
set inputdata "(1,2) (3,4) (5,6)\n(7,8) (9,10) (11,12)\n"

foreach line [split $inputdata "\n"] {
    # Skip empty lines.
    # (I often put a comment format in my data files too; this is where I'd handle it.)
    if {$line eq ""} continue

    # Parse the line.
    set bits [regexp -all -inline {\(\s*(\d+)\s*,\s*(\d+)\s*\)} $line]
    # Example results of regexp:
    #     (1,2) 1 2 (3,4) 3 4 (5,6) 5 6

    # Post-process to build the lists you really want
    set list([incr idx]) [lmap {- a b} $bits {list $a $b}]
}

Note that this is building up an array; long experience says that calling variables list1, list2, …, when you're building them in a loop is a bad idea, and that an array should be used, effectively giving variables like list(1), list(2), …, as that yields a much lower bug rate.


An alternate approach is to use a simpler regexp and then have scan parse the results. This can be more effective when the numbers aren't just digit strings.

foreach line [split $inputdata "\n"] {
    if {$line eq ""} continue
    set bits [regexp -all -inline {\([^()]+\)} $line]
    set list([incr idx]) [lmap substr $bits {scan $substr "(%d,%d)"}]
}

If you're not using Tcl 8.6, you won't have lmap yet. In that case you'd do something like this instead:

foreach line [split $inputdata "\n"] {
    if {$line eq ""} continue
    set bits [regexp -all -inline {\(\s*(\d+)\s*,\s*(\d+)\s*\)} $line]
    set list([incr idx]) {}
    foreach {- a b} $bits {
        lappend list($idx) [list $a b]
    }
}
foreach line [split $inputdata "\n"] {
    if {$line eq ""} continue
    set bits [regexp -all -inline {\([^()]+\)} $line]
    set list([incr idx]) {}
    foreach substr $bits {
        lappend list($idx) [scan $substr "(%d,%d)"]
        # In *very* old Tcl you'd need this:
        #    scan $substr "(%d,%d)" a b
        #    lappend list($idx) [list $a $b]
    }
}

Upvotes: 3

Related Questions