This is from exercise 16 from Zed Shaw's Python tutorials. I'm having a hard time understanding what exactly the truncate function does in this case. So the logic is that we open a file and then...shorten it? For what? What exactly is happening here?
from sys import argv
script, filename = argv
print "We're going to erase %r." % filename
print "If you don't want that, hit CTRL-C (^C)."
print "If you do want that, hit RETURN."
raw_input("?")
print "Opening the file..."
target = open(filename, 'w')
print "Truncating the file. Goodbye!"
target.truncate()
print "Now I'm going to ask you for three lines."
line1 = raw_input("line 1: ")
line2 = raw_input("line 2: ")
line3 = raw_input("line 3: ")
print "I'm going to write these to the file."
target.write(line1 + "\n" + line2 + "\n" + line3 + "\n")
print "And finally, we close it."
target.close()
You're right to be suspicious.
First, file.truncate does this:
Truncate the file’s size. If the optional size argument is present, the file is truncated to (at most) that size. The size defaults to the current position…
Not quite the same as Zed's description—it only "empties the file" if the current position is the start of the file—but since we just opened the file (and not in a mode), the current position is the start, so that isn't relevant. We're truncating to an empty file.
Which is all well and good, except that open already does that:
The most commonly-used values of mode are 'r' for reading, 'w' for writing (truncating the file if it already exists) …
So, we open the file, creating it if it doesn't exist and truncating it to 0 bytes if it does. Then, on the next line, we truncate it to 0 bytes.
(That "Truncating the file. Goodbye!" message is pretty misleading, since we've already truncated it. Imagine you put a breakpoint on that line and decided to kill the program before executing it…)
But notice that this isn't some silly mistake by Zed; he appears to have done this specifically to make the point in study drill #5:
If you open the file with 'w' mode, then do you really need the target.truncate()? Read the documentation for Python's open function and see if that's true.
I am currently going through Zed Shaw's book to. Often. when he gives you problems like these, he is goading you into messing around with the different elements of your code to see what they do. Feel free to delete things like target.truncate() and re-run the program. As abarnert says, there is a way to make truncate only remove a portion of the file. Leaving this hole in your knowledge, as to how to get different results with these two options, is meant to annoy you into doing some independent research. It's a very effective way of infecting the reader with curiosity. Take a look how append works in Python and see if you can make truncate() remove only the last line of your test file. Don't be afraid to break your code. "If you want to increase your success rate, double your failure rate." (Thomas J. Watson, former CEO of IBM)
Related
Am taking a tutorial on youtube for python3 - in this exercise the code runs just fine if I have the open "r" option selected for reading files. If I switch the "r" to a "w" it runs through fine the first time, tells me that it cannot read the file - which is the expected outcome. But when I look at employees.txt again or if I try to rerun the code with the "r" option enabled, I see that the text file is empty.
I have tried the syntax in every way I can think of, but so far, I have had no luck in figuring out why it empties the text file
How would you go about troubleshooting something like this? Or is there something obvious in my code that you see? (code is exactly like the course example).
#!/usr/bin/env python3
# Script Name - reading-files.fcc.py
employee_file = open("employees.txt", "r")
if employee_file.readable() == True:
print(employee_file.read())
employee_file.close()
exit
else:
print("Cannot read file.")
employee_file.close()
exit
Here is employee.txt:
Jim - Sales
Dwight - Sales
Pam - Sales
Michael - Manager
Oscar - Accounting
This might be a good post for your consideration.
Opening the file in w mode will overwrite the file, meaning that unless you are writing anything to the file thereafter, it will effectively empty the file's contents- in your case with no additional text, rendering the file's contents blank.
When you open the file with 'w' option, employee_file.readable() returns False, so it print a line, close and save an empty file.
Currently, I am using
def eofapproached(f):
pos = f.tell()
near = f.read(1) == ''
f.seek(pos)
return near
to detect if a file open in 'r' mode (the default) is "at EOF" in the sense that the next read would produce the EOF condition.
I might use it like so:
f = open('filename.ext') # default 'r' mode
print(eofapproached(f))
FYI, I am working with some existing code that stops when EOF occurs, and I want my code to do some action just before that happens.
I am also interested in any suggestions for a better (e.g., more concise) function name. I thought of eofnear, but that does not necessarily convey as specific a meaning.
Currently, I use Python 3, but I may be forced to use Python 2 (part of a legacy system) in the future.
You can use f.tell() to find out your current position in the file.
The problem is, that you need to find out how big the file is.
The niave (and efficient) solution is os.path.getsize(filepath) and compare that to the result of tell() but that will return the size in bytes, which is only relavent if reading in binary mode ('rb') as your file may have multi-byte characters.
Your best solution is to seek to the end and back to find out the size.
def char_count(f):
current = f.tell()
f.seek(0, 2)
end = f.tell()
f.seek(current)
return end
def chars_left(f, length=None):
if not length:
length = char_count(f)
return length - f.tell()
Preferably, run char_count once at the beginning, and then pass that into chars_left. Seeking isn't efficient, but you need to know how long your file is in characters and the only way is by reading it.
If you are reading line by line, and want to know before reading the last line, you also have to know how long your last line is to see if you are at the beginning of the last line.
If you are reading line by line, and only want to know if the next line read will result in an EOF, then when chars_left(f, total) == 0 you know you are there (no more lines left to read)
I've formulated this code to avoid the use of tell (perhaps using tell is simpler):
import os
class NearEOFException(Exception): pass
def tellMe_before_EOF(filePath, chunk_size):
fileSize = os.path.getsize(filePath)
chunks_num = (fileSize // chunk_size) # how many chunks can we read from file?
reads = 0 # how many chunks we read so far
f = open(filePath)
if chunks_num == 0:
raise NearEOFException("File is near EOF")
for i in range(chunks_num-1):
yield f.read(chunk_size)
else:
raise NearEOFException("File is near EOF")
if __name__ == "__main__":
g = tellMe_before_EOF("xyz", 3) # read in chunks of 3 chars
while True:
print(next(g), end='') # near EOF raise NearEOFException
The naming of the function is disputed. It's boring to name things, I'm just not good at that.
The function works like this: take the size of the file and see approximately how many times can we read N sized chunks and store it in chunks_num. This simple division gets us near EOF, the question is where do you think near EOF is? Near the last char for example or near the last nth characters? Maybe that's something to keep in mind if it matters.
Trace through this code to see how it works.
I have an automated RAM tester that writes a test report for each Module it tests. the RAM tester keeps adding to the test report indefinitely. What I want to do is have Python read the report and look for the word "PASS" and the speed of the RAM.
Once the two words are found, I need Python to write to the serial port and clear the report so there is nothing in the .txt file. That way it is ready to loop around and read the next report from the next module tested.
The code is all written besides when Python is running the RAM tester will not write its report to the.txtfile. I have created a small program that takes a test report I captured from the RAM tester and writes it to the .txt file every 3 seconds and that works perfectly.
The program I am working on opens the.txtfile, finds the text my other program wrote to it, finds the 2 key words, deletes them, loops around and does it until I close the program like I want it to. I have done some trouble shooting with it by commenting out chunks of code and everything works until it runs the
file = open("yup.txt", "r+")
txt = file.read()
part, then the RAM tester fails to write the report. I think that loop is screwing it up by constantly accessing/reading the.txtfile...not too sure though. Also Python does not crash at all it just sits there in the loop so I have no problems as far as that goes.
Here is the code I'm having troubles with:
cache_size = os.lstat("yup.txt").st_size
print '\nsearching for number of characters in cache\n'
time.sleep(2)
if cache_size == 0:
print ('0 characters found in cache!\n')
time.sleep(1.5)
print ('there is no data to process!\n')
time.sleep(1.5)
print ('waiting for RAMBot\n')
if cache_size > 0:
print '%d characters found in cache!' % (cache_size)
time.sleep(1.5)
print ('\ndata analysis will now begin\n')
print('________________________________________________________________________________')
x = 1
while x == 1:
file = open("yup.txt" , "r+")
txt = file.read()
if "PASS" and '#2x400MHZ' in txt:
ser.write('4')
print('DDR2 PC-6400 (800MHz) module detected')
open('yup.txt' , 'w')
file.close()
if "PASS" and '#2x333MHZ' in txt:
ser.write('3')
print('DDR2 PC-5300 (667MHz) module detected')
open('yup.txt' , 'w')
file.close()
if "PASS" and '#2x266MHZ' in txt:
ser.write('2')
print('DDR2 PC-4200 (533MHz) module detected')
open('yup.txt' , 'w')
file.close()
if "PASS" and '#2x200MHZ' in txt:
ser.write('1')
print('DDR2 PC-3200 (400MHz) module detected')
open('yup.txt' , 'w')
file.close()
Here is a one of the test reports from the RAM tester:
Test No.: 1
Module : DDR2 256Mx72 2GB 2R(8)#2x333MHZ 1.8V
(Tested at 2x400MHz)
Addr.(rowxcol.) : 14 x 10
Data (rankxbit) : 2 x 72
Internal Banks : 8
Burst : Mode=Sequential, Length=8
AC parameters : CL=5, AL=0, Trcd=5, Trp=5
S/N from SPD : a128f4f3
Test Loop # : 1
Test Pattern : wA, wD, mt, mX, mC, mY, S.O.E
## PASS: Loop 1 ##
Elapsed Time : 00:00:53.448
Date : 09/26/2014, 16:07:40
Am not sure if this helps or not but here is the small program that I wrote to simulate the RAM tester writing its test reports to the.txtfile. I am still confused on why this works and the RAM tester writing the test report has problems...
import os
import time
Q = '''Test No.: 1
Module : DDR2 256Mx72 2GB 2R(8)#2x333MHZ 1.8V
(Tested at 2x400MHz)
Addr.(rowxcol.) : 14 x 10
Data (rankxbit) : 2 x 72
Internal Banks : 8
Burst : Mode=Sequential, Length=8
AC parameters : CL=5, AL=0, Trcd=5, Trp=5
S/N from SPD : a128f4f3
Test Loop # : 1
Test Pattern : wA, wD, mt, mX, mC, mY, S.O.E
## PASS: Loop 1 ##
Elapsed Time : 00:00:53.448
Date : 09/26/2014, 16:07:40'''
x = 1
while x == 1:
host = open('yup.txt' , 'w')
host.write(Q)
host.close()
time.sleep(3)
Thank you very much in advance, I really need to get this to work so it is much appreciated.
The problem is that on Windows, two programs generally can't have the same file open at the same time. When you try to open the file in w or r+ mode, you're asking it to open the file for exclusive access, meaning it will fail if someone else already has the file open, and it will block anyone else from opening the file.
If you want the specifics on sharing and locks in Windows, see the dwShareMode explanation in the CreateFile function on MSDN. (Of course you're not calling CreateFile, you're just using Python's open, which calls CreateFile for you—or, in older versions, calls fopen, which itself calls CreateFile.)
So, how do you work around this?
The simplest thing to do is just not keep the file open. Open the file, write it, and close it again. (Also, since you never write to file, why open it in r+ mode in the first place?)
You will also have to add some code that handles an OSError caused by the race condition of both programs trying to open and write the file at the exact same time, but that's just a simple try:/except: with a loop around it.
Could you just open the file with more permissive sharing?
Sure. You could, for example, use pywin32 to call CreateFile and WriteFile instead of using Python's open and write wrappers, and then you can pass any parameters you want for dwShareMode.
But think about what this means. What happens if both programs try to write the file at the same time? Who wins? If you're lucky, you lose one test output. If you're unlucky, script A blanks the file halfway through script B writing its test output, and you get a garbage file that you can't parse and throw an indecipherable and hard-to-reproduce exception. So, is that really what you want?
Meanwhile, you've got some other weird stuff in your code.
Why are you opening another handle to the same path just to truncate it? Why not just, say, file.truncate(0)? Doing another open while you still have file open in r+ mode means you end up conflicting with yourself, even if no other program was trying to use the same file.
You're also relying on some pretty odd behavior of the file pointer. You've read everything in file. You haven't seeked back to the start, or reopened the file. You've truncated the file and overwritten it with about the same amount of data. So when you read() again, you should get nothing, or maybe a few lines if the test reports aren't always the exact same length. The fact that you're actually getting the whole file is an unexpected consequence of some weird things Windows does in its C stdio library.
I'm working through the "Learn python the hard way" book, I've run into a problem in the Second Exercise of the 16th study drill. I am using the newest version of python 3.
The code I wrote looks like this so far:
from sys import argv
script, filename = argv #we input the filename into argv
print ("We're going to erase %r." % filename) #tells us the name of the file we're deleting
print ("If you don't want that, hit CTRL-C (^C)")
print ("If you do want that, hit RETURN.")
input("?") #look into error unexpected EOF while parsing, I had to type "", if I don't program ends here
print ("Opening the file...")
target = open(filename, "w") #opens the file
print ("Truncating the file. Goodbye!")
target.truncate() #erases everything in the file
print ("Now I'm going to ask you for three lines")
line1 = input("line 1: ")
line2 = input("line 2: ")
line3 = input("line 3: ")
#summary: we create 3 input variables we'll use under here
print ("I'm going to write these to the file.")
target.write(line1) #summary: we write in the terminal what we want in our file and use that
target.write("\n") #we also have this newline command after every line to make sure it's not all in one line
target.write(line2)
target.write("\n")
target.write(line3)
target.write("\n")
#The default code is done at this point
print ("Type the filename again and I'll read it back to you or press CTRL + C (^C) to exit")
file_again = input(">")
print (file_again) #Added to try to see what this gave me, it gives nothing back --- isn't working, why?
print ("Now I'll read the file back to you!") #This prints
text_again = open(file_again) #Not sure if this is working
print (text_again.read()) #Is doing nothing in the Terminal
print("And finally we close the file") #Works
target.close()
At this point I'm wondering why the stuff after "#The default code is done at this point" is not working, I ripped it exactly as-is from another program (Exercise 15 of the same book), my Command line looks like this:
C:\PATH\Study Drills>py SD8.py "test.txt"
We're going to erase 'test.txt'.
If you don't want that, hit CTRL-C (^C)
If you do want that, hit RETURN.
?""
Opening the file...
Truncating the file. Goodbye!
Now I'm going to ask you for three lines
line 1: "Hey"
line 2: "yo"
line 3: "sup"
I'm going to write these to the file.
Type the filename again and I'll read it back to you or press CTRL + C (^C) to exit
>"test.txt"
Now I'll read the file back to you!
BONUS QUESTION: In the point where I say ("#look into error unexpected EOF while parsing, I had to type "", if I don't program ends here") why do I have to put quotation marks? If I don't the program ends with the error message mentioned in the comment.
Once you're done with all your write calls, close() the file. Due to things like OS buffering, the data is not guaranteed to be actually written to the file until you close the file object (which is done automatically when you exit the program, as you might have noticed -- kill your program and the data will be in the file).
In addition, your truncate call is unnecessary -- opening a file in "w" mode immediately truncates it.
I have a simple bash command here for a script that I am re-writing in Python, and I've done a lot of searching and haven't found a simple answer. I am trying to echo the output of Print to a file, making sure there are no line breaks and that I can pass a variable into it. Here is just a little snippet (there are a lot of lines like this):
echo " ServerName www.${hostName}" >> $prjFile
Now I know it would end up looking something like:
print ("ServerName www.", hostName) >> prjFile
Right? But that doesn't work. Mind you, this is in Python 2.6 (as the machine this script will run on is using that version and there are other dependencies reliant on sticking with that version).
The syntax is;
print >>myfile, "ServerName www.", hostName,
where myfile is a file object opened in mode "a" (for "append").
The trailing comma prevents line breaks.
You might also want to use sys.stdout.softspace = False to prevent the spaces that Python adds between comma-separate arguments to print, and/or to print things as a single string:
print >>myfile, "ServerName www.%s" % hostName,
You can try a simple:
myFile = open('/tmp/result.file', 'w') # or 'a' to add text instead of truncate
myFile.write('whatever')
myFile.close()
In your case:
myFile = open(prjFile, 'a') # 'a' because you want to add to the existing file
myFile.write('ServerName www.{hostname}'.format(hostname=hostname))
myFile.close()