Deleting contents of a file in Tcl - file

I'm having some troubles deleting the contents of a text file. From what I can tell, I cannot seem to rename or delete this file and create a new one with the same name, due to permission issues with the PLM software we use. Unfortunately, I am on my own here, since no one seems to know what exactly is wrong.
I can read and write to this file, however. So I've been looking at the seek command and doing something like this:
set f [open "C:/John/myFile.txt" "a+"]
seek $f 0
set fp [tell $f]
seek $f 0 end
set end [tell $f]
# Restore current file pointer
seek $f $fp
while { $fp < $end } {
puts -nonewline $f " "
incr fp
}
close $f
This seems to replace all the lines with spaces, but I'm not sure this is the correct way to approach this. Can someone give me some pointers? I'm still relatively new to Tcl.
Thanks!

If you've got at least Tcl 8.5, open the file in r+ or w+ mode (experimentation may be required) and then use chan truncate:
chan truncate $f 0
If you're using 8.4 or before, you have instead to do this (and it only works for truncating to empty):
close [open $thefilename "w"]
(The w mode creates the file if it doesn't exist, and truncates it to empty on open if it does. The rest of the program might or might not like this!)
Note however that this does not reset where other channels open on the file think they are. This can lead to strange effects (such as writing at a large offset, with the OS filling out the preceding bytes with zeroes) even without locking.

close [open $path w]
And voila, an empty file. If this file does not yet exist, it will be created.

A really easy way to do this is to just over-write your file with an empty file. For example create an empty file (you can do this manually or using the following TCL code):
set blank_file [open "C:/tmp/blank.txt" "w"]
close $blank_file
Then just over-write your original file with the blank file as follows:
file rename -force "C:/tmp/blank.txt" "C:/John/myFile.txt"
Of course, you may have permissions problems if something else has grabbed the file.

You say the file is opened exclusively with another process but you can write to it ?! I think you have permission problems. Are you using Linux or Unix ?! (It seems it is a Windows system but permission problems usually occur on Linux/Unix systems, It is weird, isn't it ?!)
The file is not exclusively opened if you are able to read and write to it and you may have no appropriate permission to delete the file.
Also it is better to test the code on a file you know you have all permissions on it. If the code is working you can focus on your target file. Also you can Google for 'how to file operations in Tcl'. Read this Manipulating Files With Tcl

Related

Can an already opened FILE handle reflect changes to the underlying file without re-opening it?

Assuming a plain text file, foo.txt, and two processes:
Process A, a shell script, overwrites the file in regular intervals
$ echo "example" > foo.txt
Process B, a C program, reads from the file in regular intervals
fopen("foo.txt", "r"); getline(buf, len, fp); fclose(fp);
In the C program, keeping the FILE* fp open after the initial fopen(), doing a rewind() and reading again does not seem to reflect the changes that have happened to the file in the meantime. Is the only way to see the updated contents by doing an fclose() and fopen() cycle, or is there a way to re-use the already opened FILE handle, yet reading the most recently written data?
For context, I'm simply trying to find the most efficient way of doing this.
On Unix/Linux, when you create a file with a name which already existed, the old file is not deleted or altered in any way. A new file is created and the directory is updated to point at the new file instead of the old one.
The old file will continue to exist as long as some directory entry points at it (Unix file systems allow the same file to be pointed to by multiple directories) or some program has an open file handle to the file, which is more relevant to your question.
As long as you don't close fp, it continues to refer to the original file, even if that file is no longer referenced by the filesystem. When you close fp, the file will get garbage collected automatically, and the next time you open foo.txt, you'll get a file descriptor for whatever file happens to have that name at that point in time.
In short, with the shell script you indicate, your C program must close and reopen the file in order to see the new contents.
Theoretically, it would be possible for the shell script to overwrite the same file without deleting it, but (a) that's tricky to get right; (b) it's prone to race conditions; and (c) closing and reopening the file is not that time-consuming. But if you did that, you would see the changes. [Note 1]
In particular, it's common (and easy) to append to an existing file, and if you have a shell script which does that, you can keep the file descriptor open and see the changes. However, in that case you would normally have already read to the end of the file before the new data was appended, and the standard C library treats the feof() indicator as sticky; once it gets set, you will continue to get an EOF indication from new reads. If you suspect that some process will be writing more data to the file, you should reset the EOF indication with fseek(fp, 0, SEEK_CUR); before retrying the read.
Notes
As #amadan points out in a comment, there are race conditions with echo text > foo.txt as well, although the window is a bit shorter. But you can definitely avoid race conditions by using the idiom echo text > temporary_file; mv -f temporary_file foo.txt, because the rename operation is atomic. Of course, that would definitely require you to close and reopen the file. But it's a good idea, particularly if the contents being written are long or critical, or if new files are created frequently.

Read/Write highscore from a file (Lua - Corona SDK)

Here's my issue: I have a file with the highscore written in it (just the first line, no nicknames, just the highscore), I need to read that line and compare it with the actual score obtained in the game session, if the score is higher, overwrite the file with the new value, but if I try to read it I get a null value... Seems like i'm not reading it the right way. What's wrong with my code?
Thanks for the help!
local path = system.pathForFile( "data.sav", system.DocumentsDirectory )
local file = io.open( path, "w+" )
highscore_letta = file:read("*n")
print(highscore_letta)
if (_G.player_score > tonumber(highscore_letta)) then
file:write(_G.player_score)
end
io.close( file )
I had this problem myself. I found out that if you open a file in "w+" mode, the current contents are deleted, so that you can write new contents. So to read and write you have to open the file twice. First, you open the file in "rb" mode and get the file contents, then close it. Then you reopen it in "wb" mode, write the new number, and close it.
In Windows, you need "b" in the file mode. Otherwise, the strings that you are reading and writing may be modified in unexpected ways: for instance, a newline ("\n") may be replaced with carriage return–newline ("\r\n").
The file modes that Lua supports are borrowed from the C language. (I found a description on page 305 of what I guess is a draft of the C specification.) I think the Lua manual sort of assumes that you will know what these modes mean, as an experienced C programmer would, but to me it wasn't at all obvious.
Thus to read a number and then write a new one:
local filepath = "path/to/file"
-- Create a file handle that will allow you to read the current contents.
local read_file = io.open(filepath, "rb")
number = read_file:read "*n" -- Read one number. In Lua 5.3, use "n"; the asterisk is not needed.
read_file:close() -- Close the file handle.
local new_number = 0 -- Replace this with the number you actually want to write.
-- Create a file handle that allows you to write new contents to the file,
-- while deleting the current contents.
write_file = io.open(filepath, "wb")
write_file:write(new_number) -- Overwrite the entire contents of the file.
write_file:flush() -- Make sure the new contents are actually saved.
write_file:close() -- Close the file handle.
I created a script to do these operations automatically, as they're somewhat annoying to type every time.
The mode "r+" or "r+b" is supposed to allow you to read and write, but I couldn't get it to work when the original contents are longer than the new contents. If the original contents are "abcd", four bytes, and the new contents are "efg", three bytes, and you write at offset 0 in the file, the file will now have "efgd": the last byte of the original contents is not deleted.

how to write a tcl script to delete last line of a file

I have to delete the last line of file in using tcl script. I know the content so content replacement is also ok. But my content is which has to be replaced by a space or newline character or have to deleted. And my job is in a loop.
Please let me know which is the efficient way, capturing the entire file content each time in loop and replace that string is better or deleting simply the last line.
Please give some script code because I am very new to tcl.
Are we talking about removing the last line from the data on disk or the data in memory? It matters because the approach you use to do those two cases is entirely different.
In memory
Exactly how you manipulate things in memory depends on whether you're representing the data as list of lines or a big string. Both approaches work. (You could be doing something else too, I suppose, but these two are the common obvious ways.)
If you've got your data as a list of lines in memory, you can simply do (assuming you're holding the lines in a variable called theLines):
set theLines [lreplace $theLines end end]
For a particularly large list, there are a few tricks to make it more efficient, but they come down to careful management of references:
# Needs a new enough Tcl (8.5 or 8.6 IIRC)
set theLines [lreplace $theLines[set theLines ""] end end]
Try the first version instead of this if you don't know you need it. Also be aware that if you're wanting to keep the original list of lines around, you should definitely use the first approach.
You might instead have the data in memory as a single big string. In that case, we can use some of Tcl's string searching capabilities to do the job.
set index [string last "\n" $theString end-1]
set theString [string range $theString 0 $index]
The optimisation mentioned above in relation to lreplace is also applicable here (with all the same caveats):
set index [string last "\n" $theString end-1]
set theString [string range $theString[set theString ""] 0 $index]
On disk
When working on disk, things are different. There you need to be much more careful since you can't undo changes easily. There are two general approaches:
Read the file into memory, do the change there (using the techniques above), and do a (destructive) ordinary write out. This is the approach you need when you are doing many other changes anyway (e.g., removing a line from the middle, adding a line to the middle, adding or removing characters from a line in the middle).
set filename "..."
# Open a file and read its lines into a list
set f [open $filename]
set theLines [split [read $f] "\n"]
close $f
# Transform (you should recognise this from above)
set theLines [lreplace $theLines end end]
# Write the file back out
set f [open $filename "w"]
puts -nonewline $f [join $theLines "\n"]
close $f
Find where the data you don't want starts as an offset in the file and truncate the file at that point. This is the right approach with a very large file, but it is rather more sophisticated.
set f [open $filename "r+"]; # NEED the read-write mode!
seek $f -1000 end; # Move to a little bit before the end of the file.
# Unnecessary, and guesswork, but can work and will
# speed things up for a big file very much
# Find the length that we want the file to become. We do this by building a list of
# offsets into the file.
set ptrList {}
while {![eof $f]} {
lappend ptrList [tell $f]
gets $f
}
# The length we want is one step back from the end of the list
set wantedLength [lindex $ptrList end-1]
# Do the truncation!
chan truncate $f $wantedLength
close $f
However you do the disk transformations, make sure you test on a trash file before applying it to anything real! In particular, I've not checked what the truncation method does on a file without a newline at the end. It probably works, but you should test.

How to remove contents of the file when opened using r+ mode?

I've got my file open using r+ mode. I've made some alteration to it, and I would like to remove the contents of it starting from X to the end of the file.
Unfortunately I have no clue how to do that. I've been browsing through the docs, but there's no mention of that.
I don't want to write :space: to the file so it "looks" like it's cleared, I would like to make it smaller as well.
Opening the file using w is not an option in this case.
Opening a file with r+ mode preserves the current content; as the result, I don't think there is a way to truncate the rest of the file using the functions available in Lua. This SO answer indicates that it can be done using reopen with w+, but since you indicated that you can't open it for writing, I don't think there is a way to do what you want.
Testing on this script produces new contentext (where ext is the leftover from the earlier content):
local f = io.open("somefile", "w")
f:write("some long text")
f:close()
local f = io.open("somefile", "r+")
f:write("new content")
f:close()
Based on my experience, the only way I have found to truncate a file in Lua is to write the contents to a secondary file in "w+" mode, and then rename the secondary file to overwrite the original file. You will probably want to use this method sparingly depending on the size of the file, of course.
In this example "path" is the path to the original file
local file, err = io.open( path + ".tmp", "w+" )
if not file then return end
file:write( truncated_data )
file:close( )
assert( os.rename( path + ".tmp", path ) )

Search a pattern in .gz file in C

I want to read a .gz file (text.gz) with 300MB length and search a pattern in it. I opened the text file in a binary format using fopen with "rb" and stored it in a buffer. When I search a pattern that I know it exists in the text, the result is wrong. When I debug the program, the elements of the buffer are different from what I expect. Do I have to read and store these kind of files in other ways??????
You might try using zlib and gzread to read the file.
http://zlib.net/manual.html
Try this.
gunzip -c file.gz | grep <pattern>
If the program is exiting and failing to read the file, a real common problem is that you don't close the file in Notepad or whatever is using it and the FileIO fails due to not being able to access the file. Make sure you don't have anything with that file open before you test your program.

Resources