Learning Python 3 with the Linkbot/File IO

File I/O
Here is a simple example of file I/O (input/output):

The output and the contents of the file  are:

This Text is going to out file Look at it and see!

Notice that it wrote a file called  in the directory that you ran the program from. The  in the string tells Python to put a newline where it is.

A overview of file I/O is:
 * Get a file object with the  function
 * Read or write to the file object (depending on how it was opened)
 * If you did not use  to open the file, you'd have to close it manually

The first step is to get a file object. The way to do this is to use the  function. The format is   where   is the variable to put the file object,   is a string with the filename, and   is   to read a file as text or   to write a file as text (and a few others we will skip here). Next the file objects functions can be called. The two most common functions are  and. The  function adds a string to the end of the file. The  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:

Notice that it now includes saving and loading files. Here is some output of my running it twice:

1. Print Phone Numbers 2. Add a Phone Number 3. Remove a Phone Number 4. Lookup a Phone Number 5. Load numbers 6. Save numbers 7. Quit Type in a number (1-7): 2 Add Name and Number Name: Jill Number: 1234 Type in a number (1-7): 2 Add Name and Number Name: Fred Number: 4321 Type in a number (1-7): 1 Telephone Numbers: Name: Jill    Number: 1234 Name: Fred    Number: 4321 Type in a number (1-7): 6 Filename to save: numbers.txt Type in a number (1-7): 7 Goodbye

1. Print Phone Numbers 2. Add a Phone Number 3. Remove a Phone Number 4. Lookup a Phone Number 5. Load numbers 6. Save numbers 7. Quit Type in a number (1-7): 5 Filename to load: numbers.txt Type in a number (1-7): 1 Telephone Numbers: Name: Jill    Number: 1234 Name: Fred    Number: 4321 Type in a number (1-7): 7 Goodbye

The new portions of this program are:

First we will look at the save portion of the program. First it creates a file object with the command. Next it goes through and creates a line for each of the phone numbers with the command. 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 a  loop to keep looping until a   statement is encountered. Next it gets a line with the line. The  function will return an empty string when the end of the file is reached. The  statement checks for this and  s out of the   loop when that happens. Of course if the  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 what   returns. Hence we have to get rid of the newline. The line  does this for us by dropping the last character. Next the line  splits the line at the comma into a name and a number. This is then added to the  dictionary.

Advanced use of .txt files
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.

The  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,  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.

Storing and loading music saved in a text file
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.

A little music theory
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:
 * C
 * C# (Db)
 * D
 * D# (Eb)
 * E
 * F
 * F# (Gb)
 * G
 * G# (Ab)
 * A
 * A# (Bb)
 * B

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:
 * A0
 * B0
 * C1
 * D1
 * E1
 * F1
 * G1
 * A1
 * B1
 * C2
 * etc...

Now, we have a way to specify exactly 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:

$$ \mathrm{frequency} = 27.5 * 2^{\mathrm{steps}/12} $$

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 $$n$$ for the note name we want, and $$v$$ for the octave of the note. Then,

$$ \mathrm{offset}(n, v) = n-\mathrm(A0) + 12*v $$

Reading and playing a melody from a file
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:
 * We receive a string describing the note. The first character is the letter name of the note. We calculate the offset of that letter name from the "A" note. For instance, "B" would have an offset of +2 and "E" would have an offset of -5. We find these offsets simply by counting the keys on a keyboard.
 * The next character in the string might be a "#" sharp or a "b" flat. If it is sharp, increase the offset by one. If it is flat, decrease it by one. If it is neither, don't do anything.
 * The final character is the octave number. We multiply this number by 12 and add it to our offset.
 * The result is the number of keys away the note is away from A0. We can now use the equation $$ f = 27.5 * 2^{\mathrm{offset}/12} $$ to find the frequency based on the key offset from A0.

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.


 * 1)   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.
 * 2) The "-1" index means the last item of a list. Since all of the items in our list are strings, we need to convert it to an integer with.
 * 3) This loop goes through every single line in the music file, line by line. Each time, it stores the text of the line in the   variable.
 * 4) The   function splits lines of text based on whitespace. For instance,   becomes the list  . Since each line in our text file has 2 "words", the first part (the note name) is stored into our variable , and the second part (the note duration) is stored into.

Exercises
Now modify the grades program from section Dictionaries so that is uses file I/O to keep a record of the students.

Write a program that reads a text file containing motor angles, such as 90 45 20 180 22 -180 5 32 0 Each triplet of numbers represents three motor angles. Write a program that moves the Linkbot's joints to those positions with the  function for each line in the data file.