Skip to Content
Course content

322: Encoding and Decoding Text Safely

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

I was digging through some legacy logs from a client's old database export yesterday, and I ran straight into a classic Python headache. I assumed the files were UTF-8—because, honestly, everything should be UTF-8 in 2024—but as soon as I tried to read the file, Python threw a UnicodeDecodeError right in my face. Let's walk through how I fixed it, because this is where most developers get tripped up when moving data between systems.

The moment it all breaks

I started with the simplest approach. I had a file called reviews.txt that contained user feedback, including some characters like the Euro symbol (€) and some accented letters from French users. Here is how I first tried to open it:

with open('reviews.txt', 'r') as f:
    data = f.read()
# Result: UnicodeDecodeError: 'utf-8' codec can't decode byte 0x80 in position 102...

The error tells me exactly where the problem is: position 102 has a byte (0x80) that doesn't fit the UTF-8 pattern. This usually happens when a file was saved using a different encoding—like Latin-1 (ISO-8859-1) or Windows-1252—but Python is trying to read it as UTF-8 (which is the default on most modern systems).

Guessing the encoding

Now, I could spend an hour guessing the encoding, but that's a waste of time. I tried switching to latin-1 just to see if the file would at least open. Latin-1 is a "safe" bet for reading because it maps every possible byte to something, so it rarely crashes.

with open('reviews.txt', 'r', encoding='latin-1') as f:
    data = f.read()
    print(data)
# Result: "The price was 10\x80 and the café was great!"

Wait, look at that. It didn't crash, but the text is garbage. café instead of café. This is called "mojibake." What happened here is that the file actually was UTF-8, but there was one single "dirty" byte somewhere that didn't belong, and by forcing the whole thing into Latin-1, I've corrupted the parts that were actually correct. I've traded a crash for silent data corruption, which is actually much worse.

Dealing with the 'dirty' data

If I know the file is 99% UTF-8 but has a few corrupted bytes from a legacy system, I don't want to throw away the whole file or guess a different encoding. I need to tell Python how to handle the characters it doesn't understand. This is where the errors argument comes in.

I tried errors='ignore' first, but that just deletes the offending characters, which can change the meaning of the text. Instead, I prefer errors='replace'. It inserts a official "replacement character" (usually a diamond with a question mark) so I can see exactly where the data was corrupted.

with open('reviews.txt', 'r', encoding='utf-8', errors='replace') as f:
    data = f.read()
    print(data)
# Result: "The price was 10 and the café was great!"

Now the café is correct, and I can see that the Euro symbol was the culprit. I've safely decoded the text without crashing and without corrupting the valid parts.

The bytes-to-string pipeline

To really wrap your head around this, you have to remember that str in Python is Unicode (abstract characters), but files on a disk are bytes (numbers from 0-255). Encoding is the process of turning a string into bytes; decoding is turning bytes back into a string.

If I'm dealing with an API response or a network socket where I get raw bytes, I should do the decoding explicitly. It makes the code much more readable for the next person who has to maintain it.

raw_data = b'Hello \xf0\x9f\x98\x8a' # Raw bytes including an emoji
# I'll decode this explicitly to a string
clean_text = raw_data.decode('utf-8')
print(clean_text) # Result: Hello 😊

My rule of thumb? Always be explicit. Don't rely on the system default encoding, because your code might work on your Mac but crash on a Windows server. Always specify encoding='utf-8' when opening files, and use errors='replace' if you're dealing with unpredictable third-party data.




📋 Practical Task

Exercise: The Corrupted Log Sanitizer

You have been given a byte-string representing a log file from a legacy sensor. The log is mostly UTF-8, but it contains some "garbage" bytes (\xff and \xfe) that cause standard decoding to fail.

Your Task: Write a function called sanitize_log(raw_bytes) that takes a byte-string and returns a clean, decoded string. The function must:

  1. Decode the bytes using utf-8.
  2. Handle decoding errors by replacing them with the replacement character (do not ignore them).
  3. Return the resulting string.
# Test Case
log_data = b"Sensor_1: OK\nSensor_2: \xff ERROR\nSensor_3: \xfe OK"
# Expected Output: 
# "Sensor_1: OK
# Sensor_2:  ERROR
# Sensor_3:  OK"

def sanitize_log(raw_bytes):
    # Your code here
    pass

print(sanitize_log(log_data))
Rating
0 0

There are no comments for now.

to be the first to leave a comment.