CS108
Spring 2024

Assignment 13: String objects; Working with files

due by 9:00 p.m. EST on Thursday 3/7/24

Preliminaries

In your work on this assignment, make sure to abide by the collaboration policies of the course.

For each problem in this problem set, we will be writing or evaluating some Python code. You are encouraged to use the Spyder IDE which will be discussed/presented in class, but you are welcome to use another IDE if you choose.

If you have questions while working on this assignment, please post them on Piazza! This is the best way to get a quick response from your classmates and the course staff.

Programming Guidelines

  • Refer to the class Coding Standards for important style guidelines. The grader will be awarding/deducting points for writing code that comforms to these standards.

  • Every program file must begin with a descriptive header comment that includes your name, username/BU email, and a brief description of the work contained in the file.

  • Every function must include a descriptive docstring that explains what the function does and identifies/defines each of the parameters to the function.

  • Your functions must have the exact names specified below, or we won’t be able to test them. Note in particular that the case of the letters matters (all of them should be lowercase), and that some of the names include an underscore character (_).

  • Make sure that your functions return the specified value, rather than printing it. None of these functions should use a print statement.

  • If a function takes more than one input, you must keep the inputs in the order that we have specified.

  • You should not use any Python features that we have not discussed in class or read about in the textbook.

  • Your functions do not need to handle bad inputs – inputs with a type or value that doesn’t correspond to the description of the inputs provided in the problem.

  • You must test your work before you submit it You can prove to yourself whether it works correctly – or not – and make corrections before submission. If you need help testing your code, please ask the course staff!

  • Do not submit work with syntax errors. Syntax errors will cause the Gradescope autograder to fail, resulting in a grade of 0.

Warnings: Individual Work and Academic Conduct!!

  • This is an individual assignment. You may discuss the problem statement/requirements, Python syntax, test cases, and error messages with your classmates. However, each student must write their own code without copying or referring to other student’s work.

  • It is strictly forbidden to use any code that you find from online websites including but not limited to as CourseHero, Chegg, or any other sites that publish homework solutions.

  • It is strictly forbidden to use any generative AI (e.g., ChatGPT or any similar tools**) to write solutions for for any assignment.

Students who submit work that is not authentically their own individual work will earn a grade of 0 on this assignment and a reprimand from the office of the Dean.

If you have questions while working on this assignment, please post them on Piazza! This is the best way to get a quick response from your classmates and the course staff.


Task 1: Using string methods

30 points; individual-only

This task will give you practice with using the methods that are inside every string object.

Begin by downloading this file: a13_strings.py.

When you open the file in Spyder, you’ll see that we’ve given you the following strings:

s1 = 'Three little kittens lost their mittens'
s2 = 'Star light, star bright'
s3 = 'Nothing YOU can do, but YOU can learn how to be YOU in time'

We have also given you the solution to the first puzzle.

Warmup
Run a13_strings.py in Spyder, so that the strings s1 and s2 will be available to you in the Python Shell (i.e., the interactive console).

Next, enter the following method calls and other expressions from the Shell, and take note of the values that are returned:

>>> s1.upper()
>>> s1
>>> s2.lower()
>>> s2
>>> s1.title()
>>> s1
>>> s2.count('s')
>>> s2.lower().count('s')
>>> s1.count('tt')
>>> s1.replace('th', 'f')
>>> s1.lower().replace('th', 'f')
>>> s2.replace('r', 'x')
>>> s2.replace('ar', 'amp')
>>> s1
>>> s2
  1. Write a function to_upper_case(s, word) that will process a string variable s, and return a new string in which all occurrences of the string word have been changed to upper case, but all other words remain unchanged.

    For example:

    >>> s1 = 'Three little kittens lost their mittens'  
    >>> to_upper_case(s1, 'little')
    Three LITTLE kittens lost their mittens
    >>> to_upper_case(s1, 'tt')
    Three liTTle kiTTens lost their miTTens
    

    You can accomplish this using only methods on the string object. Do not use any loops or the accumulator pattern.

    After you get the “basic” version working, you should re-write it to handle some special cases, such as when the word to replace is in all lower case, or in title case (first letter capitalized).

    For example:

    >>> s2 = 'Star light, star bright'
    >>> to_upper_case(s2, 'star')
    STAR light, STAR bright
    
  2. Write a function count_vowels(s) that will return the count of all vowels in the string s. You have written a function like this before, but now you must use the string method count. You may not iterate over each character in the string, as we did previously.

    For example:

    >>> s1 = 'Three little kittens lost their mittens'  
    >>> count_vowels(s1)
    11
    

    The function should work just as well with either upper or lower case letters:

    For example:

    >>> s2 = 'Star light, star bright'
    >>> count_vowels(s2.upper())
    4
    >>> s3 = 'Nothing YOU can do, but YOU can learn how to be YOU in time'
    >>> count_vowels(s3)
    20
    

Task 2: Reading and writing files

50 points; individual-only

In this task, you will work with some text files to read/process the contents, as well as to create a new (output) file. The simplest way to use text files from within Python is to put the files in the same directory (folder) as your Python code files.

Here are some text files for you to download and work with:

You may also use any other (ASCII-only) text file as well.

Do all of the work for this task in a file called a13_file_operations.py.

  1. The unix operating system (including all MacOS computers) includes a pattern-matching program called grep. grep searches for a pattern in one or more files, and prints out all matching lines.

    Here is an example of using grep in a Macbook Terminal window. The command grep print functions.py searches for all occurrences of the pattern print in the file functions.py.

    Write the function grep(pattern, filename) to search for the pattern in the file identified by filename. The function must return a string containing all matching lines, with line breaks (the character \n) inserted where appropriate.

    For example:

    >>> fn = 'a02_functions.py'
    >>> print(grep('print', fn))
    print("cube(3) = ", cube(3))
    print("slope(1, 2, 3, 4) = ", slope(1, 2, 3, 4))
    print("cylinder_volume(4, 2) = ", cylinder_volume(4, 2))
    

    Note! This function is not printing – it is returning a string, which contains embedded line breaks ('\n') characters). If you call the function at the console, but not in a print statement, you will see the string returned with the embedded characters.

    >>> fn = 'a02_functions.py'
    >>> grep('print', fn) # find all occurences of 'print' in the file
    'print("opposite(3) = ", opposite(3))\nprint("cube(3) = ", cube(3))\nprint("slope(1, 2, 3, 4) = ", slope(1, 2, 3, 4))\nprint("cylinder_volume(4, 2) = ", cylinder_volume(4, 2))\n'
    

    The simplest way to accomplish this result is to use an accumulator pattern to process the file, one line at a time. For each line (a string), use one of the string methods to determine if that line contains the pattern. If it does, accumulate that line into your result.

    If there are no occurrences of the pattern, the function will return an empty string.

  2. The grep program also has a way to count the number of lines within a file that contain the patter.

    Here is an example of using grep -c in a Macbook Terminal window. The command grep print functions.py searches for all occurrences of the pattern print in the file functions.py, and returns the count of the number of matching lines.

    Notice how this version (with the -c flag) returns the number of occurrences, but not the actual matching lines.

    Write the function grep_count(pattern, filename) to search for the pattern in the file identified by filename. The function will return an integer, which is the count of the number of lines containing the pattern.

    For example:

    >>> fn = 'a02_functions.py'
    >>> grep_count('print', fn)
    4
    >>> grep_count('(', fn)
    13
    >>> grep_count('+', fn)
    0
    

    The simplest way to accomplish this result is to use an accumulator pattern to process the file, one line at a time. For each line (a string), use one of the string methods to determine if that line contains the pattern. Count that line as one match within your accumulation.

  3. Write the function find_and_replace(input_filename, output_filename, old_pattern, new_pattern) that will process the file identified by input_filename, and replace all occurences of the string old_pattern with the string new_pattern. The function will write the results to a file identified by output_filename.

    The function will also produce several print outs, to provide some explanation of how it worked.

    For example, here’s is some client code to read the file "all_you_need.txt", and create the result file "all_you_need.txt". It replaces all occurrences of "Love" with "Chocolate", and produces 3 lines of output to the console:

    >>> input_filename = "all_you_need.txt"
    >>> output_filename = "all_you_need_out.txt"
    >>> find_and_replace(input_filename, output_filename, 
                     "Love", "Chocolate")
    Read input file `all_you_need.txt`.
    Replaced 30 occurences of `Love` with `Chocolate`.
    Wrote output file `all_you_need_out.txt`.
    

    Here are the first few lines of the output file:

    All You Need is Chocolate
    Lennon/McCartney
    
    Chocolate, love, love
    Chocolate, love, love
    Chocolate, love, love
    

    You will notice that all occurrences of the string "Love" have been replaced by "Chocolate", but "love" (with a lower case 'l') has not been replaced.

    Begin by testing a version to make these replacements. Once it works, you should enhance your function to handle several permutations of the old_pattern, and replace each with the corresponding permutation of the new_pattern:

    • the actual old_pattern should be replaced by new_pattern

    • the UPPERCASE version of old_pattern should be replaced by an UPPERCASE version of new_pattern

    • the lowercase version of old_pattern should be replaced by a lowercase version of new_pattern

    • the Title version of old_pattern should be replaced by a Title version of new_pattern

    For example (enhanced version):

    >>> input_filename = "all_you_need.txt"
    >>> output_filename = "all_you_need_out.txt"
    >>> find_and_replace(input_filename, output_filename, 
                     "Love", "Chocolate")
    Read input file `all_you_need.txt`.
    Replaced 30 occurences of `Love` with `Chocolate`.
    Replaced 44 occurences of `love` with `chocolate`.
    No occurences of `LOVE` were found.
    Wrote output file `all_you_need_out.txt`.
    

    Here are the first few lines of the new output file:

    All You Need is Chocolate
    Lennon/McCartney
    
    Chocolate, chocolate, chocolate
    Chocolate, chocolate, chocolate
    Chocolate, chocolate, chocolate
    

    Hints:

    • You can solve this the hard way: using one or more loops and accumulators and decision statements, or...

    • You can solve this the easy way: using some of the string object’s methods to obtain the UPPERCASE, lowercase, and Title versions of the strings.


Submitting Your Work

20 points; will be assigned by code review

Log in to GradeScope to submit your work.

Be sure to name your files correctly!

You will submit two files for this assignment: a13_strings.py and a13_file_operations.py.

When you upload the files, the autograder will test your program.

Notes:

Warning: Beware of Global print statements

  • The autograder script cannot handle print statements in the global scope, and their inclusion causes this error:

    The autograder failed to execute correctly. Please ensure that your submission is valid. Contact your course staff for help in debugging this issue. Make sure to include a link to this page so that they can help you most effectively.

  • You can prevent this error by not having any print statements in the global scope. Instead, create an if __name__ == '__main__': section at the bottom of the file, and put any test cases/print statements in that controlled block.

  • print statements inside of functions do not cause this problem.