Skip to content

Lab 03 - Working with Strings

In this lab, you will be working in the Python interactive interpreter with the "string" data type, exploring its syntax and functionality.

Task 1 - Explore Basic String Operations

Step 1-1

On the lab Linux Jump Host, execute the following in a Python interpreter session (run python in an SSH session or terminal if you need).

Create the variable hostname and assign it the value of 'CORESWITCH-A':

>>>
>>> hostname = 'CORESWITCH-A'
>>>

Note: In Python, ' and " can be used interchangeably to define strings, as long as the beginning and ending characters match (i.e. 'A string' or "A String").

Step 1-2

Verify the data type of the hostname variable and prove that it is a string:

>>> type(hostname)
<class 'str'>
>>>

Step 1-3

Now convert the value of hostname to be lowercase using the lower built-in method.

>>> hostname.lower()
'coreswitch-a'
>>>

Notice how the new value coreswitch-a is printed automatically. This is because this is the value being returned from the lower method.

When you type a variable.method() command in at the shell window, you will see the returned data printed to the terminal. In this case, it's coreswitch-a -- this data is not saved (yet). In order to do that, you'll need to perform variable assignment.

Step 1-4

Re-run the previous statement assigning the new hostname to the variable lower_hostname and then print the new hostname. This time print it using the print statement.

>>> lower_hostname = hostname.lower()
>>>
>>> print(lower_hostname)
coreswitch-a
>>>
>>> lower_hostname
'coreswitch-a'
>>>

As you can see, not only you can print by using the print statement, but also by typing the variable name and pressing Enter. Using the print statement in the Python shell is not a requirement, but it is needed if you want to render escape characters such as \n (newline).

Step 1-5

Create a new variable called interface_config and assign it the value of "interface Eth1\n duplex full\n speed 100":

>>> interface_config = "interface Eth1\n duplex full\n speed 100"
>>>

Step 1-6

Print interface_config with and without using the print statement:

Without the print statement:

>>> interface_config
'interface Eth1\n duplex full\n speed 100'
>>>

With the print statement:

>>> print(interface_config)
interface Eth1
 duplex full
 speed 100
>>>

Step 1-7

Let's now take a look at another method - this time we'll look at using replace().

Create a new variable called ip_addr and assign it the value of '10.20.5.5'.

>>>
>>> ip_addr = '10.20.5.5'
>>>

Step 1-8

Once it's created, use the built-in method replace to replace only the third octet. The new 3rd octet should be 100.

>>>
>>> ip_addr.replace('5', '100')
'10.20.100.100'
>>>

Notice that this replaces the 3rd and 4th octets. We only want to replace the 3rd octet though. The replace built-in method supports an optional parameter which specifies how many occurrences to replace of the matching string.

Step 1-9

Use that parameter to replace only ONE occurrence of the number 5.

The format to use is replace(old, new, count).

>>>
>>> ip_addr.replace('5', '100', 1)
'10.20.100.5'
>>>

Remember you need to assign the value returned from the method into a variable should you want to use it further down in your code.

Step 1-10

Store the value of the new IP address in a variable called ip_addr2.

For this task, you can replace both the 3rd and 4th octets:

>>> ip_addr2 = ip_addr.replace('5', '100')
>>>
>>> print(ip_addr2)
10.20.100.100
>>>

Step 1-11

Let's take a step back. So far, you've used the lower and replace methods. You can use the built-in function called dir() to print ALL built-in methods for a given datatype by doing either dir(<datatype>) or dir(<variable>.

Print the list of available built-in methods for strings. To do this, you can use any string variable or str to see these methods.

Using a variable that is a string:

>>> dir(ip_addr2)
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isascii', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']

And now using str as the data type:

>>> dir(str)
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isascii', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
>>>

As you can see, either the variable or str works just fine.

Step 1-12

The type function allows you to see the data type. The dir() function allows you to see all built-in methods.

And help() allows you to learn how to use a given method.

Use help() on a few different methods:

>>> help(str.upper)

# Then press "q" to quit the help text.
>>> help(str.replace)

# Then press "q" to quit the help text.
>>> help(str.split)

# Then press "q" to quit the help text.

Step 1-13

Print each octet in ipaddr individually using the split built-in method.

>>> octets = ip_addr2.split('.')
>>>
>>> print(octets)
['10', '20', '100', '100']
>>>
>>> print(octets[0])
10
>>>
>>> print(octets[1])
20
>>>
>>> print(octets[2])
100
>>>
>>> print(octets[3])
100

Note: The split method returns another data type called a list. In this case, by splitting the string based on a delimiter, you end up with a list of 4 smaller strings.

Step 1-14

Verify the data type of octets by using the type() function.

>>> type(octets)
<class 'list'>
>>>

Don't worry, we're showing this now because split is a method for strings, but we'll cover lists in detail in an upcoming lab.

Task 2 - Extract String Contents

As a network engineer, you'll often work with numbers in the form of IP Addresses, MAC addresses, Interface Numbers etc., and you may need to validate the correct syntax of an user input parameter. As such, you may want to ensure the value entered is really a number.

Let's say you need to parse the interface "Ethernet1/10" and want to ensure the interface has a number followed by a "/" followed by another number.

There are a few ways to do this. One way is to use the isdigit method combined with the split and lstrip methods. Let's take a look.

Step 2-1

Create a new variable called interface that stores a value of 'Ethernet1/10'.

>>> interface = 'Ethernet1/10'
>>>

Step 2-2

Use lstrip or strip to strip off the word "Ethernet" from the interface object.

>>> interface.lstrip('Ethernet')
'1/10'
>>>

Note: The strip methods can strip characters, or by default, white space.

Step 2-3

Create a new variable that stores the resulting string after "Ethernet" is stripped.

>>> int_id = interface.lstrip('Ethernet')
>>>
>>> int_id
'1/10'
>>>

Step 2-4

Create two new variables called slot and intf - each should store the slot/interface, respectively.

>>> slot = int_id.split('/')[0]
>>>
>>> intf = int_id.split('/')[1]

Remember that split returns a list and you can access elements in a list using an index value starting at zero. Again, we'll cover lists in depth in an upcoming lab.

Step 2-5

You could also try this in two separate steps:

>>> parsed_interface = int_id.split('/')
>>>
>>> parsed_interface
['1', '10']
>>>
>>> slot = parsed_interface[0]
>>> intf = parsed_interface[1]
>>>

Step 2-6

Finally, use the isdigit method to see if the values are digits.

>>> slot.isdigit()
True
>>>
>>> intf.isdigit()
True
>>>

Feel free to run through this example again with a typo such as EthernetX/10, and see what happens.

If you do another example, you'll need to re-run the "parsing" statements stripping "Ethernet", splitting on /, and then checking each element to see if it is a number.

Note: This is just one example of how this could be done to teach particular methods. There are other ways this could be done that are more "production" grade.

Task 3 - Format String Output

When working with CLI commands, you usually know the command to enter, but just need the particular value to configure. So, if you are configuring Ethernet1/10, you may need to configure its speed, duplex, and description.

As a network engineer, you already know the interface commands for each of these are:

speed { auto | 100 | 1000}
duplex { auto | full }
description { user text }

When you're working in Python, you may store the values you want to insert in a variable (or take them in as user inputs). Let's take a look.

Step 3-1

Configure the following variables that will be treated as simulated user inputs:

>>> speed = '1000'
>>>
>>> duplex = 'full'
>>>
>>> description = 'Uplink Interface Configured by Python'

Step 3-2

Create three new command strings and insert the proper value into each command using the format built-in method.

>>> speed_cmd = 'speed {}'.format(speed)
>>>
>>> duplex_cmd = 'duplex {}'.format(duplex)
>>>
>>> descr_cmd = 'description {}'.format(description)

Note: the variables are being inserted where the curly braces are. This is merely a templated string.

Step 3-3

Print each of the variables you created in the previous step:

>>> print(speed_cmd)
speed 1000
>>>
>>> print(duplex_cmd)
duplex full
>>>
>>> print(descr_cmd)
description Uplink Interface Configured by Python

Step 3-4

Create a new variable called default_gw and assign it the value of "10.{}.10.1".

>>> default_gw = "10.{}.10.1"
>>>

Step 3-5

Create a new variable called site_id and assign it the value of "20":

>>> site_id = "20"
>>>

Step 3-6

Using the format method, insert site_id in the default_gw string:

>>> default_gw.format(site_id)
'10.20.10.1'
>>>

Step 3-7

Perform the same step again using "30" as site_id:

>>> site_id = "30"
>>>
>>> default_gw.format(site_id)
'10.30.10.1'
>>>

Step 3-8

Create variables called service_id, node_id, and mask. Assign them the values of "100", "1", and "24" respectively:

>>> service_id = "100"
>>>
>>> node_id = "1"
>>>
>>> mask = "24"
>>>

Step 3-9

You will now use a more modern (Python 3.6+) method to insert all 4 variables into their correct locations in the default_gw string:

>>> default_gw = f"10.{site_id}.{service_id}.{node_id}/{mask}"
>>>
>>> default_gw
'10.30.100.1/24'
>>>

Note: Pay special attention to the f character preceding the actual string - this tells the Python interpreter to parse it as a formatted string (or f-string)!

Step 3-10

You can also format the spacing of a string while using the format method. For example, you may want a nicely formatted string that is spaced accordingly with a pre-defined amount of spaces between each "column".

If you use the syntax :<integer> in between the curly brackets, it'll ensure you have that many spaces before the variable is printed. Let's take a look:

Standard printing with the format method:

>>> "{} {} {}".format("Hostname", "Location", "Vendor")
'Hostname Location Vendor'

>>>

Allocating 12 spaces for each element:

>>> "{:12} {:12} {:12}".format("Hostname", "Location", "Vendor")
'Hostname     Location     Vendor      '
>>>

If you're printing "rows" in each table, you may have devices as such:

>>> "{:12} {:12} {:12}".format("nyc-rt01", "New York", "Cisco")
'nyc-rt01     New York     Cisco       '
>>>

And another, but this time with f-strings:

>>> f"{'nyc-rt02':12} {'New York':12} {'Juniper':12}"
'nyc-rt02     New York     Juniper     '
>>>

This will be more handy as you start to do more with loops and have the desire to print text tables. This is in contrast with manually adding spaces in your templated string.

Note: While the format() method is widely used due to its backwards compatibility with Python 2 (which is now End of Life!), we recommend using only f-strings going forward due to their more compact and easier to read syntax.

Conclusion

In this lab you explored various "string" data type functionality and used it to perform operations that change, validate, and print network oriented data.