Here is a simple example of file I/O (input/output):
# Write a filewithopen("test.txt","wt")asout_file:out_file.write("This Text is going to out file\nLook at it and see!")# Read a filewithopen("test.txt","rt")asin_file:text=in_file.read()print(text)
The output and the contents of the filetest.txt are:
This Text is going to out fileLook at it and see!
Notice that it wrote a file calledtest.txt in the directory that you ran the program from. The\n in the string tells Python to put anewline where it is.
A overview of file I/O is:
open functionwith to open the file, you'd have to close it manuallyThe first step is to get a file object. The way to do this is to use theopen function. The format isfile_object = open(filename, mode) wherefile_object is the variable to put the file object,filename is a string with the filename, andmode is"rt" toread a file astext or"wt" towrite a file astext (and a few others we will skip here). Next the file objects functions can be called. The two most common functions areread andwrite. Thewrite function adds a string to the end of the file. Theread function reads the next thing in the file and returns it as a string. If no argument is given it will return the whole file (as done in the example).
Now here is a new version of the phone numbers program that we made earlier:
defprint_numbers(numbers):print("Telephone Numbers:")fork,vinnumbers.items():print("Name:",k,"\tNumber:",v)print()defadd_number(numbers,name,number):numbers[name]=numberdeflookup_number(numbers,name):ifnameinnumbers:return"The number is "+numbers[name]else:returnname+" was not found"defremove_number(numbers,name):ifnameinnumbers:delnumbers[name]else:print(name," was not found")defload_numbers(numbers,filename):in_file=open(filename,"rt")whileTrue:in_line=in_file.readline()ifnotin_line:breakin_line=in_line[:-1]name,number=in_line.split(",")numbers[name]=numberin_file.close()defsave_numbers(numbers,filename):out_file=open(filename,"wt")fork,vinnumbers.items():out_file.write(k+","+v+"\n")out_file.close()defprint_menu():print('1. Print Phone Numbers')print('2. Add a Phone Number')print('3. Remove a Phone Number')print('4. Lookup a Phone Number')print('5. Load numbers')print('6. Save numbers')print('7. Quit')print()phone_list={}menu_choice=0print_menu()whileTrue:menu_choice=int(input("Type in a number (1-7): "))ifmenu_choice==1:print_numbers(phone_list)elifmenu_choice==2:print("Add Name and Number")name=input("Name: ")phone=input("Number: ")add_number(phone_list,name,phone)elifmenu_choice==3:print("Remove Name and Number")name=input("Name: ")remove_number(phone_list,name)elifmenu_choice==4:print("Lookup Number")name=input("Name: ")print(lookup_number(phone_list,name))elifmenu_choice==5:filename=input("Filename to load: ")load_numbers(phone_list,filename)elifmenu_choice==6:filename=input("Filename to save: ")save_numbers(phone_list,filename)elifmenu_choice==7:breakelse:print_menu()print("Goodbye")
Notice that it now includes saving and loading files. Here is some output of my running it twice:
1. Print Phone Numbers2. Add a Phone Number3. Remove a Phone Number4. Lookup a Phone Number5. Load numbers6. Save numbers7. QuitType in a number (1-7):2Add Name and NumberName:JillNumber:1234Type in a number (1-7):2Add Name and NumberName:FredNumber:4321Type in a number (1-7):1Telephone Numbers:Name: Jill Number: 1234Name: Fred Number: 4321Type in a number (1-7):6Filename to save:numbers.txtType in a number (1-7):7Goodbye
1. Print Phone Numbers2. Add a Phone Number3. Remove a Phone Number4. Lookup a Phone Number5. Load numbers6. Save numbers7. QuitType in a number (1-7):5Filename to load:numbers.txtType in a number (1-7):1Telephone Numbers:Name: Jill Number: 1234Name: Fred Number: 4321Type in a number (1-7):7Goodbye
The new portions of this program are:
defload_numbers(numbers,filename):in_file=open(filename,"rt")whileTrue:in_line=in_file.readline()ifnotin_line:breakin_line=in_line[:-1]name,number=in_line.split(",")numbers[name]=numberin_file.close()defsave_numbers(numbers,filename):out_file=open(filename,"wt")fork,vinnumbers.values():out_file.write(k+","+v+"\n")out_file.close()
First we will look at the save portion of the program. First it creates a file object with the commandopen(filename, "wt"). Next it goes through and creates a line for each of the phone numbers with the commandout_file.write(x + "," + numbers[x] + "\n"). This writes out a line that contains the name, a comma, the number and follows it by a newline.
The loading portion is a little more complicated. It starts by getting a file object. Then it uses awhile True: loop to keep looping until abreak statement is encountered. Next it gets a line with the linein_line = in_file.readline(). Thereadline function will return an empty string when the end of the file is reached. Theif statement checks for this andbreaks out of thewhile loop when that happens. Of course if thereadline function did not return the newline at the end of the line there would be no way to tell if an empty string was an empty line or the end of the file so the newline is left in whatreadline returns. Hence we have to get rid of the newline. The linein_line = in_line[:-1] does this for us by dropping the last character. Next the linename, number = in_line.split(",") splits the line at the comma into a name and a number. This is then added to thenumbers dictionary.
You might be saying to yourself, "Well I know how to read and write to a textfile, but what if I want to print the file without opening out another program?"
There are a few different ways to accomplish this. The easiest way does open another program, but everything is taken care of in the Python code, and doesn't require the user to specify a file to be printed. This method involves invoking the subprocess of another program.
Remember the file we wrote output to in the above program? Let's use that file. Keep in mind, in order to prevent some errors, this program uses concepts from the Next chapter. Please feel free to revisit this example after the next chapter.
importsubprocessdefmain():try:print("This small program invokes the print function in the Notepad application")#Lets print the file we created in the program abovesubprocess.call(['notepad','/p','numbers.txt'])exceptWindowsError:print("The called subprocess does not exist, or cannot be called.")main()
Thesubprocess.call takes three arguments. The first argument in the context of this example, should be the name of the program which you would like to invoke the printing subprocess from. The second argument should be the specific subprocess within that program. For simplicity, just understand that in this program,'/p' is the subprocess used to access your printer through the specified application. The last argument should be the name of the file you want to send to the printing subprocess. In this case, it is the same file used earlier in this chapter.
This section will demonstrate how we can store simple melodies in a file. We can then write a program that reads the file and plays the melody on a Linkbot.
To fully understand the sample program, we will need a little knowledge about music theory. If you don't care about music theory and you just want to get the Linkbot to play music, you can skip to the next section.
What is music? Music is a series of notes played in a certain order. Sometimes, more than one note is played together. Each note is played for a certain duration. Notes themselves are vibrations in the air that we can hear. Each note has a certain frequency of vibration; when the frequency changes, the perceived pitch of the note changes too.
Each note has a name. If you know it's name, you can find it on a piano keyboard, and vice versa. You may have heard the term "Middle-C", or the phrase "Do-Re-Mi". These are both ways to refer to notes. If you are familiar with a piano keyboard, you will know that the "C" note appears more than once on the keyboard. When you play the C notes, you'll notice that they don't sound the same, but they are all called "C". It turns out that there are 12 tones that repeat over and over again. Starting at C, they are:
The "#" symbol is pronounced "sharp", so C# is pronounced "See-Sharp". The "sharp" indicates that the tone is one step higher than the normal note. For instance, "A#" is one step higher than "A". You might also be familiar with another symbol that looks like a lowercase b. That symbol is pronounced "flat", so "Bb" is pronounced "B-flat". It has the opposite meaning of sharp, indicating that the note is one step lower than the un-flatted note. This means that the note "G#" is actually the same note as "Ab". For now, we will only use sharps to avoid confusion. On a piano keyboard, all of the sharp/flat notes are the black keys and the rest of the notes are the white keys.
On a piano keyboard, these notes repeat over and over again, so we must devise a way to specifically refer which A or B or C we are referring to. To do this, we introduce the concept of an octave number. The lowest note a piano can play is called "A0", where 0 is the octave number. Going from left to right on a piano keyboard, the octave number increases every time you encounter the C note. Thus, the first several white keys on a piano keyboard from left to right are:
Now, we have a way to specifyexactly what key on a keyboard we are referring to using a string such as "F#5".
We also have an equation where we can calculate the frequency of a note depending on how many steps away it is from our "A0" note.The equation is:
For instance, G0 is -2 steps away from A0, and B0 is 2 steps away from A0. For notes in the same octave, we can simply count the number of black and white keys to find the distance away from A, but how about different octaves? For instance, how many keys away is A0 from B4?
First, lets consider how many notes away A4 is from A0. When we count the keys, each octave is 12 notes. Since A4 is 4 octave away from 0 (4-0 = 4), A4 is 4*12=48 keys away from A0.
How about A0 and B4? A0 is 2 keys away from B0, and B0 is 4*12 keys away from B4, so in total, A0 is 2+4*12 = 50 keys away from B4. Now, we can write an equation to figure out exactly how many keys away any note is from A0. Lets use the variable for the note name we want, and for the octave of the note. Then,
First, let us write a function that takes a string describing a note such as "A4" and gives us a frequency for that note. Here is our strategy:
Next, lets talk about our file format. Since the Linkbot can only play one note at a time, the file should specify single notes for the Linkbot to play. Also, the file should specify how many seconds to play each note. We choose to have our file such that each line contains a note name such as "C4" or "Bb3", along with a note duration in seconds.
Here is a file that we have created for you:
fur_elise.txt
E5 0.125 D#5 0.125 E5 0.125 D#5 0.125 E5 0.125 B4 0.125 D5 0.125 C5 0.125 A4 0.375 C4 0.125 E4 0.125 A4 0.125 B4 0.375 G#3 0.125 G#4 0.125 B4 0.125 C5 0.375 E4 0.125 E5 0.125 D#5 0.125 E5 0.125 D#5 0.125 E5 0.125 B4 0.125 D5 0.125 C5 0.125 A4 0.375 C4 0.125 E4 0.125 A4 0.125 B4 0.375 E4 0.125 C5 0.125 B4 0.125 A4 0.375
Go ahead and copy-paste the text into a file called "fur_elise.txt". Lets write our function and program that will read this file and play a tune.
importtimeimportbarobodongle=barobo.Dongle()dongle.connect()myLinkbot=dongle.getLinkbot()defnoteToFreq(note):# First, we need to figure out where the note is relative to 'A'.# For instance, 'B' is 2 half-steps above A, 'C' is 9 half-steps# below 'A'. Lets create a dictionary called "note-offset" and store# all of our offsets from A in the dictionary.noteOffsets={'C':-9,'D':-7,'E':-5,'F':-4,'G':-2,'A':0,'B':2}# Find our offsetoffset=noteOffsets[note[0].upper()]# 1# See if there is a sharp or flatifnote[1]=='#':offset+=1elifnote[1]=='b':offset-=1# Calculate the offset based on the octaveoctave=int(note[-1])# 2offset+=(octave)*12# Calculate the note frequencyfreq=2**((offset)/12)*27.5returnfreqmusicFile=open('fur_elise.txt','r')forlineinmusicFile:# 3data=line.split()# 4myLinkbot.setBuzzerFrequency(noteToFreq(data[0]))time.sleep(float(data[1]))myLinkbot.setBuzzerFrequency(0)
note[0].upper() takes the first character and uppercases it. We want to uppercase anything that comes in just in case the user used a lowercase note name, like "e4". Since we wrote our dictionary with uppercase note names, we need to make sure the incoming note names are upper-case too so that it can be matched with the ones in our dictionary.int.line variable.split() function splits lines of text based on whitespace. For instance,"Hello there".split() becomes the list ["Hello", "there"]. Since each line in our text file has 2 "words", the first part (the note name) is stored into our variabledata[0], and the second part (the note duration) is stored intodata[1].Now modify the grades program from sectionDictionaries so that is uses file I/O to keep a record of the students.
Now modify the grades program from sectionDictionaries so that is uses file I/O to keep a record of the students.
assignments=['hw ch 1','hw ch 2','quiz ','hw ch 3','test']students={}defload_grades(gradesfile):inputfile=open(gradesfile,"r")grades=[]whileTrue:student_and_grade=inputfile.readline()student_and_grade=student_and_grade[:-1]ifnotstudent_and_grade:breakelse:studentname,studentgrades=student_and_grade.split(",")studentgrades=studentgrades.split(" ")students[studentname]=studentgradesinputfile.close()print("Grades loaded.")defsave_grades(gradesfile):outputfile=open(gradesfile,"w")fork,vinstudents.values():outputfile.write(k+",")forxinv:outputfile.write(x+" ")outputfile.write("\n")outputfile.close()print("Grades saved.")defprint_menu():print("1. Add student")print("2. Remove student")print("3. Load grades")print("4. Record grade")print("5. Print grades")print("6. Save grades")print("7. Print Menu")print("9. Quit")defprint_all_grades():ifstudents:keys=sorted(students.keys())print('\t',end=' ')forxinassignments:print(x,'\t',end=' ')print()forxinkeys:print(x,'\t',end=' ')grades=students[x]print_grades(grades)else:print("There are no grades to print.")defprint_grades(grades):forxingrades:print(x,'\t',end=' ')print()print_menu()menu_choice=0whilemenu_choice!=9:print()menu_choice=int(input("Menu Choice: "))ifmenu_choice==1:name=input("Student to add: ")students[name]=[0]*len(assignments)elifmenu_choice==2:name=input("Student to remove: ")ifnameinstudents:delstudents[name]else:print("Student:",name,"not found")elifmenu_choice==3:gradesfile=input("Load grades from which file? ")load_grades(gradesfile)elifmenu_choice==4:print("Record Grade")name=input("Student: ")ifnameinstudents:grades=students[name]print("Type in the number of the grade to record")print("Type a 0 (zero) to exit")fori,xinenumerate(assignments):print(i+1,x,'\t',end=' ')print()print_grades(grades)which=1234whilewhich!=-1:which=int(input("Change which Grade: "))which-=1if0<=which<len(grades):grade=input("Grade: ")# Change from float(input()) to input() to avoid an error when savinggrades[which]=gradeelifwhich!=-1:print("Invalid Grade Number")else:print("Student not found")elifmenu_choice==5:print_all_grades()elifmenu_choice==6:gradesfile=input("Save grades to which file? ")save_grades(gradesfile)elifmenu_choice!=9:print_menu()
Write a program that reads a text file containing motor angles, such as
90 45 20180 22 -1805 32 0
Each triplet of numbers represents three motor angles. Write a program that moves the Linkbot's joints to those positions with themoveTo() function for each line in the data file.
| Learning Python 3 with the Linkbot | ||
| ← Revenge of the Strings | File IO | Dealing with the imperfect → |