Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations IamaSherpa on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

File modifications 1

Status
Not open for further replies.

pankajpanjwani

Technical User
Mar 25, 2002
24
EU
Hi Experts,
I am trying to write a small program in tcl which uses a file as database. I am trying to modify a particular line in that file(can be anywhere in that file). What I want to do is take a line from that file and modify that without disturbing the rest of file. Can anyone help me with this.
thanks in anticipation
-Pankaj
 
Well, here's what I do in similar applications, although I make no claims to elegance. If the file is small enough (whatever that means in your environment), I read the whole file into a list:
set fileid [open <filename> r]
set filst [split [read $fileid] \n]
close $fileid
Then I do whatever manipulation on the list, filst, and then write the whole thing back:
set fileid [open <filename> w]
foreach line $filst {puts $line}
close $fileid

If your file is too big for that, I would read and write each line to a temp file until the right line is reached, manipulate the line, write it out to the temp file, then read and write the rest of the file, copy the temp file back onto the original file, and delete the temp file:

set inid [open <filename> r]
set outid [open <temp filename> w]
set conditionflag 0
while {$conditionflag ==0} {
set line [gets $inid]
if {!whatever your condition is} {
set conditionflag 1
} else {
puts $outid $line
}
}
<do whatever to the line>
puts $outid $line
while {![eof $inid]} {
set line [gets $inid]
puts $outid $line
}
close $inid
close $outid
file copy -force <temp filename> <filename>
file delete <temp filename>

Or something like that Bob Rashkin
rrashkin@csc.com
 
The technique that Bong described, possibly modified to suit your application, is what you need to do. Tcl's not unique in that aspect. It's just the nature of disk I/O. Consider that if you're inserting an extra character in the middle of a file, all of the characters following it need to get &quot;pushed down&quot; one byte (assuming 8-bit character widths :) ). That movement of all of those bytes doesn't happen by magic; some program somewhere has to read the characters in and then write them out to their new location. This functionality isn't provided as part of the standard I/O libraries for disk access, so it's your program that has to take care of it.

By the way, a flat file is a standard and convenient way of storing information. But depending on the type of information you're storing, and the frequency in which your program will be querying and/or modifying it, you might consider using the MetaKit database instead of a flat file. MetaKit is a fast, lightweight (approx. 100KB shared library) database that uses compact, cross-platform data files. It's freely available in open source, and has Tcl, C++, and Python language bindings. You can read a bit more about it at the Tcl'ers Wiki ( on the page &quot;MetaKit,&quot; as well as the MetaKit homepage, - Ken Jones, President
Avia Training and Consulting
866-TCL-HELP (866-825-4357) US Toll free
415-643-8692 Voice
415-643-8697 Fax
 
Thanks for the tip on MetaKit avia< I have been searching for something like this. :)
I looked at rdb, but they wanted $300 plus for a single site liscense (or close to it) for a shell interface to a flat file based DB. A little high. Have you worked with
Metakit as a backend for web services?

pankajpanjwani,

I have a little script that approximates what bong was talking about, it is not very elegant either, but..

#!/usr/bin/tclsh

set all_mats {}

proc fwline {file pat {p &quot;0&quot;}} {
global all_mats
set i 0
if {$p == 0} {
puts &quot;Inside id run.&quot;
catch {set fd [open &quot;$file&quot; r+]} err_opened
while {![eof $fd] && [gets $fd line] > -1} {
incr i
puts &quot;Looking at line #$i, $line.&quot;

if {[string match &quot;*$pat*&quot; $line]} {
#puts &quot;Match at [tell $fd]&quot;
#could use regexp for better searches

set r [tell $fd]
#file byte offset

append all_mats &quot;$r:$line\n&quot;
#building an acceptable format
#an array would be easier, but started with a list
#and was determined to do it that way.
}
}
close $fd
puts &quot;Looking at info: $all_mats.&quot;

fwline $file &quot;&quot; $all_mats
#recursive recall with new arguments :)

} elseif {$p != 0} {
catch {set fd [open &quot;$file&quot; r+]} err_opened

regsub -all &quot;\{&quot; $p &quot;&quot; p
#need to kill the list open bracket: we don't care
#about the end

foreach seg [split $p &quot;\n&quot;] {
#rebuilding outer

foreach {el obj} [split $seg &quot;:&quot;] {
#selecting from inner


seek $fd $el start
#goto file string ptr

puts stdout &quot;Changing line content: $obj.&quot;
#lets us see what we are about to change
#would be much cooler to write this to the
#users buffer space, edit it there and then copy
#it back TODO...

set rr [gets stdin]
#replacement string

puts -nonewline $fd $rr
#replaces line at location

flush $fd
#writes to file buffer
}
}
}
return
}

fwline [lindex $argv 0] [lindex $argv 1]
 
Alternatively, if your &quot;database&quot; is a comma (or any character) delimited value file, and you want to manipulate it manually, I'm still tickled with TkTable:
#set default file parameter
set filename tlmdb.csv
#load table widget
load &quot;c:/program files/tcl/lib/tktable2.7/tktable.dll&quot;
wm title . &quot;CSV View/Edit&quot;
wm deiconify .
frame .top -borderwidth 2
pack .top -side top
set w .top
label $w.lb -text &quot;CSV file name: &quot;
entry $w.en -textvariable filename -width 42
button $w.bu -text readCSV -command fillTbl
bind $w.en <Return> fillTbl
bind $w.en <Control-f> getFile
bind . <Escape> exit
pack $w.lb $w.en -side left
pack $w.bu -side left -padx 12
proc fillTbl {} {
destroy .t .yscr .xscr .f
frame .f -borderwidth 2 -relief groove
pack .f -side bottom
set w .f
button $w.r -text Add_row -command {.t insert rows end 1}
button $w.d -text Del_row -command {.t delete rows [.t index active row] 1}
button $w.s -text Save -command saveM
pack $w.r $w.d $w.s -side left -padx 3 -pady 3
table .t -yscrollcommand &quot;.yscr set&quot; -xscrollcommand &quot;.xscr set&quot;
scrollbar .yscr -command &quot;.t yview&quot;
scrollbar .xscr -command &quot;.t xview&quot; -orient horizontal
pack .yscr -side right -fill y
pack .t -side top
pack .xscr -side top -fill x
.t configure -variable m -bg white -width 12 -height 12
set fid [open $::filename r]
set flst [split [read $fid] \n]
close $fid
set nrows [llength $flst]
.t configure -rows $nrows
.t configure -cols [llength [split [lindex $flst 0] ,]]
catch [array unset ::m] rtn
for {set i 0} {$i<$nrows} {incr i} {
set linen [lindex $flst $i]
set llst [split $linen ,]
foreach elm $llst {
set j [lsearch $llst $elm]
set ::m($i,$j) $elm
}
}
}
proc saveM {} {
global m filename
set fid [open $filename w]
set nrows [.t cget -rows]
set ncols [.t cget -cols]
for {set i 0} {$i<$nrows} {incr i} {
set rwlst &quot;&quot;
for {set j 0} {$j<$ncols} {incr j} {
if [catch {lappend rwlst $m($i,$j)} rtn] {lappend rwlst &quot; &quot;}
}
set ostring [join $rwlst ,]
puts $fid $ostring
}
close $fid
}
proc getFile {} {
global dirlst dirname filelst
set dirname [pwd]
set filelst [glob -directory $dirname *]
lappend filelst &quot;$dirname/..&quot;
toplevel .open
frame .open.up -borderwidth 4
frame .open.bottom -borderwidth 4
pack .open.up .open.bottom -side top
set w .open.up
listbox $w.hval -height 8 -font {courier 9} -exportselection 1 -yscrollcommand &quot;$w.yscr set&quot; -listvar filelst -xscrollcommand &quot;$w.xscr set&quot; -width 55
scrollbar $w.yscr -command &quot;$w.hval yview&quot;
scrollbar $w.xscr -command &quot;$w.hval xview&quot; -orient horizontal
pack $w.yscr -side right -fill y
pack $w.hval $w.xscr -side top -fill x
bind .open.up.hval <Double-Button-1> {
set filename [lindex $filelst [.open.up.hval curselection]]
}
bind .open.up.hval <Return> {
set filename [lindex $filelst [.open.up.hval curselection]]
}

set u .open.bottom
entry $u.filen -textvariable filename -width 50
button $u.opn -text Open -command {
if [file isdirectory $filename] {
set filelst [glob -directory $filename *]
lappend filelst &quot;$filename/..&quot;
} else {
destroy .open
set ::filename $filename
}
}
pack $u.filen $u.opn -side left -padx 8
bind .open.bottom.filen <Return> {
if [file isdirectory $filename] {
set filelst [glob -directory $filename *]
} else {
destroy .open
set ::filename $filename
}
}
focus .open.bottom.filen
}
(this also includes a crude file search if you hit &quot;control-f&quot; in the filename entry widget. Bob Rashkin
rrashkin@csc.com
 
marsd: I've not personally used MetaKit for a web services engine. However, there are some interesting applications of MetaKit for driving web services.

One is the Tcl'ers Wiki itself ( All of the pages are stored in a MetaKit database. You can read a little bit more about it at
Actually, the Wiki is a rather cool example of TclKit, which I mentioned in another post today. TclKit is a method for distributing Tcl applications as scripted documents. A TclKit-based application consists of two parts:[ul][li]A platform-dependent, single-file Tcl runtime engine[/li][li]A single-file &quot;scripted document&quot; consisting of a compressed archive of all of the scripts, data files, etc. in your application[/li][/ul]This is analogous to the idea of being able to distribute a Java-based application as a single-file Java JRE and a JAR file for your application.

In terms of actual implementation, the TclKit runtime engine is a special implementation of a Tcl interpreter with built-in MetaKit support; all of Tcl's support files are included as an internal MetaKit archive. Your scripted document application is actually a MetaKit database file, with all of your scripts and support files stored as records in the database. This allows for some very cool features, as your script is now just another form of data. Because MetaKit data is managed using standard database transaction techniques, it's safe to update a script while it is running. Which enables you to implement things like safe, automated web updates of distributed applications. You can read more about TclKit at
And, to bring this back to your original question, some people have taken TclHttpd, the commonly-used Tcl-based web server, and turned it into a TclKit scripted document. They've been able to get an entire functioning web server along with an entire web site on a single floppy disk. If you'd like to read more about this, I'd suggest starting with the Wiki's TclHttpd page, and following the link to the &quot;a tclhttpd scripted document&quot; page, - Ken Jones, President
Avia Training and Consulting
866-TCL-HELP (866-825-4357) US Toll free
415-643-8692 Voice
415-643-8697 Fax
 
Thanks Guys,
Good info, and a nice little program Bong.
 
thanks experts,
your suggestions were valuable, especially that of bong.
My database is not very big, and file modifications is also not performed very frequently. So I used the techniq of reading file and modifying the particular line and then writing that again to file.
thanks again!!
-Pankaj
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top