how to compare lines of two files and change the matched line in one file in tcl - tcl

tcl
I wanna compare two files line by line.
file1
abc
123
a1b2c3
file2
abc
00 a1b2c3
if the line of file1 matched one of the line of file2, change the line of file1 to the line of file2
so the output file woule be like that.
file1
abc
123
00 a1b1c3
please help me
thank you

Here's a working example, if necessary adjust file paths to fit your needs.
This code makes a temporary work file that overwrites the original file1 at end.
set file1Fp [open file1 "r"]
set file1Data [read $file1Fp]
close $file1Fp
set file2Fp [open file2 "r"]
set file2Data [read $file2Fp]
close $file2Fp
set tempFp [open tempfile "w"]
foreach lineFile1 [split $file1Data "\n"] {
set foundFlag 0
foreach lineFile2 [split $file2Data "\n"] {
if { $lineFile1 == {} } continue
if { [string match "*$lineFile1*" $lineFile2] } {
set foundFlag 1
puts $tempFp "$lineFile2"
}
}
if { $foundFlag == 0 } {
puts $tempFp "$lineFile1"
}
}
close $tempFp
file rename -force tempfile file1

You could write
set fh [open file2]
set f2_lines [split [read -nonewline $fh] \n]
close $fh
set out_fh [file tempfile tmp]
set fh [open file1]
while {[gets $fh line] != -1} {
foreach f2_line $f2_lines {
if {[regexp $line $f2_line]} {
set line $f2_line
break
}
}
puts $out_fh $line
}
close $fh
close $out_fh
file rename -force $tmp file1
Depending on how you want to compare the two lines, the regexp command can also be expressed as
if {[string match "*$line*" $f2_line]}
if {[string first $line $f2_line] != -1}

Related

TCL: Read lines from file that contain only relevant words

I'm reading file and make some manipulation on the data.
Unfortunately I get the below error message:
unable to alloc 347392 bytes
Abort
Since the file is huge, I want to read only the lines that contain some word (describe in "regexp_or ")
Is there any way to read only the lines that contain "regexp_or" and save the foreach loop?
set regexp_or "^Err|warning|Fatal error"
set file [open [lindex $argv 1] r]
set data [ read $file ]
foreach line [ split $data "\n" ] {
if {[regexp [subst $regexp_or] $line]} {
puts $line
}
}
You could pull your input through grep:
set file [open |[list grep -E $regexp_or [lindex $argv 1]] r]
But that depends on grep being available. To do it completely in Tcl, you can process the file in chunks:
set file [open [lindex $argv 1] r]
while {![eof $file]} {
# Read a million characters
set data [read $file 1000000]
# Make sure to only work with complete lines
append data [gets $file]
foreach line [lsearch -inline -all -regexp [split $data \n] $regexp_or] {
puts $line
}
}
close $file

Compare columns between 2 files using TCL

I have 2 files having only one column. Say file1.txt and file2.txt.
Below are the contents inside the file
Inside file1.txt
Tom
Harry
Snowy
Edward
Inside file2.txt
Harry
Tom
Edward
2) I want to write a code that will check each item in the column and print something as below.
"Tom, Harry, Edward" are present in both the files
Snowy is there in file1.txt but not in file2.txt
3) Basic code
set a [open file1.txt r]
set b [open file2.txt r]
while {[gets $a line1] >= 0 && [gets $b line2] >= 0} {
foreach a_line $line1 {
foreach b_line $line2 {
if {$a_line == $b_line } {
puts "$a_line in file test1 is present in $b_line in file test2\n"
} else {
puts "$a_line is not there\n"
}
}
}
}
close $a
close $b
Issue is that it is not checking each name in the column.
Any suggestions.
Thanks in advance.
Neel
What you want to do is read each file separately and not have nested loops:
# read the contents of file1 into an associative array
# store the user as an array **key** for fast lookoup
set fh [open "file1.txt" r]
while {[gets $fh user] != -1} {
set f1tmp($user) ""
}
close $fh
# read file2 and compare against file1
array set users {both {} file1 {} file2 {}}
set fh [open "file2.txt" r]
while {[gets $fh user] != -1} {
if {[info exists f1tmp($user)]} {
lappend users(both) $user
unset f1tmp($user)
} else {
lappend users(file2) $user
}
}
close $fh
set users(file1) [array names f1tmp]
parray users
users(both) = Harry Tom Edward
users(file1) = Snowy
users(file2) =
Or as Donal suggests, use tcllib
package require struct::set
set fh [open file1.txt r]
set f1users [split [read -nonewline $fh] \n]
close $fh
set fh [open file2.txt r]
set f2users [split [read -nonewline $fh] \n]
close $fh
set results [struct::set intersect3 $f1users $f2users]
puts "in both: [join [lindex $results 0] ,]"
puts "f1 only: [join [lindex $results 1] ,]"
puts "f2 only: [join [lindex $results 2] ,]"
in both: Harry,Tom,Edward
f1 only: Snowy
f2 only:

To copy the content of the file between between two matches to other file

I have a file in which data is shown below:
Files {
string1
string2
string3
...
...
}
but I want to copy content between { & } i.e string1, string2 to other file both the braces are in different lines to other files.
Since Tcl is so flexible, and that file has a Tcl-ish syntax, I'd do this:
proc Files {data} {set ::files_data $data}
source data_file
set fin [open other_file r]
set fout [open other_file.tmp w]
set have_string1 false
while {[gets $fin line] != -1} {
if {$have_string1 && [string match "string2" $line]} {
puts $fout $files_data
}
puts $fout $line
if {[string match "string1" $line]} {
set have_string1 true
}
}
close $fin
close $fout
# next line only if you need to keep a backup of the original
file link -hard other_file.bak other_file
file rename -force other_file.tmp other_file

need to print two different strings from a text file using tcl script

I have arequirement to get two different strings from two consecutive lines from a file using tcl script
I tried following but it doesn't work.
So here below i need to print string "Clock" and "b0". I am able to print Clock. but i need both "clock" "b0"
set f [eval exec "cat src.txt"]
set linenumber 0
while {[gets $f line] >= 0} {
incr linenumber
if {[string match "Clock" $line] >= 0 } {
# ignore by just going straight to the next loop iteration
while {[gets $f line] >= 0} {
incr linenumber
if { [string match "b0" $line"]} {
close $out
puts "final $line"
}
puts "\n$line"
continue
}
}
}
close $f
Is this you want?
set f [open "src.txt" r]; # opening file
while {![eof $f] >= 0} {
set line [gets $f]; # reading line from file
if {[string match "*Clock*" $line]} {
; # if Clock found
puts $line
set line [gets $f]; # reading next line from file
if { [string match "*b0*" $line]} {
; # if b0 found
puts "final $line"
}
}
}
close $f

TCL - find a regular pattern in a file and return the occurrence and number of occurrences

I am writing a code to grep a regular expression pattern from a file, and output that regular expression and the number of times it has occured.
Here is the code: I am trying to find the pattern "grep" in my file hello.txt:
set file1 [open "hello.txt" r]
set file2 [read $file1]
regexp {grep} $file2 matched
puts $matched
while {[eof $file2] != 1} {
set number 0
if {[regexp {grep} $file2 matched] >= 0} {
incr number
}
puts $number
}
Output that I got:
grep
--------
can not find channel named "qwerty
iiiiiii
wxseddtt
lsakdfhaiowehf'
jbsdcfiweg
kajsbndimm s
grep
afnQWFH
ACV;SKDJNCV;
qw qde
kI UQWG
grep
grep"
while executing
"eof $file2"
It's usually a mistake to check for eof in a while loop -- check the return code from gets instead:
set filename "hello.txt"
set pattern {grep}
set count 0
set fid [open $filename r]
while {[gets $fid line] != -1} {
incr count [regexp -all -- $pattern $line]
}
close $fid
puts "$count occurrances of $pattern in $filename"
Another thought: if you're just counting pattern matches, assuming your file is not too large:
set fid [open $filename r]
set count [regexp -all -- $pattern [read $fid [file size $filename]]]
close $fid
The error message is caused by the command eof $file2. The reason is that $file2 is not a file handle (resp. channel) but contains the content of the file hello.txt itself. You read this file content with set file2 [read $file1].
If you want to do it like that I would suggest to rename $file2 into something like $filecontent and loop over every contained line:
foreach line [split $filecontent "\n"] {
... do something ...
}
Glenn is spot on. Here is another solution: Tcl comes with the fileutil package, which has the grep command:
package require fileutil
set pattern {grep}
set filename hello.txt
puts "[llength [fileutil::grep $pattern $filename]] occurrences found"
If you care about performance, go with Glenn's solution.