Today: string find/slice practice, file-reading, crazycat example program

Problems today:

> str3 functions

## Idea: Use var= to Shorten Code

• Style idea develop later
• Have phrase X repeated in code
• Use var = X to avoid duplication
• 1. Shorten the code
• 2. Variable name helps make it readable

## Problems: int indexing, slicing, no loops

• No Loops: here .find() does the looping for us
• str.find() solves the problem where you know you are looking for an '@'
• Other times you write your own loop (previous lecture), e.g. find first digit
• Always: sketch diagram, plot out index numbers, slices

## 1. right_left()

`'aabb' → 'bbbbaaaa'`

We'll say the midpoint of a string is index len // 2, dividing the string into a left half before the midpoint and a right half starting at the midpoint. Given string s, return a new string made of 2 copies of right followed by 2 copies of left. So 'aabb' returns 'bbbbaaaa'.

Solution

```def right_left(s):
mid = len(s) // 2
left = s[:mid]
right = s[mid:]
return right + right + left + left
# Style comparison:
# Without using any variables, the solution
# is longer and not so readable:
# return s[len(s) // 2:] + s[len(s) // 2:] + s[:len(s) // 2] + s[:len(s) // 2]
```

## 2. at_3()

This looks simple, but the details are tricky. Make a drawing.

Given string s. Find the first '@' within s. Return the len-3 substring immediately following the '@'. Except, if there is no '@' or there are not 3 chars after the @, return ''.

Solution

```def at_3(s):
at = s.find('@')
if at == -1:
return ''
# Is at + 3 past end of string?
# Could "or" combine with above
if at + 3 >= len(s):
return ''
return s[at + 1:at + 4]
# Working out >= above ... drawing!
```

## 3. parens

This is nice, realistic string problem with a little logic in it.

Recall: `s.find(target, start_index)` - start search at start_index vs. starting search at index 0.

Given string s. Look for a '(.....)' within s - look for the first '(' in s, then the first ')' after the '(', using the second start_index parameter of .find(). If both parens are found, return the chars between them, so `'xxx(abc)xxx'` returns `'abc'`. If no such pair of parens is found, return the empty string. Think about the input `'))(abc)'`.

Solution

```def parens(s):
left = s.find('(')
if left == -1:
return ''
# Start search at left + 1:
right = s.find(')', left + 1)
if right == -1:
return ''
# Use slice to pull out chars between left/right
return s[left + 1:right]
```

## crazycat example

This code is complete, you can run it to see what it does.

## Backslash Chars in a String

Use backslash \ to include special chars in a string

```s = 'isn\'t'

\n  newline char
\\  backlash
\'  single quote
\"  double quote
```

## What is a Text File?

• "text file", aka "plain text file"
• Extremely common way to store/exchange data on computers
• Very old (teletype) .. and used up through today
• Text file is a series of lines
• Each line is a series of chars ending with a '\n' char
• Special char: `'\n'` is called the "newline" char
• `\n'` is like hitting the "return" or "enter" key on your keyboard
• Aside: a few other chars can appear instead of '\n':
Sequence of \r\n (DOS) \r (very old Mac)
Mostly Python insulates you from the exact line-ending of a text file

## hibye.txt Text Example

2 lines, each line has a '\n' at the end. The first line has a space, just ' ' in Python.

```Hi and
bye
```

Here is what that file looks like in an editor that shows little gray marks for the space and \n • Consider a program made of several functions
• We have functions calling each other
• Black Box Model
• Structured data flow:
Params data into fn
Return value data out of fn
• Structured data like this makes Doctests
• This is the most important data pattern, and we use it constantly

## Secondary: Program Text Output Area • A running program has a single "standard output" text area
• 1. Textual
• 2. One area shared by all functions
• 3. Calling print(xxx) adds a line of text to end of output
The order that print() is called is the order the lines appear
So print('Hello there') print('How are you?') print('I am fine!')
Results in those 3 lines in that order
• 4. Terminal - see the print output here
• Historical - output convention from when the computer was connected to a teletype printer to see its output
• This is not structured
The printing from various functions is all mixed together
• Mostly do not use this for Doctests

## print() function

• Python print() function
• Prints text lines to the standard output area
• Takes a number of items, separated by commas
• Converts them to string form internally
• sep='xx' option - use to separate items
• end='xx' option - put this at end instead of '\n'
• use end='' to put nothing at the end
• Try print() in the interpreter, see its output there
```>>> print('hello', 'there', '!')
hello there !
>>> print('hello', 123, '!')
hello 123 !
>>> print('hello', 123, '!', sep=':')
hello:123:!
>>> print(1, 2, 3)               # end='\n' the default
1 2 3
>>> print(1, 2, 3, end='xxx\n')  # end= what goes at end
1 2 3xxx
>>> print(1, 2, 3, end='')       # suppress the \n
1 2 3>>>
```

## Data out of function: return vs. print

Return and print() are both ways to get data out of a function. We will be careful when specifying a function to say that it should "return" a value (most common), or it should "print" something to standard output. Usually the functions that do computation use return, so they can be tested.

## 1. Try "ls" and "cat" in terminal

Open a terminal in the crazycat directory (see the Command guide for more information running in the terminal). Terminal commands - work in both Mac and Windows

ls - see list of filenames ("dir" on older Windows)

cat filename - see file contents

```\$ ls
alice.txt       crazycat.py     hibye.txt       poem.txt        quotes
\$ cat poem.txt
Roses Are Red
Violets Are Blue
This Does Not Rhyme
\$
```

## 2. Run crazycat.py with filename

• It does "cat" but implemented Python
Demonstrating how to read lines of a text file and print them out
• Use "tab" to autocomplete filenames
• The standard out of a program is printed to the terminal, so we see it here
```\$ python3 crazycat.py poem.txt
Roses Are Red
Violets Are Blue
This Does Not Rhyme
\$ python3 crazycat.py hibye.txt
Hi and
bye
\$
```

```with open(filename, 'r') as f:
for line in f:
# use line in here
``` • Read series of lines of a file
• for loop - treats file like a collection of line strings
Each run of the loop body gets the next line of text
e.g. 4 line file = loop body runs 4 times
• Memory efficient
Only holds one line in memory at a time
• Each line has the '\n' at its end ..
• 'r' for reading - later 'w' for writing
• Super handy form, we already know loops!

Here is the whole function to print the contents of a file. Why do we need end='' here? The line already has \n at its end, so get double spacing if print() adds its standard \n.

```def print_file_plain(filename):
with open(filename, 'r') as f:
for line in f:
# use line in here
print(line, end='')
```

## 4. Run With -crazy option

• Look at the code crazy_line() function
• Swaps upper/lower
• String/loop type code we've done before
• Returns computed result
```def crazy_line(line):
"""
Given a line of text, returns a "crazy" version of that line,
where upper/lower case have all been swapped, so 'Hello'
returns 'hELLO'.
>>> crazy_line('Hello')
'hELLO'
>>> crazy_line('@xYz!')
'@XyZ!'
>>> crazy_line('')
''
"""
result = ''
for i in range(len(line)):
char = line[i]
if char.islower():
result += char.upper()
else:
result += char.lower()
return result
```

Here is command line to run with -crazy option

```\$ python3 crazycat.py -crazy poem.txt
rOSES aRE rED
vIOLETS aRE bLUE
tHIS dOES nOT rHYME
```
• Provided main() sees -crazy, calls crazy_line() like this
We provide this main() code for you for now
• `print(crazy_line(line), end='')`
• Nesting: the return-result of crazy_line() is then sent to print()

Here print_file_crazy(), similar to print_file_plain() but passes each line through the crazy_line() function before printing.

```def print_file_crazy(filename):
"""
Given a filename, read all its lines and print them out
in crazy form.
"""
with open(filename, 'r') as f:
for line in f:
print(crazy_line(line), end='')
```