Skip to Content
Course content

111: Substitution with re.sub

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I was digging through some legacy server logs this morning, and I ran into a classic headache. The logs were a mess—some entries used dashes for dates, some used slashes, and some had weird whitespace. I needed to scrub these dates out for a privacy report, and my first instinct was to just use .replace().

The limits of simple replacement

I started with something like this, trying to normalize the date separators before removing them:

log_entry = "Error at 2023-10-12 14:00:01 - User failed login. See 2023/10/12 for details."
clean_entry = log_entry.replace("-", "/").replace(" ", "_")
print(clean_entry)
# Output: Error at 2023/10/12_14:00:01_/_User_failed_login._See_2023/10/12_for_details.

Yeah, that's a disaster. .replace() is great when you know exactly which character you're hunting, but it's a blunt instrument. It doesn't understand patterns. It just sees a dash and kills it, regardless of whether that dash is part of a date or just a separator in the sentence. I don't want to replace every dash; I only want to replace things that look like dates.

Testing the waters with re.sub

This is where re.sub() comes in. The "sub" is short for substitution. Instead of a static string, I can give it a regex pattern. I'll try to target those dates (YYYY-MM-DD or YYYY/MM/DD) and swap them for a generic [DATE] tag.

import re

log_entry = "Error at 2023-10-12 14:00:01 - User failed login. See 2023/10/12 for details."
# I'll try a simple pattern for 4 digits, a separator, 2 digits, a separator, 2 digits
pattern = r"\d{4}[-/]\d{2}[-/]\d{2}"
result = re.sub(pattern, "[DATE]", log_entry)
print(result)
# Output: Error at [DATE] 14:00:01 - User failed login. See [DATE] for details.

That's much better. In one move, I handled both the dashes and the slashes. The logic here is simple: re.sub(pattern, replacement, original_string). It scans the string and every time the pattern hits, it swaps it out.

Dealing with messy whitespace

But wait, looking closer at the logs, some of the entries have accidental double spaces or tabs that make the output look jagged. I want to collapse any sequence of two or more whitespace characters into a single space. I'll try to chain another re.sub call.

messy_log = "Error   at [DATE]    14:00:01   - User failed login."
# \s matches any whitespace, + means "one or more"
# But I want "two or more", so I'll use {2,}
clean_log = re.sub(r"\s{2,}", " ", messy_log)
print(clean_log)
# Output: Error at [DATE] 14:00:01 - User failed login.

I love this because it's surgical. I'm not just replacing every space with a space (which would be pointless); I'm specifically targeting the "clumps" of whitespace. It keeps the single spaces intact while cleaning up the noise.

Stopping the substitution early

One last thing: sometimes I only want to mask the first date found in a line—maybe the first date is the event time, and the second date is a reference that needs to stay. I noticed re.sub has an optional count argument. Let's see what happens when I use it.

log_entry = "Event: 2023-10-12. Reference: 2023-11-01."
# Only replace the first occurrence
result = re.sub(r"\d{4}-\d{2}-\d{2}", "[DATE]", log_entry, count=1)
print(result)
# Output: Event: [DATE]. Reference: 2023-11-01.

Exactly what I needed. By default, count is 0, which means "replace everything." Setting it to 1 makes it stop after the first match. It's a small detail, but it prevents you from over-cleaning your data when precision matters.




📋 Practical Task

The Product SKU Standardizer

You are working with a database of product SKUs that were entered by different people over five years. Some use underscores, some use hyphens, and some use dots. To make the database searchable, you need to standardize all SKUs to use only hyphens.

Your Task: Write a function called standardize_sku(sku) that takes a string and replaces any sequence of one or more underscores, dots, or hyphens with a single hyphen.

Test Case:
Input: "PROD__123...456--ABC_XYZ"
Expected Output: "PROD-123-456-ABC-XYZ"

Rating
0 0

There are no comments for now.

to be the first to leave a comment.