user2905800
user2905800

Reputation: 41

How to replace a string of different length through file handling in tcl

Want to replace SVT-ATL in all the lines of file with SVT without disturbing other text. Using below code:

set fileDest3 "$dirName/$filename"
set fpr [open $fileDest3 r+]
set line [gets $fpr]
regsub -all "SVT-ATL" $line "SVT" line
puts $fpr "$line"

Upvotes: 0

Views: 309

Answers (4)

Peter Lewerin
Peter Lewerin

Reputation: 13282

package require fileutil

proc cmd data {
    string map {SVT-ATL SVT} $data
}

if {[catch {fileutil::updateInPlace [file join $dir $filename] cmd}]} {
    error "failed to change file"
}

The Tcllib fileutil::updateInPlace command takes care of the low-level details of opening, reading, applying a given command to the content, truncating, writing, and closing files that you want updated. You simply provide a command like cmd here and enjoy the odds ever being in your favor.

Documentation: catch, error, if, package, proc, string

The fileutil package is documented here: fileutil

Upvotes: 1

glenn jackman
glenn jackman

Reputation: 247210

I would consider

exec sed -i {s/SVT-ATL/SVT/g} "$dirName/$filename"

Upvotes: 0

Donal Fellows
Donal Fellows

Reputation: 137787

Because you're changing the length of lines, you must rewrite the whole file. (Well, you could theoretically leave the lines before the first thing being changed a lot, but that's a whole bunch more work.) The simplest way is to read it all in, string map to perform the change (in the simplest case; regsub if things are trickier) and then write it all back out (chan seek to the beginning first, of course). As you're shortening things, you'll need to finish with a chan truncate.

set fileDest3 "$dirName/$filename"
set fpr [open $fileDest3 r+]
set newContents [string map {"SVT-ATL" "SVT"} [read $fptr]]
chan seek $fptr 0
puts -nonewline $fptr $newContents
chan truncate $fptr
close $fptr

The puts has a -nonewline so you don't get an extra terminating newline; the one that was there originally will still be in (as we're reading it all in and not just line-by-line).

Upvotes: 2

Dinesh
Dinesh

Reputation: 16436

set timestamp [clock format [clock seconds] -format {%Y%m%d%H%M%S}]

set filename "yourfilenamehere.txt"
set temp     $filename.tmp.$timestamp
set backup   $filename.bak.$timestamp

set in  [open $filename r]
set out [open $temp     w]

# line-by-line, read the original file
while {[gets $in line] != -1} {

    # Modifying $line by replacing the 'SVT-AL' with 'SVT'
    regsub -all "SVT-ATL" $line "SVT" line

    # then write the modified line to 'tmp' file
    puts $out $line
}

close $in
close $out

# This is to rename the current file to backup file
file rename -force $filename $backup
# This is to rename the tmp file to the original file
file rename -force $temp $filename 

Reference : Glenn Jackman & Donal Fellows

Update :

If you don't want to create a new file, then at least, as Jerry pointed out, we can read all the file content at once, apply our string replacement and then write back to file.

# Reading the file content
set fd [ open "yourfilename" r ] 
set data [ read $fd ]
close $fd

# Replacing the string now... 
regsub -all "SVT-ATL" $data "SVT" data

# Opening file with 'w' mode which will truncate the file
set fd [ open "yourfilename" w ]
puts $fd $data
close $fd

Upvotes: 0

Related Questions