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 Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Can a file be read into an array? 1

Status
Not open for further replies.

WTHolmes

Technical User
Mar 22, 2007
18
0
0
US
Hi, as you have probably already guessed from my question, I am fairly new to programming and brand new to TCL/TK.

I am trying to write a TCL program which will read in a file, one line at a time, into an array. Once that line is read into the array, I would then need to use the 'array size' function to "count" the number of elements in the array. After I know how many elements are in the array, I would then want to address each element individually. Checking for whether that character is a letter, a number, a space or a symbol. I would then report my findings on the screen,

i.e. There were 6 A's in the file
There were 5 a's in the file
There were 12 .'s in the file....

It is quite possible that there is a much easier way to accomplish this without using an array. Is it possible to read a file one character at a time? If it is, this, along with some strategically-placed nested loops, would also accomplish the task.

Thank You for you time and consideration.

 
Hi

This does what you asked for : reads the file character by character.
Code:
set fil [open "[green][i]input.file[/i][/green]"]
while {! [eof $fil]} {
  set c [read $fil 1]
  if {[array names cc -exact $c]!=""} {
    set cc($c) [expr $cc($c)+1]
  } else {
    set cc($c) 1
  }
}
close $fil

foreach c [array names cc] {
  puts "There were $cc($c) $c's in the file"
}
But that approach is inefficient, is much faster to read the whole file at once.
Code:
set f [open "[green][i]input.file[/i][/green]"]
set t [read $f]
close $f

set s [split $t {}]

for {set i 0} {$i<[llength $s]} {incr i} {
  set c [lindex $s $i]
  if {[array names cc -exact $c]!=""} {
    set cc($c) [expr $cc($c)+1]
  } else {
    set cc($c) 1
  }
}

foreach c [array names cc] {
  puts "There were $cc($c) $c's in the file"
}
But there could be huge files, which exceeds in size the available memory, so better combine the above two approaches and read in chunks, lets say 10Kb.
Code:
set f [open "[green][i]input.file[/i][/green]"]
while {! [eof $f]} {
  set t [read $f 10240]
  set s [split $t {}]
  for {set i 0} {$i<[llength $s]} {incr i} {
    set c [lindex $s $i]
    if {[array names cc -exact $c]!=""} {
      set cc($c) [expr $cc($c)+1]
    } else {
      set cc($c) 1
    }
  }
}
close $f

foreach c [array names cc] {
  puts "There were $cc($c) $c's in the file"
}
This is not your homework, right ?

Feherke.
 
I would like to first thank feherke for a very concise and timely response. Thank You! And, no, this is not MY homework.

Now, I would like to understand better, exactly what you have done. Like I said, I am new to Tcl/Tk and I've spent over an hour trying to figure out exactly what is going on in your code. I have added my own code to it to make it interactive. I will copy the code and comment it line by line as I interpret it.

I also want to make a few slight changes:

1. Instead of counting each upper and lowercase letter individually, I want to convert them all to lowercase (or uppercase) and just count them once as a single a or b or c.... Instead of displaying only letters that appear in the file, I would like to display all 26 letters and show how often each letter occured in the file, and I would like to lump all non-letter characters together and display their presence as "other characters".

i.e. There were 11 A's in the file.
There were 2 B's in the file...
There were 0 Z's in the file.
There were 13 other charaters in the file.

There were 6 lines in the file.

2. As you can see from above, I would also like to count the number of lines read in from the file, and display that information on a separate line formatted as above. I noticed that from the code that you gave me, it counted the carriage returns, however, it also "used" them as carriage returns, splitting the output into two separate lines.

"There were 7
's in the file"

3. I have done lots of research on this, and have come to the conclusion that it might not be possible, however, if it is possible, I would like to clear the screen upon executing the program and again after the file name is input from the user. This will give a nice full page for the summary output to display.

Well, here goes nothing:

puts "This program counts the occurances letters and symbols in a given file.\n"
#displays the quoted text to the screen

puts "\n"
#displays a blank line to the screen

puts "example: c:\\data\\data.dat\n"
#displays the quoted text to the screen, note the double backslashes, I don't know why, but unless you double the backslashes, they won't display

puts -nonewline stdout "Enter the file name: "
#displays the quoted text to the screen, I am not real sure what the "-nonewline stdout" is for you can get the same results by using a simple puts

flush stdout
#I don't know what this does, but a shot in the dark is, it clears the memory buffer allocated for "stdout". As I said previously, it is probably not necessary to use

set filename [gets stdin]
#assigns the variable named "filename" whatever the user types in. There should probably be some error checking here but, I am new to this and don't know how to do that

set f [open $filename r]
#opens the file and assigns it to the variable named "f"

puts "\n"
#displays a blank line to the screen

while {! [eof $f]} {
#starts a while loop to process the file until the 'end-of-file' is reached

set t [read $f 10240]
#assigns the variable named "t" one 'byte' of data at a time. Very nice, I like that.

set s [split $t {}]
#I am guessing this assigns the variable named "s" one character at a time from the "byte" using the 'split' command

for {set i 0} {$i<[llength $s]} {incr i}
#initiates a for loop with initialize, condition, and increment or decrement. In this case, you initialize the variable "i" to zero. Your condition is while "i" is less than "s". I am guessing that as long as there is an "s" s's length will return a value of 1, when there is no "s", the value of s will be 0 making the condition false and terminating the loop. However the i is being incremented by 1 through each iteration of the loop and by my definition would cause the loop to end after just one pass. So obviously I don't know what the split command does in the previos line of code

{
#the open braces is used to group all of the actions which will take place if the conditions are met in the for loop

set c [lindex $s $i]
#now this is where things get muddy. I know c is a variable being assigned something, but that's about it

if {[array names cc -exact $c]!=""}
#I recognise an if statement, however, the condition is unclear. I believe an array is being set up. I am guessing that names is a reserved word, the rest, I don't have a clue

{
#the open braces is used to group all of the actions which will take place if the conditions are met making the 'if' statement "true"

set cc($c) [expr $cc($c)+1]
#again, nothing

}
#this close braces marks the end of the true if-condition group

else
#the "else" condition if not this, then this

{
#the open braces is used to group all of the actions which will take place if the conditions are false in the if statement

set cc($c) 1
#I got nothing

}
#this close braces marks the end of the else-condition group

}
#this close braces marks the end of the for loop

}
#this close braces marks the end of the while loop

close $f
#closes the open file

foreach c [array names cc]
#this is a new loop structure for me. It appears to be very useful because you don't have to know how many elements are in the array, you simply "do" 'for each element within the array', cool!

{
#the open braces is used to group all of the actions which will take place "for each" element of the array

puts "There were $cc($c) $c's in the file"
#I know that this is formatted output, however, I am not quite sure what is going on with the $cc. I am guessing that that is the name of the array, and that the '($c)' is the current element of the array. The '$c' is the current character

}
#this close braces marks the end of the foreach loop


puts "This program counts the occurances letters and symbols in a given file.\n"
puts "\n"
puts "example: c:\\data\\data.dat\n"
puts -nonewline stdout "Enter the file name: "
flush stdout
set filename [gets stdin]
set f [open $filename r]
puts "\n"
while {! [eof $f]} {
set t [read $f 10240]
set s [split $t {}]


for {set i 0} {$i<[llength $s]} {incr i} {
set c [lindex $s $i]
if {[array names cc -exact $c]!=""} {
set cc($c) [expr $cc($c)+1]
} else {
set cc($c) 1
}
}
}
close $f

foreach c [array names cc] {
puts "There were $cc($c) $c's in the file"
}
 
Hi

Sorry, I have not enough time to answer your syntax questions. Please read the Tcl documentation, that helps a lot.
Code:
puts "This program counts the occurances letters and symbols in a given file.\n\n"

puts "example: c:\\data\\data.dat\n"
puts -nonewline "Enter the file name: "
flush stdout

set filename ""
while {$filename==""} {
  set filename [gets stdin]
  if {! [file exist $filename]} {
    puts "Error : the file '$filename' can not be found.\n"
    set filename ""
  } elseif {! [file readable $filename]} {
    puts "Error : the file '$filename' is not readable.\n"
    set filename ""
  } elseif {[file isdirectory $filename]} {
    puts "Error : the file '$filename' is a directory.\n"
    set filename ""
  }
  if {$filename==""} {
    puts -nonewline "Enter the file name: "
    flush stdout
  }
}

puts "\n"

for {set i 65} {$i<=90} {incr i} {
  set cc([format "%c" $i]) 0
}
set cc(newline) 0
set cc(other) 0

set f [open $filename r]

while {! [eof $f]} {
  set t [read $f 10240]
  set s [split $t {}]

  for {set i 0} {$i<[llength $s]} {incr i} {

    set c [lindex $s $i]

    if {[string is alpha $c]} {
      set c [string toupper $c]
    } elseif {$c=="\n"} {
      set c "newline"
    } else {
      set c "other"
    }

    set cc($c) [expr $cc($c)+1]
  }
}

close $f

foreach c [lsort [array names cc]] {
  puts "There were $cc($c) $c's in the file"
}

Feherke.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top