This is the new home of the egghelp.org community forum.
All data has been migrated (including user logins/passwords) to a new phpBB version.


For more information, see this announcement post. Click the X in the top right-corner of this box to dismiss this message.

Basic File Operations

Issues often discussed about Tcl scripting. Check before posting a scripting question.
Post Reply
User avatar
stdragon
Owner
Posts: 959
Joined: Sun Sep 23, 2001 8:00 pm
Contact:

Basic File Operations

Post by stdragon »

There are lots of questions about reading lines from files, replacing lines, etc. So I'll address a few things here.

1. Reading all the lines from a file into a list.
This is one of the most basic things you can do. It's the basis for several other operations we'll be discussing, so get familiar with it!

Code: Select all

# File name to read.
set fname "yourfile.txt"

# Open file for read access (note we're not catching errors, you might
# want to use catch {} if the file might not exist.
set fp [open $fname "r"]

# Here we read in all of the data.
set data [read -nonewline $fp]

# Close the file, since we're done reading.
close $fp

# Now we split the data into lines.
set lines [split $data "\n"]
There you go, a list of all the lines in the file, with just a few simple lines of code! On to more advanced topics.

2. Reading a random line from a (small) file.
There are lots of reasons you might want to do this: quote scripts, random topic scripts, trivia games, etc. In this example we'll only be reading a single line, but you could easily change it to read more than 1 line in a row, or multiple lines from different places.

Code: Select all

# Use the code from above (1.) to read in all the lines from the file.
# We continue right after: set lines [split $data "\n"]

# Get the number of lines.
set numlines [llength $lines]

# Choose a random line with eggdrop's rand function.
set num [rand $numlines]

# Get the line from the list!
set randline [lindex $lines $num]
Now you have the random line in the $randline variable. If you wanted to read multiple lines, you could put the last part in a loop.

3. Deleting a line from a text file.
We'll assume you want to delete a specific line. Remember that most languages, including tcl, index things starting from 0. So if you want to delete the first line, it is line 0. The second line is line 1. Etc.

Code: Select all

# Use the code from above (1.) to read in all the lines from the file.
# We continue right after: set lines [split $data "\n"]

# We'll delete the first line.
set line_to_delete 0

# If you wanted to delete the last line instead, you would do this:
# set line_to_delete [expr [llength $lines] - 1]

# Now, we remove the line from the list in memory first.
set lines [lreplace $lines $line_to_delete $line_to_delete]

# And finally, we re-write the file with the new data.
set fp [open $fp "w"]
puts $fp [join $lines "\n"]
close $fp
As you can see, to delete a line from a file, we have to read the file into memory, remove the line from memory, then overwrite the file on disk. If you are doing a lot of adding/removing of records, you should consider using a database instead! Also, if your file is very large (several megabytes), these operations will take some time -- you should not be using a flat text file.

4. Inserting a line into a file.
Usually people want to insert lines at the beginning of a file, or append them onto the end. The latter will be handled in the next question.

Code: Select all

# Use the code from above (1.) to read in all the lines from the file.
# We continue right after: set lines [split $data "\n"]

# For the beginning, use line 0, since tcl starts counting at 0. If you want
# to append the line to the end, see the next question for a better way.
set line_to_insert "this is the new line"
set where_to_insert 0

# Now we insert the line into our list in memory.
set lines [linsert $lines $where_to_insert $line_to_insert]

# And now we re-write the file, just like in 3.
set fp [open $fp "w"]
puts $fp [join $lines "\n"]
close $fp
5. Add a line to the end of a file.
For the special case of adding a line to the end of the file, we don't need
to mess with reading/re-writing at all. We simply open the file in a special
mode called "append mode."

Code: Select all

# The new line.
set line_to_add "the dragon will eat your sheep!"

# Name of file to append to.
set fname "yourfile.txt"

# Open the file in append mode.
set fp [open $fname "a"]

# Add the line.
puts $fp $line_to_add

# We're done!
close $fp


If you'd like to see some other file i/o topics answered here, make a post below!
Of course, don't bother with things like "how do I read a random line and display it in a private message" because the whole reading-random-lines topic is covered already.
User avatar
strikelight
Owner
Posts: 708
Joined: Mon Oct 07, 2002 10:39 am
Contact:

Re: Basic File Operations

Post by strikelight »

stdragon wrote: 3. Deleting a line from a text file.
We'll assume you want to delete a specific line. Remember that most languages, including tcl, index things starting from 0. So if you want to delete the first line, it is line 0. The second line is line 1. Etc.

Code: Select all

# Use the code from above (1.) to read in all the lines from the file.
# We continue right after: set lines [split $data "\n"]

# We'll delete the first line.
set line_to_delete 0

# If you wanted to delete the last line instead, you would do this:
# set line_to_delete [expr [llength $lines] - 1]

# Now, we remove the line from the list in memory first.
set lines [lreplace $lines $line_to_delete $line_to_delete]

# And finally, we re-write the file with the new data.
set fp [open $fp "w"]
puts $fp [join $lines "\n"]
close $fp
As you can see, to delete a line from a file, we have to read the file into memory, remove the line from memory, then overwrite the file on disk. If you are doing a lot of adding/removing of records, you should consider using a database instead! Also, if your file is very large (several megabytes), these operations will take some time -- you should not be using a flat text file.
Just a couple of optimization tips, especially with reference to this section:
Take advantage of TCL's expression optimization by using {}'s:

Code: Select all

# If you wanted to delete the last line instead, you would do this:
# set line_to_delete [expr [llength $lines] - 1]

to:

# set line_to_delete [expr {[llength $lines] - 1}]
When performing an in-place lreplace (that is to say, setting a variable to the result of an lreplace operation on the same variable itself), performance can be noticably enhanced by using the following:

Code: Select all

# Now, we remove the line from the list in memory first.
set lines [lreplace $lines $line_to_delete $line_to_delete]

Becomes:

proc K {x y} { set x }
set lines [lreplace [K $lines [set lines {}]] $line_to_delete $line_to_delete]
(Tips obtained from The Tcl'ers Wiki).
G
Gust
Voice
Posts: 15
Joined: Wed Jun 28, 2006 12:53 pm

Re: Basic File Operations

Post by Gust »

stdragon wrote:3. Deleting a line from a text file.
We'll assume you want to delete a specific line. Remember that most languages, including tcl, index things starting from 0. So if you want to delete the first line, it is line 0. The second line is line 1. Etc.
First of all, thanks for this explanation.

In the piece of code you've provided, you should allready know the line-number of the line you want to remove...
But what if you don't know the line-number in the textfile?

For example: I have a file that contains the following:

Code: Select all

hostname 1 user1
hostname5 user5
hostname8 user8
hostname3 user3
hostname12 user12
hostname6 user6
And i want to delete the following line:

Code: Select all

hostname8 user8
by searching on "user8"; how can i do that?

Thanks in advance!

Greetings,
Gust
User avatar
user
 
Posts: 1452
Joined: Tue Mar 18, 2003 9:58 pm
Location: Norway

Re: Basic File Operations

Post by user »

lsearch

Code: Select all

# delete ALL lines ending with " user8"
set match "* user8"
while {[set i [lsearch -glob $lines $match]]>-1} {
	set lines [lreplace $lines $i $i]
}
Have you ever read "The Manual"?
S
SL0RD
Voice
Posts: 19
Joined: Sun Oct 05, 2008 11:44 am

Post by SL0RD »

Thanks for the great basic tutorial.

I have two "requests"

First off, say you had this:

Code: Select all

thing1 disc
thing2 disc
thing3 disc
thing4 disc
I would like to add a new line but if the line that i want to add starts with thing1 for example instead of adding it again at the end i want to update the existing thing1
also how could you delete a line by using lsearch but by searching the first word in each line so i would like to delete things3 for example by searching for thing3

secondly, say you had that same table, I want to be able to do a command and have it pull the first word from every line and list it in the channel ex: <bot> thing1, thing2, thing3, thing4

That is all for now but i will probably have more questions later. thanks ;)
A
A Hylian Human
Voice
Posts: 5
Joined: Mon Sep 06, 2010 11:59 am

Basic File Operations - Evaluating variables read from file

Post by A Hylian Human »

If I have variables, such as $nick or $arg, in the file, it doesn't evaluate the variables. Instead, it just leaves it at $nick or $arg.
Is there any way to evaluate the variables?
using [eval] makes Tcl want to use the first word of the line as a command, which is not appropriate when I have, say:
putmsg $chan "[eval [readFile filename.txt]]"
(readFile is a seperate process that reads the file and returns a random line)
User avatar
username
Op
Posts: 196
Joined: Thu Oct 06, 2005 9:20 am
Location: Russian Federation, Podolsk
Contact:

Re: Basic File Operations - Evaluating variables read from f

Post by username »

A Hylian Human wrote:If I have variables, such as $nick or $arg, in the file, it doesn't evaluate the variables. Instead, it just leaves it at $nick or $arg.
Is there any way to evaluate the variables?
using [eval] makes Tcl want to use the first word of the line as a command, which is not appropriate when I have, say:
putmsg $chan "[eval [readFile filename.txt]]"
(readFile is a seperate process that reads the file and returns a random line)
You need to use subst command. Look at this example:

Code: Select all

        set realmsgs {
         "Hi, how are you?"
         "Hi $nick!"
         "Hello $nick, welcome to $chan"
         "]]] WELCOME to $chan!!!11111 ^^ ^^ [[["
         "Hi [$nick], you have to pay 20$ to enter here"
     }
     
     bind join - * greet
     proc greet {nick host hand chan} {
         set greetmsg [lindex $::realmsgs [rand [llength $::realmsgs]]]
         set greetmsg [subst -nocommands $greetmsg]
         putserv "PRIVMSG $nick :$greetmsg"
     }
Архив TCL скриптов для ботов Eggdrop/Windrop:
http://egghelp.ru/
Post Reply