Class Notes: Strings


  1. String Literals [Pre-reading]
  2. Some String Constants [Pre-reading]
  3. Some String Operators [Pre-reading]
  4. Looping over Strings [Pre-reading]
  5. Example: isPalindrome
  6. Some String-related Built-In Functions [Pre-reading]
  7. Some String Methods [Pre-reading]
  8. String Formatting with f Strings [Pre-reading]
  9. String Formatting (optional)
  10. Strings are Immutable
  11. Strings and Aliases
  12. Basic File IO


  1. String Literals [Pre-reading]
    1. Four kinds of quotes
      # Quotes enclose characters to tell Python "this is a string!" # single-quoted or double-quoted strings are the most common print('single-quotes') print("double-quotes") # triple-quoted strings are less common (though see next section for a typical use) print('''triple single-quotes''') print("""triple double-quotes""") # The reason we have multiple kinds of quotes is partly so we can have strings like: print('The professor said "No laptops in class!" I miss my laptop.')
      # See what happens if we only use one kind of quote. # It causes a syntax error! We don't even know how to color this. print("The professor said "No laptops in class!" I miss my laptop.")

    2. Newlines in strings
      # A character preceded by a backslash, like \n, is an 'escape sequence'. # Even though it looks like two characters, Python treats it as one special character. # Note that these two print statements do the same thing! print("abc\ndef") # \n is a single newline character. print("""abc def""") print("""\ You can use a backslash at the end of a line in a string to exclude the newline after it. This should almost never be used, but one good use of it is in this example, at the start of a multi-line string, so the whole string can be entered with the same indentation (none, that is). """)

    3. More Escape Sequences
      print("Double-quote: \"") print("Backslash: \\") print("Newline (in brackets): [\n]") print("Tab (in brackets): [\t]") print("These items are tab-delimited, 3-per-line:") print("abc\tdef\tg\nhi\tj\\\tk\n---")

      An escape sequence produces a single character:
      s = "a\\b\"c\td" print("s =", s) print("len(s) =", len(s))

    4. repr() vs. print()
      Sometimes it can be difficult to debug strings! Two strings that look identical when printed may not actually be the same. Have you ever had trouble distinguishing a tab from several spaces in a word processor? The repr() function is sort of like a 'formal' version of print(). Where print() is meant to produce output intended for the user, repr() shows us a representation of the data contained in the string. This can be really useful for debugging! Looking at an example will help you understand the difference:
      print("These look the same when we print them!") s1="abc\tdef" s2="abc def" print("print s1: ",s1) print("print s2: ",s2) print("\nThey aren't really though...") print("s1==s2?", s1==s2) print("\nLet's try repr instead") print("repr s1: ",repr(s1)) print("repr s2: ",repr(s2)) print("\nHere's a sneaky one") s1="abcdef" s2="abcdef \t" print("print s1: ",s1) print("print s2: ",s2) print("s1==s2?", s1==s2) print("repr s1: ",repr(s1)) print("repr s2: ",repr(s2)) print("repr() lets you see the spaces^^^")

    5. String Literals as Multi-line Comments
      """ Python does not have multiline comments, but you can do something similar by using a top-level multiline string, such as this. Technically, this is not a comment, and Python will evaluate this string, but then ignore it and garbage collect it! """ print("wow!")

  2. Some String Constants [Pre-reading]
  3. import string print(string.ascii_letters) # abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ print(string.ascii_lowercase) # abcdefghijklmnopqrstuvwxyz print("-----------") print(string.ascii_uppercase) # ABCDEFGHIJKLMNOPQRSTUVWXYZ print(string.digits) # 0123456789 print("-----------") print(string.punctuation) # '!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~' print(string.printable) # digits + letters + punctuation + whitespace print("-----------") print(string.whitespace) # space + tab + linefeed + return + ...

  4. Some String Operators [Pre-reading]
    1. String + and *
      print("abc" + "def") # What do you think this should do? print("abc" * 3) # How many characters do you think this prints? print("abc" + 3) # ...will this give us an error? (Yes)

    2. The in operator
      # The "in" operator is really really useful! print("ring" in "strings") print("wow" in "amazing!") print("Yes" in "yes!") print("" in "No way!")

    3. String indexing and slicing
      1. Indexing a single character
        # Indexing lets us find a character at a specific location (the index) s = "abcdefgh" print(s) print(s[0]) print(s[1]) print(s[2]) print("-----------") print("Length of ",s,"is",len(s)) print("-----------") print(s[len(s)-1]) print(s[len(s)]) # crashes (string index out of range)

      2. Negative indexes
        s = "abcdefgh" print(s) print(s[-1]) print(s[-2])

      3. Slicing a range of characters
        # Slicing is like indexing, but it lets us get more than 1 character. # ...how is this like range(a,b)? s = "abcdefgh" print(s) print(s[0:3]) print(s[1:3]) print("-----------") print(s[2:3]) print(s[3:3])

      4. Slicing with default parameters
        s = "abcdefgh" print(s) print(s[3:]) print(s[:3]) print(s[:])

      5. Slicing with a step parameter
        print("This is not as common, but perfectly ok.") s = "abcdefgh" print(s) print(s[1:7:2]) print(s[1:7:3])

      6. Reversing a string
        s = "abcdefgh" print("This works, but is confusing:") print(s[::-1]) print("This also works, but is still confusing:") print("".join(reversed(s))) print("Best way: write your own reverseString() function:") def reverseString(s): return s[::-1] print(reverseString(s)) # crystal clear!

  5. Looping over Strings [Pre-reading]
    1. "for" loop with indexes
      s = "abcd" for i in range(len(s)): print(i, s[i])

    2. "for" loop without indexes
      s = "abcd" for c in s: print(c)

    3. "for" loop with split
      # By itself, names.split(",") produces something called a list. # Until we learn about lists (soon!), do not store the result of # split() and do not index into that result. Just loop over the # result, as shown here: names = "fred,wilma,betty,barney" for name in names.split(","): print(name)

    4. "for" loop with splitlines
      # splitlines() also makes a list, so only loop over its results, # just like split(): # quotes from brainyquote.com quotes = """\ Dijkstra: Simplicity is prerequisite for reliability. Knuth: If you optimize everything, you will always be unhappy. Dijkstra: Perfecting oneself is as much unlearning as it is learning. Knuth: Beware of bugs in the above code; I have only proved it correct, not tried it. Dijkstra: Computer science is no more about computers than astronomy is about telescopes. """ for line in quotes.splitlines(): if (line.startswith("Knuth")): print(line)

  6. Example: isPalindrome

  7. A string is a palindrome if it is exactly the same forwards and backwards.
    # There are many ways to write isPalindrome(s) # Here are several. Which way is best? def reverseString(s): return s[::-1] def isPalindrome1(s): return (s == reverseString(s)) def isPalindrome2(s): for i in range(len(s)): if (s[i] != s[len(s)-1-i]): return False return True def isPalindrome3(s): for i in range(len(s)): if (s[i] != s[-1-i]): return False return True def isPalindrome4(s): while (len(s) > 1): if (s[0] != s[-1]): return False s = s[1:-1] return True print(isPalindrome1("abcba"), isPalindrome1("abca")) print(isPalindrome2("abcba"), isPalindrome2("abca")) print(isPalindrome3("abcba"), isPalindrome3("abca")) print(isPalindrome4("abcba"), isPalindrome4("abca"))

  8. Some String-related Built-In Functions [Pre-reading]

    1. str() and len()
      name = input("Enter your name: ") print("Hi, " + name + ". Your name has " + str(len(name)) + " letters!")

    2. chr() and ord()
      print(ord("A")) # 65 print(chr(65)) # "A" print(chr(ord("A")+1)) # ?

    3. eval()
      # eval() works but you should not use it! s = "(3**2 + 4**2)**0.5" print(eval(s)) # why not? Well... s = "reformatMyHardDrive()" print(eval(s)) # no such function! But what if there was?

  9. Some String Methods [Pre-reading]

  10. Methods are a special type of function that we call "on" a value, like a string. You can tell it's a method because the syntax is in the form of value.function(), like s.islower() in the code below.
    1. Character types: isalnum(), isalpha(), isdigit(), islower(), isspace(), isupper()
      # Run this code to see a table of isX() behaviors def p(test): print("True " if test else "False ", end="") def printRow(s): print(" " + s + " ", end="") p(s.isalnum()) p(s.isalpha()) p(s.isdigit()) p(s.islower()) p(s.isspace()) p(s.isupper()) print() def printTable(): print(" s isalnum isalpha isdigit islower isspace isupper") for s in "ABCD,ABcd,abcd,ab12,1234, ,AB?!".split(","): printRow(s) printTable()

    2. String edits: lower(), upper(), replace(), strip()
      print("This is nice. Yes!".lower()) print("So is this? Sure!!".upper()) print(" Strip removes leading and trailing whitespace only ".strip()) print("This is nice. Really nice.".replace("nice", "sweet")) print("This is nice. Really nice.".replace("nice", "sweet", 1)) # count = 1 print("----------------") s = "This is so so fun!" t = s.replace("so ", "") print(t) print(s) # note that s is unmodified (strings are immutable!)

    3. Substring search: count(), startswith(), endswith(), find(), index()
      print("This is a history test".count("is")) # 3 print("This IS a history test".count("is")) # 2 print("-------") print("Dogs and cats!".startswith("Do")) # True print("Dogs and cats!".startswith("Don't")) # False print("-------") print("Dogs and cats!".endswith("!")) # True print("Dogs and cats!".endswith("rats!")) # False print("-------") print("Dogs and cats!".find("and")) # 5 print("Dogs and cats!".find("or")) # -1 print("-------") print("Dogs and cats!".index("and")) # 5 print("Dogs and cats!".index("or")) # crash!

  11. String Formatting with f-strings [Pre-reading]
  12. # We saw this example back in week1! # It shows a nice relatively new way to format strings: x = 42 y = 99 # Place variable names in {squiggly braces} to print their values, like so: print(f'Did you know that {x} + {y} is {x+y}?')

  13. Other String Formatting (optional)

  14. f-strings are a great way to format strings. You may also see python code written with other string formating techniques that were common before Python 3.6. See these optional notes on formatting strings using the % operator and using the format() method.

  15. Strings are Immutable
    1. You cannot change strings! They are immutable.
      s = "abcde" s[2] = "z" # Error! Cannot assign into s[i]

    2. Instead, you must create a new string.
      s = "abcde" s = s[:2] + "z" + s[3:] print(s)

  16. Strings and Aliases
  17. Strings are immutable, so aliases do not change!
    s = 'abc' # s references the string 'abc' t = s # t is an alias to the one-and-only string 'abc' s += 'def' print(s) print(t)

  18. Basic File IO
  19. # Note: As this requires read-write access to your hard drive, # this will not run in the browser in Brython. def readFile(path): with open(path, "rt") as f: return f.read() def writeFile(path, contents): with open(path, "wt") as f: f.write(contents) contentsToWrite = "This is a test!\nIt is only a test!" writeFile("foo.txt", contentsToWrite) contentsRead = readFile("foo.txt") assert(contentsRead == contentsToWrite) print("Open the file foo.txt and verify its contents.")

    Here is another more-complete example that saves and loads data from a file:
    # Here is an example that saves and loads data from a file. # It works well with builtin data types. It's a bit more complicated # to do this with custom classes. For those, you have to be sure # that your __repr__ method returns a string such that # (eval(v.__repr__()) == v) is True. import ast def readFile(path): with open(path, "rt") as f: return f.read() def writeFile(path, contents): with open(path, "wt") as f: f.write(contents) # Place all your data in a dictionary, like so: myData = { 'names': ['fred', 'wilma', 'betty'], 'highScores': [32, 41, 18, 17, 64], 'stateCapitals': { 'pa':'harrisburg', 'oh':'columbus' }, 'setOfPrimes': { 2, 3, 5, 7, 11 }, } # Then, you can save your data to a file like so: writeFile('myData.txt', repr(myData)) # Later on, you can restore your data from the file like so: myData1 = ast.literal_eval(readFile('myData.txt')) # Finally, let's confirm that these two dictionaries are equal: print(myData1 == myData) # True!