Why does my text show weird characters like é?
You are seeing UTF-8 bytes interpreted with a single-byte encoding. Declare UTF-8 everywhere — file, database, connection and HTTP header — rather than trying to repair the characters.
é in UTF-8 is two bytes: C3 A9. Read as Latin-1, those two bytes are à and © — hence é. The data is fine; the interpretation is wrong.
#Declare UTF-8 at every layer
HTML:
<meta charset="utf-8">HTTP header (this wins over the meta tag):
Content-Type: text/html; charset=utf-8Python:
open(path, encoding="utf-8")Without it, Python uses a platform default — often cp1252 on Windows. This is the classic "works on my machine" encoding bug.
MySQL: use utf8mb4, not utf8.
ALTER DATABASE app CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;MySQL's utf8 is a historical mistake — it only stores three bytes per character, so emoji and some CJK characters fail. utf8mb4 is real UTF-8. Set the connection charset too:
new PDO('mysql:host=localhost;dbname=app;charset=utf8mb4', …);#Question marks instead of mojibake
? or \ufffd means the data was already lost in a conversion — the bytes are gone. Fix the pipeline, then re-import the source.
#Characters are not bytes
len("café") # 4 characters
len("café".encode()) # 5 bytesTruncating a UTF-8 string by bytes can split a character in half. Slice by characters, or use a library that understands grapheme clusters — some emoji are several code points that render as one symbol.