CS 105 - Lab 8 Today, you will be doing a lot with files! We will start with the basics of reading and writing and then expand upon the pixel value work that you did in a previous lab by working on image processing with ASCII art. Let's get to it with the basics of file I/O. I recommend that you try out Spyder instead of IDLE for today s work. On your USB drive or account, create a new folder called lab08, and put all of today s work in it. Files to download from the class Web site: alice.txt, input.txt, image.bmp Periodically during the lab, please have one of us check your progress. Have fun! Part 1 - Basics of File I/O Open up Spyder and create a new file. Enter the following code and save the file as file_writing.py. Finally, run the code from the Run menu. file = open('myfile.txt', 'w') file.write('hello, world!') file.close() What happened when you ran this script? Create a new file in the same directory called file_reading.py and put the following text in it: file = open('myfile.txt', 'r') text = file.read() print(text) file.close() What happens now? Try opening myfile.txt in a text editor and changing its contents. Does this change the output of the program? What do you think the 'r' and 'w' arguments mean in the open function? Let's try opening a file that doesn't exist. Modify file_reading.py to open nonexist.txt instead of myfile.txt and now run it again. You should get an error:
FileNotFoundError: [Errno 2] No such file or directory: 'nonexist.txt' You cannot open files for reading when they don't exist. When writing a new file, the file will be created if it doesn't exist, or overwritten if it does exist. To fix this problem, it is good practice to check whether a file exists before trying to open it. Luckily for us, Python has a way of doing this already using the os.path module. Modify file_reading.py to read like this: import os.path if os.path.isfile('nonexist.txt'): file = open('nonexist.txt', 'r') text = file.read() print(text) file.close() else: print("this file cannot be found!") Now, when you run the file, you gracefully recover from errors (meaning your program deals with problems and doesn't just crash when something goes wrong). This will help you with the next part of the lab, in which we create a simple program to remember certain things about the user. Run file_reading.py again to verify this, show your professor or aide, and move onto part 3 when finished! Part 2 - Remember Me! Now it's time to put your file skills to the test. Create a new program called remember_me.py which does the following: If the file user.txt exists: Open the file and store its contents in a variable called name. (hint: file_reading.py shows you how) Close the file. Ask the user if their name is what was stored in the text file. If so, greet them by name. print('hi there,' + name + '!') If not, ask the user to input a new name and save it in user.txt for next time.* End the program. Otherwise: Ask the user for their name and put it into a variable.* Create a new file called user.txt and put the name into this file.* Close the file.* *Note: these steps appear whether or not the user.txt file exists. Instead of typing them twice, consider combining them into a subroutine. def make_new_user() could be the name for your subroutine. Here's some skeleton code to get you started: # remember_me.py # This program will store some information about the last person who used it # into a text file, and will greet the user when they next log in. # Imports import os.path # Functions and Subroutines def make_new_user(): # Greet the user and ask for their name and age
# Open up a text file (called user.txt) in write mode. # Write the name to the file # Close the file # MAIN PROGRAM: # If no file exists, then nobody has used the program before. # Greet the user and make a new text file. if not os.path.isfile('user.txt'): make_new_user() else: # Open up the user's file in read mode and load his/her name # Read everything from the file into a string # Close the file # Ask if this is the correct user # Greet the user if they chose yes. If not, make_new_user(). Complete the code and make sure that it runs correctly, then move onto Part 3. Part 3 - ASCII Art! Now that you have a good understanding of file I/O, we're going to look at something entirely different: text art! In your last lab, you read in pixel values that you used to determine whether a certain value was light or dark. In this new lab, you will be converting each pixel from a bitmap file to a Unicode character. Viewed from a distance, this will appear as art! The following is an example of the output from the finished program: A brief introduction to Unicode Unicode is a universal character encoding system that supersedes older encoding standards such as ASCII. Most languages can be represented in Unicode. Python 3 uses Unicode as its standard encoding language. Let's try converting between regular text and Unicode. The ord() function will convert a single character to its
Unicode equivalent. Go to the Spyder shell and type the following: >>> ord('a') >>> ord('b') >>> ord('c') >>> ord('a') >>> ord('b') >>> ord('c') What are the numbers you get back? Use the ord() function as above to get the Unicode equivalent for your first name. Write in the values here. The following is a copy of the complete ASCII table.
Convert your first name into ASCII using the table above and write them down. Use the 'Chr' and 'Dec' values. Compare this sequence to the one you got in the previous question. Notice how the Unicode and ASCII values are identical for letters. Why do you think this might be? ASCII Blocks Now, let's get into the art! The main idea is that we're going to take a pixel and convert it to one of a few possible characters. These characters are: Nearest color Character Python Character Code White ' ' Light '\u2591' Medium '\u2592' Dark '\u2593' Black '\u2588' You can see these codes in action by typing print('\u2591') and the like into a shell. Let's make the program now. We're going to be using bitmap image files and the Python Imaging Library (PIL) to do the hard work of reading bitmap files pixel by pixel. Each pixel will consist of a tuple of (red,green,blue) values. You are going to be averaging these together and coming up with a single value, the lightness of the pixel. Depending on the lightness, you will either output black, dark, medium, light, or white Unicode blocks. First, download an image that is less than 64 pixels wide and 40 pixels tall. You can find a few on the class website or you can search for one on Google Images. You can even make one in Microsoft Paint. Save this image as a bitmap and call it image.bmp Create a new file called text_art.py and put the following into it. Be absolutely sure you are using a bitmap. The Python Imaging Library won't work on these computers with any other image types. # Imports from PIL import Image # Declarations white = ' ' light = '\u2591' medium = '\u2592' dark = '\u2593' black = '\u2588' # Open image
im = Image.open('image.bmp') # file name can be changed to whatever you use. im = im.convert('rgb') width, height = im.size # Convert the image for i in range(height): for j in range(width): # Get all three values (red, green, and blue) r, g, b = im.getpixel((j,i)) # Calculate the average of r, g, and b as an integer average = # Decide which text character to print. # average will be a value from 0 to 255 if <= average <= : print(black, end='') # Black elif <= average <= : print(dark, end='') # Dark Gray elif <= average <= : print(medium, end='') # Medium Gray elif <= average <= : print(light, end='') # Light Gray else: print(white, end='') # White # Finish off the line print() Replace the blanks ( ) with code. For the average line, use a formula or a built-in function. For the if statements, use numbers. Run your program and observe the output. Modify it until your ranges choose the characters that most resemble the light values in your image. Which ranges did you finally decide on? Write them in the blanks above as well as your average function. Show your professor or aide when finished! Part 4 Next, you will write a couple of Python programs that will practice steganography. This means we are going to hide a secret message inside an otherwise innocent looking text file. Here are the steps to follow. Open your folder lab08. From the class Web site, please download a large input file called input.txt and another called alice.txt. The first one is a famous speech given by President Reagan in 1987 in Berlin. We want to write two programs: Let's write steg.py. This program will hide a short secret message, among the characters of the speech (input.txt), and it will write the resulting output to a new file, output.txt. This program will have the following structure. Create a variable called secretmessage, and set it equal to some sentence of your choice. Open the input and output files, and associate each one with a variable. Read the entire input file into a string variable. For example, the name of this string variable could be text.
Write a loop that examines each character in the string. The loop needs to be written in such a way that the variable i refers to which letter in the string we are looking at. For example, i = 0 is the first character, i = 1 is the next character, and so on. In other words, all letters in the input file are numbered, all the way up to the total number of bytes in the file, which in this case is about 15,000. The purpose of the loop is to copy the input file into the output file. But we have to hide our secret message. How? Let's say that every 100th character of the input file is going to be replaced with the next character of the secret message. Let me show you how this is done: if i % 100 == 0 and i /100 < len(secretmessage): outfile.write(secretmessage[int(i/100)]) else: outfile.write(text[i]) At the end of the program, tell Python to close the files. Run the program steg.py and look at the output.txt file. Can you tell if anything has changed relative to the input.txt file? If so, what is different? Let s write steg2.py: This program will attempt to discover the hidden message that is contained inside output.txt. The program will print the secret message to the screen. In other words, steg2.py only needs to open 1 file. At the beginning of the program, create an empty string called secretmessage. This is where we are going to store the message that was left for us in output.txt. This program will have the same general structure as steg.py. But the idea here is that on every 100th character of the text, the program needs to grab this character and place it in our secretmessage. For instance, try this if-statement inside your loop that looks at every character in the text: if i % 100 == 0: secretmessage += text[i] Finally, at the end of the program, print the secretmessage to the screen. Run steg2.py. Is the secret message being printed out correctly? Why is the program printing more text than the length of this message? Changing every 100th character in the input file is not stealthy. It s especially obvious to a casual reader if there is an alteration very early in the text file, such as in the first few words. It s also better to hide our message inside a larger body of text. How about a whole book?
Copy steg.py and steg2.py into new files steg3.py and steg4.py, respectively. Change steg3.py so that the input file is alice.txt instead of input.txt. The output file should be alice_output.txt instead of output.txt. Modify steg3.py and steg4.py so that they do not change letters so often. Instead, let s change every 1,000th character. Also, if we write a condition that says that we are going to modify a character if i % 1000 == 0, this statement is true when i is zero. This means we will be changing the first letter in the text file. Does this sound like a good idea? Why / why not? Instead, let s arrange for the program to change the following characters. Pick some number from 1-1000, say 687 (not zero or a very small number). In this case, we ll change the 687th character; and then every 1000th character after that. In other words, we want to ask if the remainder of dividing i by 1000 is 687, rather than zero. Modify steg3.py and steg4.py accordingly, and verify that the secret message is preserved. You should look at output.txt to see that it s a little more difficult to find letters of your secret message. Where are its first three letters? Experiment with a third input file. Especially a file that is large and already has a lot of typos or other weird looking text in it. Also use a different secret message and a different formula for placing characters in the output file. Create complementary programs steg5.py and steg6.py (analogous to our original steg.py and steg2.py) to do the encoding and decoding, respectively. Be stealthy! That's it! Have the professor or aide check your work and you can leave.