<jupyter_start><jupyter_text>Strings In the section on Basic Data Types in Python, you learned how to define **strings**: objects that contain sequences of character data. Processing character data is integral to programming. It is a rare application that doesn’t need to manipulate strings at least to some extent.   Table of Contents * [String Manipulation](string_manipulation)    * [String Operators](string_operators)    * [The * Operator](the_*_operator)    * [The in Operator](the_in_operator)    * [Built-in String Functions](built-in_string_functions)        * [`ord(c)`](`ord(c)`)        * [`chr(n)`](`chr(n)`)        * [len(s)](len(s))        * [`str(obj)`](`str(obj)`)* [String Indexing](string_indexing)* [String Slicing](string_slicing)    * [Specifying a Stride in a String Slice](specifying_a_stride_in_a_string_slice)    * [Interpolating Variables Into a String](interpolating_variables_into_a_string)    * [Modifying Strings](modifying_strings)* [ Conclusion](_conclusion)---  String ManipulationThe sections below highlight the operators, methods, and functions that are available for working with strings.  String OperatorsYou have already seen the operators `+` and `*` applied to numeric operands in the section on Operators and Expressions in Python. These two operators can be applied to strings as well.<jupyter_code>s = 'foo'
t = 'bar'
u = 'baz'
s + t
s + t + u
print('Go team' + '!!!')<jupyter_output>Go team!!!<jupyter_text>The * OperatorThe `*` operator creates multiple copies of a string. If `s` is a string and `n` is an integer, either of the following expressions returns a string consisting of `n` concatenated copies of `s`:```pythons * nn * s``` Here are examples of both forms:<jupyter_code>s = 'foo.'
s * 4
4 * s<jupyter_output><empty_output><jupyter_text>The multiplier operand `n` must be an integer. You’d think it would be required to be a positive integer, but amusingly, it can be zero or negative, in which case the result is an empty string:<jupyter_code>'foo' * -8<jupyter_output><empty_output><jupyter_text>If you were to create a string variable and initialize it to the empty string by assigning it the value `'foo' * -8`, anyone would rightly think you were a bit daft. But it would work.  The in OperatorPython also provides a membership operator that can be used with strings. The in operator returns `True` if the first operand is contained within the second, and `False` otherwise:<jupyter_code>s = 'foo'
s in 'That\'s food for thought.'
s in 'That\'s good for now.'<jupyter_output><empty_output><jupyter_text>There is also a `not in` operator, which does the opposite:<jupyter_code>'z' not in 'abc'
'z' in 'xyz'<jupyter_output><empty_output><jupyter_text>Built-in String FunctionsAs you saw in the tutorial on Basic Data Types in Python, Python provides many functions that are built-in to the interpreter and always available. Here are a few that work with strings:|Function | Description||:--|:--||`chr()` | Converts an integer to a character ||`ord()` | Converts a character to an integer ||`len()` | Returns the length of a string ||`str()` | Returns a string representation of an object | These are explored more fully below.<jupyter_code>ord('?')<jupyter_output><empty_output><jupyter_text>`ord(c)`> Returns an integer value for the given character. At the most basic level, computers store all information as numbers. To represent character data, a translation scheme is used which maps each character to its representative number. The simplest scheme in common use is called [ASCII](https://en.wikipedia.org/wiki/ASCII). It covers the common Latin characters you are probably most accustomed to working with. For these characters, ord(c) returns the ASCII value for character c:<jupyter_code>ord('a')
ord('#')<jupyter_output><empty_output><jupyter_text>ASCII is fine as far as it goes. But there are many different languages in use in the world and countless symbols and glyphs that appear in digital media. The full set of characters that potentially may need to be represented in computer code far surpasses the ordinary Latin letters, numbers, and symbols you usually see. [Unicode](http://www.unicode.org/standard/WhatIsUnicode.html) is an ambitious standard that attempts to provide a numeric code for every possible character, in every possible language, on every possible platform. Python 3 supports Unicode extensively, including allowing Unicode characters within strings. As long as you stay in the domain of the common characters, there is little practical difference between ASCII and Unicode. But the `ord()` function will return numeric values for [Unicode characters](https://realpython.com/courses/python-unicode/) as well:<jupyter_code>ord('€')
ord('∑')<jupyter_output><empty_output><jupyter_text>`chr(n)`> Returns a character value for the given integer. `chr()` does the reverse of `ord()`. Given a numeric value `n`, `chr(n)` returns a string representing the character that corresponds to `n`:<jupyter_code>chr(97)
chr(35)<jupyter_output><empty_output><jupyter_text>`chr()` handles Unicode characters as well:<jupyter_code>chr(8364)
chr(8721)<jupyter_output><empty_output><jupyter_text>len(s)With `len()`, you can check Python string length. `len(s)` returns the number of characters in `s`:<jupyter_code>s = 'I am a string.'
len(s)<jupyter_output><empty_output><jupyter_text>`str(obj)`> Returns a string representation of an object.<jupyter_code>str(2)<jupyter_output><empty_output><jupyter_text>Virtually any object in Python can be rendered as a string. `str(obj)` returns the string representation of object obj:<jupyter_code>str(49.2)
str(3+4j)
str(3 + 29)
str('foo')<jupyter_output><empty_output><jupyter_text>String IndexingOften in programming languages, individual items in an ordered set of data can be accessed directly using a numeric index or key value. This process is referred to as indexing. In Python, strings are ordered sequences of character data, and thus can be indexed in this way. Individual characters in a string can be accessed by specifying the string name followed by a number in square brackets (`[]`). String indexing in Python is zero-based: the first character in the string has index 0, the next has index 1, and so on. The index of the last character will be the length of the string minus one. For example, a schematic diagram of the indices of the string 'foobar' would look like this:  The individual characters can be accessed by index as follows:<jupyter_code>s = 'foobar'
s[0]
s[1]
s[3]
len(s)
s[len(s)-1]<jupyter_output><empty_output><jupyter_text>Attempting to index beyond the end of the string results in an error:<jupyter_code>s[6]<jupyter_output><empty_output><jupyter_text>String indices can also be specified with negative numbers, in which case indexing occurs from the end of the string backward: `-1` refers to the last character, `-2` the second-to-last character, and so on. Here is the same diagram showing both the positive and negative indices into the string `'foobar'`: Here are some examples of negative indexing:<jupyter_code>s = 'foobar'
s[-1]
s[-2]
len(s)
s[-len(s)]<jupyter_output><empty_output><jupyter_text>Attempting to index with negative numbers beyond the start of the string results in an error:<jupyter_code>s[-7]<jupyter_output><empty_output><jupyter_text>For any non-empty string `s`, `s[len(s)-1]` and `s[-1]` both return the last character. There isn’t any index that makes sense for an empty string.  String SlicingPython also allows a form of indexing syntax that extracts substrings from a string, known as string slicing. If `s` is a string, an expression of the form `s[m:n]` returns the portion of `s` starting with position `m`, and up to but not including position `n`:<jupyter_code>s = 'foobar'
s[2:5]<jupyter_output><empty_output><jupyter_text>> **Remember:** String indices are zero-based. The first character in a string has index `0`. This applies to both standard indexing and slicing. Again, the second index specifies the first character that is not included in the result—the character `'r'` (`s[5]`) in the example above. That may seem slightly unintuitive, but it produces this result which makes sense: the expression `s[m:n]` will return a substring that is `n - m` characters in length, in this case, `5 - 2 = 3`. If you omit the first index, the slice starts at the beginning of the string. Thus, `s[:m]` and `s[0:m]` are equivalent:<jupyter_code>s = 'foobar'
s[:4]
s[0:4]<jupyter_output><empty_output><jupyter_text>Similarly, if you omit the second index as in `s[n:]`, the slice extends from the first index through the end of the string. This is a nice, concise alternative to the more cumbersome `s[n:len(s)]`:<jupyter_code>s = 'foobar'
s[2:]
s[2:len(s)]<jupyter_output><empty_output><jupyter_text>For any string `s` and any integer `n` (`0 ≤ n ≤ len(s)`), `s[:n]` + `s[n:]` will be equal to `s`:<jupyter_code>s = 'foobar'
s[:4] + s[4:]
s[:4] + s[4:] == s<jupyter_output><empty_output><jupyter_text>Omitting both indices returns the original string, in its entirety. Literally. It’s not a copy, it’s a reference to the original string:<jupyter_code>s = 'foobar'
t = s[:]
id(s)
id(t)
s is t<jupyter_output><empty_output><jupyter_text>If the first index in a slice is greater than or equal to the second index, Python returns an empty string. This is yet another obfuscated way to generate an empty string, in case you were looking for one:<jupyter_code>s[2:2]
s[4:2]<jupyter_output><empty_output><jupyter_text>Negative indices can be used with slicing as well. `-1` refers to the last character, `-2` the second-to-last, and so on, just as with simple indexing. The diagram below shows how to slice the substring `'oob'` from the string `'foobar'` using both positive and negative indices:  Here is the corresponding Python code:<jupyter_code>s = 'foobar'
s[-5:-2]
s[1:4]
s[-5:-2] == s[1:4]<jupyter_output><empty_output><jupyter_text>Specifying a Stride in a String SliceThere is one more variant of the slicing syntax to discuss. Adding an additional : and a third index designates a stride (also called a step), which indicates how many characters to jump after retrieving each character in the slice. For example, for the string `'foobar'`, the slice `0:6:2` starts with the first character and ends with the last character (the whole string), and every second character is skipped. This is shown in the following diagram:  Similarly, `1:6:2` specifies a slice starting with the second character (index 1) and ending with the last character, and again the stride value 2 causes every other character to be skipped:  The illustrative REPL code is shown here:<jupyter_code>s = 'foobar'
s[0:6:2]
s[1:6:2]<jupyter_output><empty_output><jupyter_text>As with any slicing, the first and second indices can be omitted, and default to the first and last characters respectively:<jupyter_code>s = '12345' * 5
s
s[::5]
s[4::5]<jupyter_output><empty_output><jupyter_text>You can specify a negative stride value as well, in which case Python steps backward through the string. In that case, the starting/first index should be greater than the ending/second index:<jupyter_code>s = 'foobar'
s[5:0:-2]<jupyter_output><empty_output><jupyter_text>In the above example, `5:0:-2` means “start at the last character and step backward by 2, up to but not including the first character.” When you are stepping backward, if the first and second indices are omitted, the defaults are reversed in an intuitive way: the first index defaults to the end of the string, and the second index defaults to the beginning. Here is an example:<jupyter_code>s = '12345' * 5
s
s[::-5]<jupyter_output><empty_output><jupyter_text>This is a common paradigm for reversing a string:<jupyter_code>s = 'If Comrade Napoleon says it, it must be right.'
s[::-1]<jupyter_output><empty_output><jupyter_text>Interpolating Variables Into a StringIn Python version 3.6, a new string formatting mechanism was introduced. This feature is formally named the Formatted String Literal, but is more usually referred to by its nickname **f-string**. One simple feature of f-strings you can start using right away is variable interpolation. You can specify a variable name directly within an f-string literal, and Python will replace the name with the corresponding value. For example, suppose you want to display the result of an arithmetic calculation. You can do this with a straightforward `print()` statement, separating numeric values and string literals by commas:<jupyter_code>n = 20
m = 25
prod = n * m
print('The product of', n, 'and', m, 'is', prod)<jupyter_output>The product of 20 and 25 is 500<jupyter_text>But this is cumbersome. To accomplish the same thing using an f-string:- Specify either a lowercase `f` or uppercase `F` directly before the opening quote of the string literal. This tells Python it is an f-string instead of a standard string.- Specify any variables to be interpolated in curly braces (`{}`). Recast using an f-string, the above example looks much cleaner:<jupyter_code>n = 20
m = 25
prod = n * m
print(f'The product of {n} and {m} is {prod}')<jupyter_output>The product of 20.293742397423973 and 25 is 507.34355993559933<jupyter_text>Any of Python’s three quoting mechanisms can be used to define an f-string:<jupyter_code>var = 'Bark'
print(f'A dog says {var}!')
print(f"A dog says {var}!")
print(f'''A dog says {var}!''')<jupyter_output>A dog says Bark!<jupyter_text>Modifying StringsIn a nutshell, you can’t. Strings are one of the data types Python considers immutable, meaning not able to be changed. In fact, all the data types you have seen so far are immutable. (Python does provide data types that are mutable, as you will soon see.) A statement like this will cause an error:<jupyter_code>s = 'foobar'
s[3] = 'x'<jupyter_output><empty_output><jupyter_text>In truth, there really isn’t much need to modify strings. You can usually easily accomplish what you want by generating a copy of the original string that has the desired change in place. There are very many ways to do this in Python. Here is one possibility:<jupyter_code>s = s[:3] + 'x' + s[4:]
s<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.09312341146568766, 'fraction_numerical': 0.011719853148828015, 'mean_word_length': 5.5761374187558035, 'pattern_counts': {'":': 0, '<': 133, '<?xml version=': 0, '>': 137, 'https://': 2, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '34', 'dir': 'jupyter-structured-clean-dedup', 'id': '591903', 'license': 'MIT', 'n_tokens_mistral': 4355, 'n_tokens_neox': 4138, 'n_words': 1857, 'path': 'Python/Basics/07 Strings.ipynb', 'repo_name': 'zsrabbani/PyTutorial', 'size': '14164'}