Prev: Tcl and MATLAB (Was: Re: Please help : suppress error message from exec
Next: exec - problem with stderr
From: Uwe Klein on 16 Jul 2010 11:50 Sooraj S wrote: > Thanks for your help. Now i want to read and write to the same file. I > want to get alex's current number and change it to 33333 > > my_code > ======== > set user "alex" > set new_num "33333" > > set $in [open "data.txt" r+] > while {[gets $in line] >= 0 } { > if {[string match $user:* $line] == 1} { > set len [string length $user] > set old_num [string replace $line 0 $len ] > puts "Old number :$old_num" //this > will print 11111 > > # here i want to write $new_num to the file...how will i get > the index# > } > } > close $in following our conversation I would not be too surprised if this is a homework assignment? uwe
From: Bruce on 16 Jul 2010 13:23 Sooraj S wrote: > Thanks for your help. Now i want to read and write to the same file. I > want to get alex's current number and change it to 33333 > > my_code > ======== > set user "alex" > set new_num "33333" > > set $in [open "data.txt" r+] > while {[gets $in line] >= 0 } { > if {[string match $user:* $line] == 1} { > set len [string length $user] > set old_num [string replace $line 0 $len ] > puts "Old number :$old_num" //this > will print 11111 > > # here i want to write $new_num to the file...how will i get > the index# > } > } > close $in OK, you have 2 options, read everything into memeory, update any values you want, write the whole thing out. or Read each line, modify if necessary, write out the line to temp file, and when done, copy temp file over original file. Unless the file is many gigabytes, it's easier to read it all in on go (imho) here is a quick version... # read data as a blob set in [open "data.txt" r] set data [read $in] close $in # parse data blob foreach line [split $data \n] { foreach {user num} [split $line :] {break} set UserMap([string trim $user]) [string trim $num] } #now UserMap is a associative array with all users mapped to number #modify at will # - delete old user unset UserMap($someOldName) # - add new user set UserMap($newUser) $newNumber # modify user set UserMap($currUser) $modNumber # write data back out set out [open "data.txt" w] foreach {user number} [array get UserMap] { puts $out "$user: $num" } close $out # or write sorted version set out [open "data.txt" w] foreach user [lsort [array names UserMap]] { puts $out "$user: $UserMap($user)" } Bruce
From: Bezoar on 18 Jul 2010 03:24 On Jul 16, 12:23 pm, Bruce <Bruce_do_not_...(a)example.com> wrote: > Sooraj S wrote: > > Thanks for your help. Now i want to read and write to the same file. I > > want to get alex's current number and change it to 33333 > > > my_code > > ======== > > set user "alex" > > set new_num "33333" > > > set $in [open "data.txt" r+] > > while {[gets $in line] >= 0 } { > > if {[string match $user:* $line] == 1} { > > set len [string length $user] > > set old_num [string replace $line 0 $len ] > > puts "Old number :$old_num" //this > > will print 11111 > > > # here i want to write $new_num to the file...how will i get > > the index# > > } > > } > > close $in > > OK, you have 2 options, read everything into memeory, > update any values you want, write the whole thing out. > > or > > Read each line, modify if necessary, write out the > line to temp file, and when done, copy temp file > over original file. > > Unless the file is many gigabytes, it's easier to read > it all in on go (imho) > > here is a quick version... > > # read data as a blob > set in [open "data.txt" r] > set data [read $in] > close $in > > # parse data blob > foreach line [split $data \n] { > foreach {user num} [split $line :] {break} > set UserMap([string trim $user]) [string trim $num]} > > #now UserMap is a associative array with all users mapped to number > > #modify at will > # - delete old user > unset UserMap($someOldName) > # - add new user > set UserMap($newUser) $newNumber > # modify user > set UserMap($currUser) $modNumber > > # write data back out > set out [open "data.txt" w] > foreach {user number} [array get UserMap] { > puts $out "$user: $num"} > > close $out > > # or write sorted version > set out [open "data.txt" w] > foreach user [lsort [array names UserMap]] { > puts $out "$user: $UserMap($user)" > > } > > Bruce Bruce's method is fine but assumes that all lines will be strictly user:id on each line comments or other formats are not handled nor is the original ordering of the file. Using regsub can help you there as follows. #!/bin/sh # the next line restarts using wish \ exec /opt/usr8.6b.2/bin/tclsh8.6 "$0" ${1+"$@"} # regsub returns number of substitutions so will only be 1 when it hits # the user. Regexp used will handle leading and trailing spaces and tabs around user # and the id . Also handles any false alarms e.g if chuck is your user then achuck:2344 # wont match. Any line like a comment will not match regex and line will # be unchanged and be written to the temp file. The order is maintained proc changeUser { user newid file } { set retval 0; # 0 means did not find user in file 1 means successfully changed user set fd [ open $file r 0666] set ofd [ open ${file}.new w 0666] ; # put catches around these two open calls to return file # system errors set reg [subst -nocommands {^[ \t]*($user)[ \t]*: [ \t]*([0-9]+)} ] while { ![eof $fd ] && [ gets $fd line] != -1 } { if { [regsub $reg $line "\\1:$newid" line ] == 1 } { set retval 1 # you could break out of loop here and just write each line to outfile # to avoid the regexp call if you know your user appears only once. } puts $ofd $line } close $ofd close $fd if { [ catch { file copy -force -- ${file}.new $file } err ] != 0 } { set retval 0; # could not copy file } return $retval } # test set fd [ open testfile w ] set origbuff " adrian:2345 achuck : 2322 chuck : 2322 andrea:23443 #this is a comment // could chuck also be this" set expectedbuff " adrian:2345 chuck:1111 andrea:23443 #this is a comment // could chuck also be this " puts $fd $origbuff close $fd if { [changeUser chuck 1111 testfile ] } { puts "Change successful" } else { puts "Change not successful" } # lets check to be sure set fd [ open testfile r ] set buffer [read $fd ] if { $buffer eq $expectedbuff } { puts "It really did work" } else { puts "It really DID NOT work" puts "expected:\n$expectedbuff\n====\nnewbuff:\n$buffer\n========" } file delete testfile Bezoar
From: Bezoar on 18 Jul 2010 03:43 On Jul 18, 2:24 am, Bezoar <cwjo...(a)gmail.com> wrote: > On Jul 16, 12:23 pm, Bruce <Bruce_do_not_...(a)example.com> wrote: > > > > > Sooraj S wrote: > > > Thanks for your help. Now i want to read and write to the same file. I > > > want to get alex's current number and change it to 33333 > > > > my_code > > > ======== > > > set user "alex" > > > set new_num "33333" > > > > set $in [open "data.txt" r+] > > > while {[gets $in line] >= 0 } { > > > if {[string match $user:* $line] == 1} { > > > set len [string length $user] > > > set old_num [string replace $line 0 $len ] > > > puts "Old number :$old_num" //this > > > will print 11111 > > > > # here i want to write $new_num to the file...how will i get > > > the index# > > > } > > > } > > > close $in > > > OK, you have 2 options, read everything into memeory, > > update any values you want, write the whole thing out. > > > or > > > Read each line, modify if necessary, write out the > > line to temp file, and when done, copy temp file > > over original file. > > > Unless the file is many gigabytes, it's easier to read > > it all in on go (imho) > > > here is a quick version... > > > # read data as a blob > > set in [open "data.txt" r] > > set data [read $in] > > close $in > > > # parse data blob > > foreach line [split $data \n] { > > foreach {user num} [split $line :] {break} > > set UserMap([string trim $user]) [string trim $num]} > > > #now UserMap is a associative array with all users mapped to number > > > #modify at will > > # - delete old user > > unset UserMap($someOldName) > > # - add new user > > set UserMap($newUser) $newNumber > > # modify user > > set UserMap($currUser) $modNumber > > > # write data back out > > set out [open "data.txt" w] > > foreach {user number} [array get UserMap] { > > puts $out "$user: $num"} > > > close $out > > > # or write sorted version > > set out [open "data.txt" w] > > foreach user [lsort [array names UserMap]] { > > puts $out "$user: $UserMap($user)" > > > } > > > Bruce > > Bruce's method is fine but assumes that all lines will be strictly > user:id on each line comments or other formats are not handled nor is > the original ordering of the file. Using regsub can help you there as > follows. > > #!/bin/sh > # the next line restarts using wish \ > exec /opt/usr8.6b.2/bin/tclsh8.6 "$0" ${1+"$@"} > # regsub returns number of substitutions so will only be 1 when it > hits > # the user. Regexp used will handle leading and trailing spaces > and tabs around user > # and the id . Also handles any false alarms e.g if chuck is your > user then achuck:2344 > # wont match. Any line like a comment will not match regex and > line will > # be unchanged and be written to the temp file. The order is > maintained > > proc changeUser { user newid file } { > set retval 0; # 0 means did not find user in file 1 means > successfully changed user > set fd [ open $file r 0666] > set ofd [ open ${file}.new w 0666] ; # put catches around these > two open calls to return file > # system errors > set reg [subst -nocommands {^[ \t]*($user)[ \t]*: > [ \t]*([0-9]+)} ] > while { ![eof $fd ] && [ gets $fd line] != -1 } { > if { [regsub $reg $line "\\1:$newid" line ] == 1 } { > set retval 1 > # you could break out of loop here and just write each line to > outfile > # to avoid the regexp call if you know your user appears > only once. > } > puts $ofd $line > } > close $ofd > close $fd > if { [ catch { file copy -force -- ${file}.new $file } err ] != > 0 } { > set retval 0; # could not copy file > } > return $retval > > } > > # test > > set fd [ open testfile w ] > set origbuff " adrian:2345 > achuck : 2322 > chuck : 2322 > andrea:23443 > #this is a comment > // could chuck also be this" > set expectedbuff " adrian:2345 > chuck:1111 > andrea:23443 > #this is a comment > // could chuck also be this > " > puts $fd $origbuff > close $fd > > if { [changeUser chuck 1111 testfile ] } { > puts "Change successful"} else { > > puts "Change not successful"} > > # lets check to be sure > set fd [ open testfile r ] > set buffer [read $fd ] > if { $buffer eq $expectedbuff } { > puts "It really did work"} else { > > puts "It really DID NOT work" > puts "expected:\n$expectedbuff\n====\nnewbuff:\n$buffer\n========"} > > file delete testfile > > Bezoar I should add that if the file is small ( a relative measure that varies by system and how much memory you've got) you could change the changeUser like so. Where the whole file is read in and regsub is run once ( not the -line option on regsub) : proc changeUser { user newid file } { set retval 0; # 0 means did not find user in file 1 means successfully changed user set fd [ open $file r 0666] set buffer [read $fd]; close $fd set reg [subst -nocommands {^[ \t]*($user)[ \t]*: [ \t]*([0-9]+)} ] if { [regsub -line -- $reg $buffer "\\1:$newid" buffer] } { set ofd [ open ${file} w 0666] ; puts -nonewline $ofd $buffer close $ofd set retval 1 } return $retval } Bezoar
First
|
Prev
|
Pages: 1 2 Prev: Tcl and MATLAB (Was: Re: Please help : suppress error message from exec Next: exec - problem with stderr |