# Python Concepts/Strings
## Objective

* Understand Python strings.
* Learn basic string manipulation.
* Learn about escape characters and their role in strings.
* Learn about linefeed techniques for large strings.
* Learn the string formatting basics.
* Learn about string indexing.
* Learn about string slicing.
* Learn about string encoding, ASCII, and Unicode.
* Work with common built-in string methods.

## Lesson

Python Strings
The string is one of the simplest data types in python. Strings can be created by putting either single quotations (') or double quotations (") at the beginning and end of a sequence of textual characters.
A simple string with single quotations:
>>> 'Hello!'
'Hello!'
A string can also use double quotations, which do not affect the string in any way.
>>> "Hello!"
'Hello!'
You can concatenate (join together in sequence) strings by using the plus sign (+).
>>> "Hello," + " world!"
'Hello, world!'
Strings can also be concatenated if they are literals (Strings not held in variables).
>>> "Wiki" "versity" "!"
'Wikiversity!'
The above code has spaces between the literal strings, but you don't necessarily need to put spaces between them to work. "Wiki""versity""!" will display the same results.
Now, let's say you need to type a very long string that repeats itself. You can repeat words by using the multiplication operator (*).
>>> print("hey" * 3)
heyheyhey
Examples of strings with "'" and '"' mixed:
>>> s = "This is John's shoe." ; s
"This is John's shoe."
>>> 'He said "I will come."'
'He said "I will come."'
>>> 'He said "I'll come."'
File "<stdin>", line 1
'He said "I'll come."'
SyntaxError: invalid syntax
>>> s = 'He said "I' "'" 'll come."' ; s
'He said "I\'ll come."' # more about escaped characters below.
Escape Characters
There are some characters that cannot be easily expressed within a string. These characters, called escape characters, can be easily integrated within a string by using two or more characters. In Python, we denote escape characters with a backslash (\) at the beginning. For example, to put a new line in the string we could add a linefeed (\n).
>>> "Hello, world!\n"
'Hello, world!\n'
That's not really impressive, is it? To actually see that new line in action, use the built-in function print.
>>> print("Hello, world!")
Hello, world!
>>> print("Hello, world!\n")
Hello, world!
Here is a table of other escape characters (no need to memorize them, the most important one you'll use is \n).
Escape SequenceMeaning
\\Backslash (\)
\'Single quote (')
\"Double quote (")
\aASCII Bell (BEL)
\bASCII Backspace (BS)
\fASCII Form-feed (FF)
\nASCII Linefeed (LF)
\rASCII Carriage Return (CR)
\tASCII Horizontal Tab (TAB)
\vASCII Vertical Tab (VT)
\oooCharacter with octal value ooo.
\xhhCharacter with hex value hh.
\N{name}Character named name in the Unicode database.
\uxxxxCharacter with 16-bit hex value xxxx.
\UxxxxxxxxCharacter with 32-bit hex value xxxxxxxx.
Now you might start to see a problem with using \ in your string. Let's print a Windows directory name.
>>> print("C:\new folder")
C:
ew folder
See how \n was interpreted as a linefeed? To correct this, use the backslash escape character. Be careful when using backslashes; remember that two of them will only output one backslash.
>>> print("C:\\new folder")
C:\new folder
It could get tiresome to do that with very long directory strings, so let's use a simpler way than using two backslashes; just use the prefix r or R. By putting this prefix before there are any strings quotations, we tell Python that this string is a literal string ('r' stands for raw, so it really is a raw string). That essentially tells Python to ignore all of the escape characters.
print(r"C:\new folder")
C:\new folder
You can easily assign strings to variables.
>>> spam = r"C:\new folder"
>>> print(spam)
C:\new folder
>>> s = 'He said "I\'ll come."\n' ; s ; print (s) # escaping the single quote
'He said "I\'ll come."\n'
He said "I'll come."
>>> s = "He said \"I'll come.\"\n" ; s ; print (s) # escaping the double quote
'He said "I\'ll come."\n'
He said "I'll come."
>>> s = r"He said \"I'll come.\"\n" ; s ; print (s) # a raw string
'He said \\"I\'ll come.\\"\\n'
He said \"I'll come.\"\n
The difference between displaying a string and printing a string:
>>> s3 = r'\:' ; s3
>>> print("''".format(s3))
Newlines
Now, let's say you want to print some multi-line text. You could do it like this.
>>> print("Heya!\nHi!\nHello!\nWelcome!")
A string like that could grow really long, but we can use an easy trick which will allow text to span multiple lines without cramming it all onto one line. To do this we use three quotations (
(""" or
) to start and end a string:
>>> print("""
... Heya!
... Hi!
... Hello!
... Welcome!
Heya!
Hi!
Hello!
Welcome!
That made things a lot easier. But we can still do better. By adding a backslash (\) we can remove the first linefeed.
>>> print("""\
... Heya!
... Hi!
... Hello!
... Welcome!""")
Heya!
Hi!
Hello!
Welcome!
Some of you may have noticed that print automatically ends with an extra linefeed (\n). There is a way to bypass this.
>>> print("I love Wikiversity!", end="")
I love Wikiversity!>>>
A useful way to span a string over multiple physical lines without inserting automatic line-feeds is to use parentheses (and escaping new lines as necessary):
>>> spam = ("Hello,\
... world!")
>>> print(spam)
Hello, world!
Use parentheses and concatenation for long strings:
>>> spam = ("hello, hello, hello, hello, hello, hello, hello, hello, "
... "world world world world world world world world world world.")
>>> print (spam)
hello, hello, hello, hello, hello, hello, hello, hello, world world world world world world world world world world.
>>> spam = ("hello, hello, hello, hello, hello, hello, hello, hello, " '\n'
... "world world world world world world world world world world.")
>>> print (spam)
hello, hello, hello, hello, hello, hello, hello, hello,
world world world world world world world world world world.

## Formatting

| Type | Meaning |
| s | String format. Default for formatting. |
| b | Binary format. |
| c | Converts an integer to a Unicode character before it is formatted. |
| d | Decimal format. |
| o | Octal format. |
| x | Hexadecimal format. Use lowercase for a-h. |
| X | Hexadecimal format. Use uppercase for A-H. |
| n | Number format. This is the same as 'd', except that it uses the current locale setting to insert the appropriate number separator characters. |
| e | Exponent notation. Prints a number in scientific notation. Default precision is 6. |
| E | Exponent notation. Same as 'e', except it prints 'E' in the notation. |
| f | Fix point. Displays a fixed-point number. Default precision is 6. |
| F | Fixed point. Same as 'f', but converts nan to NAN and inf to INF. |
| g | General format. |
| G | General format. Switches to 'E' if numbers are too large. |

## Indexing

| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| H | e | l | l | o | , | | w | o | r | l | d | ! |
| -19 | -18 | -17 | -16 | -15 | -14 | -13 | -12 | -11 | -10 | -9 | -8 | -7 | -6 | -5 | -4 | -3 | -2 | -1 |
| I | | l | o | v | e | | W | i | k | i | v | e | r | s | i | t | y | ! |

## Slicing

| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | Indexing left to right | Line 1 |
| -19 | -18 | -17 | -16 | -15 | -14 | -13 | -12 | -11 | -10 | -9 | -8 | -7 | -6 | -5 | -4 | -3 | -2 | -1 | Indexing right to left | Line 2 |
| I | | l | o | v | e | | W | i | k | i | v | e | r | s | i | t | y | ! | "I love Wikiversity!" | Line 3 |
| s1[0] | s1[1] | s1[2] | s2[0] | s2[1] | s2[2] | s2[3] | s2[4] | s2[5] | s3 | s4[0] | s4[1] | s4[2] | s4[3] | s4[4] | s4[5] | s4[6] | s4[7] | s5 | slices s1,s2,s3,s4,s5 | Line 4 |

## Encoding

* So we know what a string is and how it works, but what really is a string? Depending on the encoding, it could be different things without changing. The most prominent string encodings are ASCII and Unicode. The first is a simple encoding for some, but not all, Latin characters and other things like numbers, signs, and monetary units. The second, called Unicode, is a larger encoding that can have thousands of characters. The purpose of Unicode is to create one encoding that can contain all of the world's alphabets, characters, and scripts. In Python 3 Unicode is the default encoding. So this means we can put almost any character into a string and have it print correctly. This is great news for non-English countries, because the ASCII encoding doesn't permit many types of characters. In fact, ASCII allows only 127 characters! (0x00 .. 0x7F, generally the characters and symbols which you see on an American-English keyboard plus control (non-printing) characters.) Here are some examples using different languages, some with non-Latin characters.
* >>> print("Witaj świecie!")
* Witaj świecie!
* >>> print("Hola mundo!")
* Hola mundo!
* >>> print("Привет мир!")
* Привет мир!
* >>> print("שלום עולם!")
* שלום עולם!
* A brief review of ASCII
* Each ASCII character fits into one byte, specifically the least significant 7 bits of the byte.
* Therefore each ASCII character has the value 0x00 .. 0x7F. The ord and chr
* built-in functions do the conversion:
* >>> chr(65); chr(0x41)
* 'A'
* 'A'
* >>> ord('a'); hex(ord('a'))
* 97
* '0x61'
* The printable characters have values 0x20 .. 0x7E
* Remaining characters are control or non-printing characters.
* The numbers '0' .. '9' have values 0x30 .. 0x39.
* The letters 'A' .. 'Z' have values 0x41 .. 0x5A.
* The letters 'a' .. 'z' have values 0x61 .. 0x7A.
* Control characters have values 0x1 .. 0x1F and 0x7F.
* Control character 0x01 = 'A' & 0x1F, called '^A' read as 'control A'.
* Control character 0x02 is called '^B', control character 0x03 '^C', etc.
* Control character with value 0x0 is '^@' or NULL. To be specific:
* >>> chr(0x01) == chr( ord('a') & 0x1F ) == chr( ord('A') & 0x1F )
* True
* >>> chr(0x09) == chr( ord('i') & 0x1F ) == chr( ord('I') & 0x1F ) == '\t'
* True
* >>> chr(0x0C) == chr( ord('l') & 0x1F ) == chr( ord('L') & 0x1F ) == '\f'
* True
* Named control characters are:
* \a Bell = ^G = 0x7
* \b Backspace = ^H = 0x8
* \f Form Feed = ^L = 0xc
* \n Line Feed = ^J = 0xa
* \r Carriage Return = ^M = 0xd
* \t Horizontal Tab = ^I = 0x9
* \v Vertical Tab = ^K = 0xb
* See the table of Escape Sequences under "Escape Characters" above. To make sense of the named control characters
* think of the 1970's and a 'modern' typewriter for the period. A proficient typist who did not look at the keyboard needed an audible alarm when the carriage approached end of line, hence "Bell". A Line feed advanced the paper one line. A Form Feed advanced paper to the next page. Perhaps the only control characters relevant today are Tab, Backspace and Return (which is interpreted as '^J'.)
* The following Python code prints characters 0x00 .. 0x7F, the ASCII character set.
* num=0
* while num <= 0x7F: print ( hex(num), '=', chr(num) ); num += 1
* For example 0x23 = '#'; 0x24 = '$'; 0x25 = '%'.
* If you send the output to a file and open the file with emacs, you will see that control
* character '^A' is one byte. If a character is not recognized, emacs prints it using octal notation:
* >>> chr(0x80) == '\200'
* True
* Modern character sets
* In times past when hardware was expensive and the English speaking world dominated computing, one character occupied seven bits (0-6) of one byte. Then bit 7 was used to provide 128 extra characters.
* For example 0xA3 = '£' (English pound); 0xB1 = '±'; 0xBD = '½'.
* Today with cheap computers a world-wide phenomenon, one character may occupy more than two bytes in a four byte word with space for expansion up to and including 32 bits. Hence:
* '\xhh' = chr(0xhh) = '\u00hh' = '\U000000hh'
* chr(0xhhhh) = '\uhhhh' = '\U0000hhhh'
* chr(0x1hhhh) = '\U0001hhhh'
* where h is a hexadecimal digit. Examples of modern characters in action:
* >>> '\x10be' == '\x10' + 'be'
* True
* >>> '\u004110be' == '\u0041' + '10be' == 'A10be'
* True
* >>> '\x61' == 'a' == '\u0061' == '\U00000061' == chr(0x61)
* True
* >>> ' \U000010be ' == ' Ⴞ ' == ' \u10be ' == ' ' + chr(0x10BE) + ' '
* True
* >>> ' \u004110be '
* ' A10be '
* >>> ' \U004110BE '
* File "<stdin>", line 1
* SyntaxError: (unicode error) 'unicodeescape' codec can't decode bytes in position 1-10: illegal Unicode character
* >>> '\u0041\u0042\u0043\u0044'
* 'ABCD'
* >>> '\U0041\U0042\U0043\U0044'
* File "<stdin>", line 1
* SyntaxError: (unicode error) 'unicodeescape' codec can't decode bytes in position 0-5: truncated \UXXXXXXXX escape
* >>> '\U00000041\U00000042\U00000043\U00000044'
* 'ABCD'
* String as bytes object
* The method str.encode returns an encoded version of the string as a bytes object.
* The method bytes.decode returns a string decoded from the given bytes.
* >>> a = ('deヿfㇿg').encode ; a ; isinstance(a, bytes) ; len(a)
* b'de\xe3\x83\xbff\xe3\x87\xbfg'
* True
* 10 # bytes
* >>> a == ( b'de' # Ascii characters 'de'
* ... + b'\xe3\x83\xbf' # Character 'ヿ'
* ... + b'f' # Ascii character 'f'
* ... + b'\xe3\x87\xbf' # Character 'ㇿ'
* ... + b'g') # Ascii character 'g'
* True
* >>> b'\xe3\x83\xbf'.decode
* 'ヿ' # 3 bytes for this character.
* >>> b'\xe3\x87\xbf'.decode
* 'ㇿ' # 3 bytes for this character.
* >>> s = a.decode ; s ; isinstance(s, str) ; len(s)
* 'deヿfㇿg'
* True
* 6 # characters
* >>> a = ('deヿfㇿg').encode(encoding='ascii') ; a
* Traceback (most recent call last):
* File "<stdin>", line 1, in <module>
* UnicodeEncodeError: 'ascii' codec can't encode character '\u30ff' in position 2: ordinal not in range(128)
* >>> a = ('defg').encode(encoding='ascii') ; a
* b'defg'
* >>> a.decode
* 'defg'
* >>> s = ' \u0391 \u03b1 \u0392 \u03b2 \u0393 \u03b3 \u0394 \u03b4 '; isinstance(s, str)
* True
* >>> s == ' Α α Β β Γ γ Δ δ ' # Greek
* True
* >>> a = (s).encode ; a ; isinstance(a, bytes)
* b' \xce\x91 \xce\xb1 \xce\x92 \xce\xb2 \xce\x93 \xce\xb3 \xce\x94 \xce\xb4 ' # Greek encoded
* True
* >>> a.decode
* ' Α α Β β Γ γ Δ δ ' # Greek decoded
* >>> s = ' \u0426 \u0446 \u0427 \u0447 \u0428 \u0448 \u0429 \u0449 ' ; s
* ' Ц ц Ч ч Ш ш Щ щ ' # Cyrillic
* >>> a = (s).encode ; a
* b' \xd0\xa6 \xd1\x86 \xd0\xa7 \xd1\x87 \xd0\xa8 \xd1\x88 \xd0\xa9 \xd1\x89 ' # Cyrillic encoded
* >>> a.decode
* ' Ц ц Ч ч Ш ш Щ щ ' # Cyrillic decoded

## Common String methods

* In the lesson above we've seen some methods in action: str.format, str.encode, bytes.decode.
* Many more methods are available for processing strings. Some are shown below:
* Information about string
* >>> '123456'.isnumeric
* True
* >>> ' abcd efg abc '.isalnum
* False # not alpha-numeric
* >>> 'abcd123efg789abc'.isalnum
* True # all alpha-numeric
* >>> 'abcdABCefgZYXabc'.isalpha
* True # all alphabetic
* >>> '01234567'.isdecimal
* True # all decimal
* >>> ' '.isspace
* True
* >>> ''.isspace
* False
* Substring within string
* Existence of substring
* >>> 'abc' in ' abc 123 '
* True
* >>> 'abcd' in ' abc 123 '
* False
* Position of substring
* >>> ' abcd efg abc '.find('c')
* 5 # found 'c' in position 5
* >>> ' abcd efg abc '.find('x')
* -1 # did not find 'x'
* >>> ' abcd efg abc '.find('c',7)
* 14 # found 'c' at or after position 7 at position 14.
* >>> ' abcd efg abc '.find('c',7,12)
* -1 # did not find 'c' in range specified
* >>> ' abcd efg abc '.index('c',7)
* 14 # same as 'find' above unless not found.
* >>> ' abcd efg abc '.index('c',7,12)
* Traceback (most recent call last):
* File "<stdin>", line 1, in <module>
* ValueError: substring not found
* Formatting the string
* >>> ' 1234 '.strip
* '1234' # remove leading and trailing whitespace
* >>> '\t2345\t56\t678'.expandtabs
* ' 2345 56 678' # default is 8.
* >>> '\t2345\t56\t678'.expandtabs(12)
* ' 2345 56 678'
* >>> 'abc def'.zfill(30)
* '00000000000000000000000abc def' # left fill with zeroes.
* >>> 'abcd'.center(30)
* ' abcd ' # center given string in a length of 30
* >>> ' abcd '.center(30,'+')
* '++++++++++++ abcd ++++++++++++' # and fill with '+'
* >>> 'value ='. format(6)
* 'value = 6'
* Splitting the string
* >>> ' 1234 456 23456 '.split
* ['1234', '456', '23456'] # retain non-whitespace in a list
* >>> ' 1234.567e-45 '.partition('e')
* (' 1234.567', 'e', '-45 ') # split into 3 around string supplied
* >>> '\n'.join(['abc\n','def','012\n\n']).splitlines(keepends=True)
* ['abc\n', '\n', 'def\n', '012\n', '\n']
* Miscellaneous
* >>> ' The quick brown fox .... '.replace('quick','lazy')
* ' The lazy brown fox .... '
* >>> '_'.join(['abc','def','012'])
* 'abc_def_012'
* Methods may be chained
* >>> ' 1234.567E-45 '.strip.lower.partition('e')
* ('1234.567', 'e', '-45')
* >>> ' The quick brown fox .... '.replace('quick','lazy').upper.split
* ['THE', 'LAZY', 'BROWN', 'FOX', '....']
* Methods recognize international text
* >>> 'Βικιεπιστήμιο'.isupper
* False
* >>> 'Βικιεπιστήμιο'.upper
* 'ΒΙΚΙΕΠΙΣΤΉΜΙΟ'
* >>> 'Βικιεπιστήμιο'.lower
* 'βικιεπιστήμιο'
* >>> 'Βικιεπιστήμιο'.isalpha
* True

## More operations on strings

* At this point we're familiar with strings as perhaps a single line.
* But strings can be much more than a single line. The whole of "War and Peace" could be a single string.
* In this part of the lesson we'll look at "paragraphs" where a paragraph contains one or more non-blank lines.
* Consider string:
* >>> a = '\n\n line 1 \n line 2 \n line 3 \n\n'
* >>> print (a)
* line 1
* line 2
* line 3
* This string contains a paragraph surrounded by messy white space. We'll improve the appearance of the string by removing
* insignificant white space. First:
* >>> a = a.strip ; a
* 'line 1 \n line 2 \n line 3'
* >>> print (a)
* line 1
* line 2
* line 3
* Remove insignificant white space around each line:
* >>> L1 = a.splitlines ; L1
* ['line 1 ', ' line 2 ', ' line 3'] # a list containing 3 lines.
* >>> for p in range(len(L1)-1, -1, -1) : L1[p] = L1[p].strip
* >>> L1
* ['line 1', 'line 2', 'line 3'] # each line has had beginning and ending white space removed, including '\n'.
* >>> s = ''.join(L1) ; s # lines joined with ''.
* 'line 1line 2line 3'
* >>> print (s)
* line 1line 2line 3
* >>> s = '\n'.join(L1) ; s # lines joined with '\n'.
* 'line 1\nline 2\nline 3'
* >>> print (s)
* line 1
* line 2
* line 3 # a clean paragraph
* The next string is a "page" where a page contains two or more paragraphs.
* >>> a = '''
* ... paragraph 1, line 1
* ... paragraph 1, line 2
* ... paragraph 2, line 1
* ... paragraph 2, line 2
* ... paragraph 2, line 3
* ... paragraph 3, line 1
* With this page we'll do the same as above, that is, remove insignificant white space.
* >>> b = a.strip ; print (b)
* paragraph 1, line 1
* paragraph 1, line 2
* paragraph 2, line 1
* paragraph 2, line 2
* paragraph 2, line 3
* paragraph 3, line 1
* Remove white space around each line (including blank lines):
* >>> L1 = b.splitlines
* >>> for p in range(len(L1)-1, -1, -1) : L1[p] = L1[p].strip
* >>> print ('\n'.join(L1))
* paragraph 1, line 1
* paragraph 1, line 2
* paragraph 2, line 1
* paragraph 2, line 2
* paragraph 2, line 3
* paragraph 3, line 1
* Remove extraneous lines between paragraphs:
* >>> for p in range(len(L1)-1, 0, -1) : # terminator here is 0.
* ... if len( L1[p] ) == len( L1[p-1] ) == 0 : del L1[p]
* >>> print ('\n'.join(L1))
* paragraph 1, line 1
* paragraph 1, line 2
* paragraph 2, line 1
* paragraph 2, line 2
* paragraph 2, line 3
* paragraph 3, line 1
* Complicated strings simplified
* If you are working with strings containing complicated sequences of escaped characters,
* or if the whole concept of escaped characters is difficult, you might try:
* >>> back_slash = '\ '[:1] ; back_slash ; len(back_slash)
* 1
* >>> new_line = """
* ... """ # Between """ and """ there is exactly one return.
* >>> new_line ; len(new_line)
* '\n'
* 1
* >>> tab = ' ' ; tab ; len(tab) # Between ' and ' there is exactly one tab.
* '\t'
* 1
* >>> hex(ord(back_slash))
* '0x5c'
* >>> hex(ord(new_line))
* '0xa'
* >>> hex(ord(tab))
* '0x9'
* Then you can build long strings:
* >>> 'abc' + back_slash*7 + '123' + (back_slash*5 + new_line*3)*5 + 'xyz'
* 'abc\\\\\\\\\\\\\\123\\\\\\\\\\\n\n\n\\\\\\\\\\\n\n\n\\\\\\\\\\\n\n\n\\\\\\\\\\\n\n\n\\\\\\\\\\\n\n\nxyz'
* You can put the back_slash at the end of a string:
* >>> a = 'abc' + back_slash ; a ; len (a)
* 'abc\\'
* 4
* If you have a long string, splitting it might help to reveal significant parts:
* >>> a = 'abc' + back_slash*7 + '123' + (back_slash*5 + new_line*3)*5 + 'xyz'
* >>> a.split(back_slash)
* ['abc', '', '', '', '', '', '', '123', '', '', '', '', '\n\n\n', '', '', '', '', '\n\n\n', '', '', '', '', '\n\n\n', '', '', '', '', '\n\n\n', '', '', '', '', '\n\n\nxyz']
* >>> a.split(back_slash*5)
* ['abc', '\\\\123', '\n\n\n', '\n\n\n', '\n\n\n', '\n\n\n', '\n\n\nxyz']
* >>> a.split(back_slash*5 + new_line*3)
* ['abc\\\\\\\\\\\\\\123', '', '', '', '', 'xyz']

## Assignments

* It seems that modern international characters with numeric values greater then 0xFFFF are not standardized.
* Consider character '\U00010022'. This displays on the interactive Python command line as a little elephant with three legs, within emacs and on the Unix command line as a Greek delta (almost), and in Wikiversity as, well, that depends.
* >>> '\U00010022'
* '𐀢' # Copied from Python in interactive mode.
* >>> '\U00010022' == chr(0x10022) == '\U+10022'
* True
* >>> '\U+10022'
* File "<stdin>", line 1
* SyntaxError: (unicode error) 'unicodeescape' codec can't decode bytes in position 0-1: truncated \UXXXXXXXX escape
* Within interactive python, when you move the cursor over '\U+10022', it steps over one character.
* When you move the cursor over '\U00010022', it steps over ten characters.
* '\U00010022' is 'ð' as copied from emacs.
* Experiment with characters with numeric values greater than 0xFFFF and note the apparently inconsistent results. If you are producing text
* with modern international characters, ensure that the character/s displayed are what you want.

## Further Reading or Review

Completion status: Almost complete, but you can help make it more thorough.
Previous Lesson: Numbers
This lesson: Strings
Next Lesson: Lists
Course Home Page :