1 Mechanics of this Lab

Read – this lab is intended to help you learn on your own. Read what we’ve written before you code!

2 String Puzzles

Strings can be manipulated using operators, functions, and methods. Manipulating strings is a common part of many kinds of programs. This lab explores some of these ideas. It’s structure is an overview of available operations and a set of puzzles. Restrict yourself to the listed operations for this lab; we’ll learn other, more complicated tools later, which you can find if you search online, but using them before you understand these simpler operations will generally confuse, not enlighten.

In everything that follows, there is a rule Python never breaks:

Nothing but the assignment statement (=) changes a string; everything else either inspects it unchanged or creates a new string.

3 Tasks

Create a file named str_funcs.py in which you implement as many of the following tasks as possible. You are welcome to also have testing code in that file, or to test in a different file.

See also the Tools to Use section below, which lists both things you already know and things you haven’t seen before. Part of our goal here is to encourage you to develop the skills needed to learn on your own, so make an effort to understand how to use the components we list before you ask for help.

Don’t be afraid to experiment; trial-and-error is a safe and productive way to learn programming.

Most of you won’t get to all the problems below. That’s expected; do as many as you can get to in the time alloted and submit what you have at the end, primarily so we can see how much you got to.

3.1 Ellipsis

Write a function ellipsis(s) that replaces all but the first two and last two characters of a string with an ellipsis (...).

Example: ellipsis("computer science") should return "co..ce"

Tools: There is a solution that uses only the slice and concatenation operators.

3.2 Eighteen

When internationalization of computer applications became a topic of interest, people got tired of typing internationalization so they shortened it to i18n, meaning “an”i“, 18 more letters, and then an”n"". Write a function eighteen(s) that uses the same idea to shorten any string.


  • eighteen("computer science") should return "c14e"
  • eighteen("is") should return "i0s"
  • eighteen("fun") should return "f1n"

Tools: There is a solution that uses only the index operator and built-in functions

3.3 Alliterative

Two words are alliterative if they start with the same consonant sound. Since sounds are a bit complicated in English, write a simpler version: a function allit(s, t) that is True if s and t start with the same non-vowel character. Your code should treat upper- and lower-case letters as the same.


  • allit("hi", "hello") should return True
  • allit("Hi", "hello") should return True
  • allit("fun", "great fun") should return False
  • allit("exciting", "excitement") should return False

Tools: There is a solution that uses one method to fix case, several operators, and an extra string literal.

3.4 Between

Write a function between that returns the portion of its first parameter that falls between the first and second occurrence of its second parameter; or return the empty string if the second parameter does not occur twice.


  • between("peripatetics", "p") gives "eri"
  • between("loan me a lovely loon to look at", "lo") gives "an me a "
  • between("loan me a lovely loon to look at", " lo") gives "vely"
  • between("quick", "u") gives ""
  • between("quick", "z") gives ""

Tools: There is a solution that uses the find method, the slice operator, an if statement or two, and a little arithmetic.

3.5 Reversed-Between

Write a function rbetween that does the same as between, but looks between the last two, not first two, occurrences.


  • rbetween("peripatetics", "p") gives "eri"
  • rbetween("loan me a lovely loon to look at", "lo") gives "on to "
  • rbetween("loan me a lovely loon to look at", " lo") gives "on to"
  • rbetween("quick", "u") gives ""
  • rbetween("quick", "z") gives ""

Tools: There is a solution that uses the rfind method, the slice operator, an if statement or two, and a little arithmetic.

3.6 Random Between

Write a function rand_between that randomly does either between or rbetween.


  • rand_between("peripatetics", "p") gives "eri"
  • rand_between("loan me a lovely loon to look at", "lo") gives "on to " sometimes and "an me a " other times
  • rand_between("loan me a lovely loon to look at", " lo") gives "vely" sometimes and "on to" other times.
  • rand_between("quick", "u") gives ""

Tools: There is a solution that random.randint, an if statement, and both between and rbetween.

3.7 Temperature

Web pages are coded in a language called HTML, and understanding it can help us write programs that use the Internet. For example, part of <weather.gov>’s page for a given zip code is current temperature which shows up between class="myforecast-current-lrg"> and &deg;F; for example, at the time I am writing this description Charlottesville’s page contains <p class="myforecast-current-lrg">83&deg;F</p>.

Write a function temperature that returns the the content between the first class="myforecast-current-lrg"> and &deg;F in its argument string.


  • temperature("<p class="myforecast-current-lrg">83&deg;F</p>") should return "83"
  • temperature("<p class="myforecast-current">Fair</p>n<p class="myforecast-current-lrg">103&deg;F</p><p class="myforecast-current-sm">28&deg;C</p>") should return "103"

Tools: There is a solution that uses the find method, the slice operator, and a little arithmetic.

3.8 Unhide

Sometimes people obscure email addresses by writing the @ and . as at and dot. Write a function unhide that converts these back to @ and .. Even do this if they use capitals or parentheses.


  • unhide("mst3k@virginia.edu") gives "mst3k@virginia.edu"
  • unhide("mst3k at virginia.edu") gives "mst3k@virginia.edu"
  • unhide("mst3k (at) virginia (dot) edu") gives "mst3k@virginia.edu"
  • unhide("mst3k AT virginia DOT edu") gives "mst3k@virginia.edu"
  • unhide("mst3k@virginia dot edu") gives "mst3k@virginia.edu"

Tools: All you need is replace; but remember that replace (like all string methods) does not modify the string in-place, rather returns a copy. Thus

a = "a string"
a.replace("tr", "w")

prints a string not a swing; however

a = "a string"
a = a.replace("tr", "w")

prints a swing.

3.9 Vowel confusion

Write a function vowel_confusion that swaps all es and is.


  • vowel_confusion("Electric slide") gives "Ilictrec sledi"
  • vowel_confusion("I sang, and thought I sang very well; but he just looked up into my face with a very quizzical expression, and said, 'How long have you been singing, Mademoiselle?'") gives "E sang, and thought E sang viry will; but hi just lookid up ento my faci weth a viry quezzecal ixprisseon, and saed, 'How long havi you biin sengeng, Madimoesilli?'"

Tools: All you need is replace; but you’ll need to use three, not two, replacements, using some kind of rarely-typed placeholder character like ß.

3.10 Few quoted characters

Write a program in a file changing_str.py whose first line is a = "reasonable simplicity", whose last line prints radical complexity, with as few characters appearing between quotes within the program as possible.

The following program:

a = "reasonable simplicity"
print("radical complexity")

has 39 characters in quotes (21 for the first line, 18 for the second).

But this one:

a = "reasonable simplicity"
print(a[0] + "adical complex" + a[-3:])

has only 35. We’re pretty confident that it is possible to get to only 23…

4 Submission

Each partner should submit one .py file named str_funcs.py to Archimedes (the submission system): https://kytos.cs.virginia.edu/cs1110/. If you got to the last task, also submit changing_str.py. Please put both partners’ ids in comments at the top of the file.

5 Tools to Use

In the following, s and t are assumed to be strings; i and j are assumed to be integers, with i <= j; k is also assumed to be an integer.

5.1 Operators


s + t makes a new string containing the characters of s followed by the character s of t

Example: "where" + "fore" creates "wherefore"

  • s == t is True if s and t have the same characters in the same order.
  • s != t is True if s == t is False.
  • s < t is True if s comes before t in something similar to alphabetical order (except it puts all capitals before any lower-case and also has positions for all punctuation, etc).
  • s in t is True if t could be written as x + s + y for some (possibly empty) strings x and y.

Examples: the following are all True

  • "fun" == 'fun'
  • "fun" != "Fun"
  • "120" < "20" < "40" < "8"
  • "aardvark" < "animal" < "artificial" < "be" < "bee"
  • "A" < "Z" < "a" < "z"
  • "fun" in "more functions"

s[i] extracts a single-character string from s. s[0] is the first character of s, s[1] is the second character of s, and so on up to s[len(s)-1]. s[-1] is the last character of s, s[-2] is the next-to-last character of s, and so on down to s[-len(s)].

Example: "where"[1] creates "h"; so does "where"[-4]


s[i:j] extracts a substring from s, beginning with s[i] and ending before s[j].

A missing index is treated as the end of the string.


  • "slices"[1:4] creates "lic" – not "lice" because when slicing we do not get the character at ending position.
  • "slices"[:4] creates "slic"
  • "slices"[4:] creates "es"

This is also an operator called an extended slice but it isn’t needed for any of the puzzles in this lab. If you are curious,

-   "many letters"[1::2]{.python} creates "ayltes"{.python} -- every 2nd character starting at index 1.
-   "many letters"[1:9:2]{.python} creates "aylt"{.python} -- every 2nd character starting at index 1 and ending before reaching index 9.
-   "many letters"[::3]{.python} creates "myee"{.python} -- every 3rd character starting at index 0.

5.2 Functions

Functions that manipulate strings take the strings are their arguments. The following functions do not need any import to be found.

  • len(s) returns the number of characters in s.

    Example: len("fun") returns 3

  • str(anything) returns a string representation of anything.

    Example: str(1110) returns "1110"; str(str) returns "<class 'str'>".

  • repr(anything) returns a “canonical” string representation of anything. Notably for strings, this is includes surrounding quotes and no new-lines.


    • repr("fun") returns "'fun'", which, when printed, will look like 'fun'
    • repr("don't " + 'mix "quotes" ' + "with 'quotes' " + '''and new lines''') returns a string that, when printed, will look like 'don't mix "quotes" with 'quotes' andnnew lines'
  • format(value, s) is like str, but more flexible.

    • format(value) returns exactly what str(value) returns.
    • format(value, '8') returns str(value) padded with spaces to be at least 8 characters wide.
    • format(number, '.3f') returns the number represented with three digits to the right side of the decimal point.

    There are many many more things format can do (e.g., try format(0, 'w<+12.3f')) but even what we’ve covered is more than you usually need.

5.3 Methods

String methods are invoked by putting them after a string, preceded by a period. They generally return a value based on the string they follow.

  • s.capitalize() returns a version of s with the first letter upper case, the rest lower case. The same as s[0].upper() + s[1:].lower().

    s.title() is similar, but capitalizes and character following a space as well.

  • s.center(i) returns s if len(s) >= i; otherwise it adds spaces to each side of s to return a string of length i.

    s.ljust(i) and s.rjust(i) are similar but left- or right-justify, instead of centering.

  • s.count(t) returns how many non-overlapping occurrences of t there are in s. 'hahahahaha'.count('hah') returns 2.

    s.count(t,i,j) only counts between positions i and j

  • s.endswith(t) returns True if s[-len(t):] == t

    s.startswith(t) is similar, but checks the other end of s

  • s.find(t) returns the first location of t within s, or -1 if t not in s.


    • "fun for fun's sake".find("fun") returns 0
    • "fun for fun's sake".find("fun'") returns 8

    s.find(t,i,j) only looks between positions i and j

    s.index(...) is just like find except it has an error if t is not found instead of returning -1.

    s.rfind and s.rindex are just like find and index except they return the index of the last occurance instead of the first.

    • "fun for fun's sake".rfind("fun") returns 8
    • "fun for fun's sake".rfind("fun ") returns 0
  • s.lower() returns a new string where all characters with a defined case have been replaced by their lower-case versions. Thus "WoNk123e!".lower() == "wonk123e!"

    s.upper() is similar, but uses upper-case instead of lower-case characters.

  • s.strip() returns a new string that is like s but has no white-space characters (spaces, newlines, etc) at its beginning or end. Thus " so odd ".strip() == "so odd".

    s.strip(t) removes characters in t instead of white-space characters. Thus "periodically".strip("repyli") == "odica"

    The related function s.lstrip only removes them from the beginning, s.rstrip only from the end.

  • s.replace(t1, t2) returns a new string with all occurances of t1 replaced by t2. Thus "cocoa is coo-coo".replace("o", "uh") == "cuhcuha is cuhuh-cuhuh".

    s.replace(t1, t2, i) replaces the first i occurrences of t1, leaving any extras unchanged. Thus "cocoa is coo-coo".replace("o", "uh", 3) == "cuhcuha is cuho-coo".

  • There are also two other useful methods, join and split, that we will discuss more in the weeks to come.