What Is String Indexing and Why It Matters
String indexing is how you access a single character at a specific position within a text string. In Python, strings are sequences, and each character has a numbered position you can reference and pull out. When you parse a trade confirmation, extract a sort code from a bank account string, or validate the format of a regulatory identifier, you are using indexing.
Most programmers come from languages where counting starts at 1. Python does not. Python counts from 0. Off by one errors are the most common mistake in indexing work. Once you understand how numbering works and how to reference positions safely, indexing becomes your tool for precise text extraction and validation.
How Python Numbers Characters: Zero Based Indexing
Python uses zero based indexing. The first character in any string sits at position 0, not position 1. The second character is at position 1. The third is at position 2. And so on.
Take this example string:
firm_name = "Barclays Bank"
Here is how Python sees the positions:
B a r c l a y s B a n k
0 1 2 3 4 5 6 7 8 9 10 11 12
Position 0 is B. Position 1 is a. Position 8 is the space. Position 12 is k, the last character.
If you think "I want the first character" and ask for index 1, you will get the wrong result. The mental habit you need is: "position 0 is where it starts."
Python also offers negative indexing, which counts backward from the end. Position -1 is always the last character. Position -2 is the second to last, and so on. This is genuinely useful when you do not need to know the string length and you want the end of it.
B a r c l a y s B a n k
-13 -12 -11 -10 -9 -8 -7 -6 -5 -4 -3 -2 -1
The negative index for the first character is always the negative length of the string. For "Barclays Bank" (13 characters), it is -13.
Accessing Characters with Square Bracket Notation
To grab a character from a string, write the string name or value followed by square brackets with the position number inside. That is the syntax. It looks like this:
text[position]
Forward Indexing
Forward indexing uses positive numbers starting from 0.
account = "GB82WEST12345678901234"
print(account[0]) # G
print(account[1]) # B
print(account[2]) # 8
print(account[4]) # E
print(account[-1]) # 4
You can also index into a string literal directly:
print("Treasury"[0]) # T
print("Treasury"[3]) # s
In finance, fixed format parsing is routine. If you receive a fixed format file where the first two characters are always the country code, the next four are the bank code, and the next two are a check digit, you can pull those out one position at a time:
iban = "GB82WEST12345678901234"
country_code = iban[0] + iban[1]
bank_code = iban[4:8]
print(f"Country: {country_code}, Bank: {bank_code}")
Negative Indexing
Negative indexing counts backward from the end. Position -1 is the last character. Position -2 is the one before that.
filename = "settlement_2024_01_15.csv"
print(filename[-1]) # v (last char)
print(filename[-2]) # s (second to last)
print(filename[-4:]) # .csv (last 4 chars, using slice)
Negative indexing is useful when you know you want the end of something but do not need to know the string length. For instance, if you want to check the file extension:
A weekly note on treasury, liquidity and practical Python. No spam, unsubscribe any time.
file = "report.xlsx"
if file[-4:] == ".csv":
print("CSV file")
elif file[-4:] == "xlsx":
print("Excel file")
Common Mistakes and How to Avoid Them
Off by One Errors
The most common mistake is forgetting that Python counts from 0.
text = "BOND"
print(text[1]) # This is O, not B. B is at text[0].
If you think "I want the 3rd character", remember to ask for index 2, not 3.
Index Out of Range
If you ask for a position that does not exist, Python throws an IndexError.
text = "Bond"
print(text[0]) # B, fine
print(text[4]) # IndexError: string index out of range
The string "Bond" has four characters at positions 0, 1, 2, 3. Position 4 does not exist. Be careful when you are parsing user input or files where the length might vary.
Indexing Non String Types
Indexing works on strings, lists, and tuples. It does not work on numbers or dictionaries (dictionaries use keys instead). If you try to index a number, you get a TypeError.
number = 12345
print(number[0]) # TypeError: 'int' object is not subscriptable
Convert to a string first if you need to:
number = 12345
digit_string = str(number)
print(digit_string[0]) # 1
Defensive Coding with Length Checks
def extract_sort_code(account_id):
if len(account_id) < 8:
return None # Not long enough
return account_id[0:6] # First 6 chars
sort_code = extract_sort_code("123456789")
print(sort_code) # 123456
A safer approach: use slicing instead of single indexing. If the string is too short, slicing returns an empty string or a partial result instead of crashing. text[0:10] will not error even if the string is only 3 characters long. It just returns those 3 characters.
Indexing in Finance: Real Use Cases
Parsing Fixed Format Regulatory Files
Regulatory submissions often arrive in fixed format. A line might be structured like this: positions 1 to 4 are the report type, positions 5 to 12 are the date, positions 13 to 20 are the firm ID, and so on.
report_line = "PO2120240115ABC123456MYN"
report_type = report_line[0:4] # PO21
report_date = report_line[4:12] # 20240115
firm_id = report_line[12:20] # ABC12345
print(f"Type: {report_type}, Date: {report_date}, Firm: {firm_id}")
You start with indexing individual characters, then move to slicing to pull out ranges. Indexing is the foundation.
Extracting Data from Unstructured Text
If you receive a counterparty name in a trade confirmation that always follows a colon, you can search for the colon, find its position, and then grab what comes after.
confirmation = "Counterparty: Deutsche Bank AG | Settlement: 2024-01-15"
colon_pos = confirmation.find(":")
counterparty = confirmation[colon_pos + 2:] # Skip colon and space
print(counterparty) # Deutsche Bank AG | Settlement: 2024-01-15
(This is illustrative; production code would stop at the pipe or use a proper parser.)
Validating Input Strings
You can use indexing to check the structure of identifiers. For example, a CUSIP is 9 characters: 8 alphanumeric followed by one check digit.
def is_valid_cusip_length(cusip):
if len(cusip) != 9:
return False
# First 8 should be alphanumeric
for i in range(8):
if not cusip[i].isalnum():
return False
# Last char should be a digit
return cusip[-1].isdigit()
print(is_valid_cusip_length("594918104")) # True
print(is_valid_cusip_length("59491810")) # False (too short)
When to Use Indexing and When to Use Other Methods
Indexing is powerful, but it is not always the right tool.
Use indexing when you want to grab a single character at a known position. Use slicing when you want a range of characters. Use string methods like .find(), .split(), or .strip() when you are searching or transforming. Use regex when patterns are complex or variable.
For example:
text = "20240115 Settlement Report"
print(text[0]) # Indexing: get char at pos 0 → '2'
print(text[0:8]) # Slicing: get chars 0 to 8 → '20240115'
print(text.find("Settlement")) # Method: find position → 9
print(text.split()) # Method: break into parts → ['20240115', 'Settlement', 'Report']
Indexing is the foundation. Master it before moving to slicing and regex.
Next Steps
You now understand how Python numbers characters, how to access them with square brackets, and how to avoid the common pitfalls. The next step is slicing, which extends indexing to ranges.
For structured learning with exercises, see the Academy catalogue and learning paths.

The Complete Python Course
Welcome to the most practical and beginner friendly Python Bootcamp Course on YouTube.
Take the courseGet the next one in your inbox
A weekly note across Finance & Treasury, Innovation & Automation and Career Development. No spam, unsubscribe any time.
Notes across finance and treasury, innovation and automation, and career development, written by practitioners who do the work.
