Unicode Support
MicroPython provides Unicode support for strings, with the level of support depending on the build configuration.
Terminology
This document uses the following Unicode terms:
Code point: a single Unicode value in the range U+0000 to U+10FFFF, for example U+0041
Aor U+1F600 😀. MicroPython strings are sequences of code points.Character: informally used to mean a code point. Be aware that a user-perceived character (a grapheme) may consist of several code points, such as a base letter followed by combining marks.
Byte: a single 8-bit value. In UTF-8 each code point is stored as one to four bytes (see below).
Operations such as len(), indexing and slicing act on code points, not on
graphemes or display width, so a base letter followed by a combining mark counts
as two code points.
Character Encoding
MicroPython uses UTF-8 encoding for all strings. When Unicode support is enabled
(MICROPY_PY_BUILTINS_STR_UNICODE), strings can contain any valid Unicode code
point from U+0000 to U+10FFFF.
ASCII characters (0-127) are stored in a single byte, making them as memory-efficient as on systems without Unicode support. Multi-byte UTF-8 code points use 2-4 bytes depending on the code point:
U+0000 to U+007F: 1 byte (ASCII)
U+0080 to U+07FF: 2 bytes
U+0800 to U+FFFF: 3 bytes
U+10000 to U+10FFFF: 4 bytes
Encoding and Decoding
The bytes.decode() and str.encode() methods support the following encodings:
UTF-8 (
'utf-8'or'utf8')ASCII (
'ascii')
Other encodings (such as 'latin-1', 'utf-16', etc.) are not supported and
will raise LookupError.
Example:
>>> '日本語'.encode('utf-8')
b'\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e'
>>> b'\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e'.decode('utf-8')
'日本語'
Error Handling
When decoding bytes that contain invalid UTF-8 sequences, the errors parameter
of bytes.decode() controls the behavior:
'strict'(default): RaiseUnicodeError'ignore': Skip invalid bytes (requiresMICROPY_PY_BUILTINS_BYTES_DECODE_ERRORS)'replace': Replace invalid bytes with U+FFFD � (requiresMICROPY_PY_BUILTINS_BYTES_DECODE_ERRORS)
Example:
>>> # Strict mode (default) raises an error
>>> b'hello\xffworld'.decode('utf-8')
UnicodeError: invalid UTF-8
>>> # Ignore mode skips invalid bytes
>>> b'hello\xffworld'.decode('utf-8', 'ignore')
'helloworld'
>>> # Replace mode substitutes replacement character
>>> b'hello\xffworld'.decode('utf-8', 'replace')
'hello�world'
For memory-conscious applications, consider using 'ignore' mode when processing
untrusted or partially corrupted data, as it avoids raising exceptions while still
recovering valid text.
The same errors handling applies when decoding any bytes-like object, including
via the str() constructor (for example str(buf, 'utf-8', 'replace') where
buf is a bytes, bytearray, memoryview or array object).
String Methods
When Unicode support is enabled, string methods operate on code points rather than bytes:
str.center()- Counts code points for width calculationlen(s)- Returns number of code points (not bytes)String indexing and slicing work on code-point boundaries
No support for display width calculations (East Asian width, combining characters, etc.)
Example:
>>> s = 'Hello 世界'
>>> len(s) # 8 code points
8
>>> len(s.encode()) # 12 bytes
12
>>> s.center(12) # Centered by code-point count
' Hello 世界 '
String Formatting
The %c format specifier and {:c} format code support full Unicode:
Accepts code points from 0 to 0x10FFFF
Properly encodes multi-byte UTF-8 code points
Raises
ValueErrorfor invalid code points
Example:
>>> '%c' % 65 # ASCII
'A'
>>> '%c' % 0x03B1 # Greek α
'α'
>>> '%c' % 0x1F600 # Emoji 😀
'😀'
>>> '{:c}'.format(0x4E2D) # Chinese 中
'中'
>>> # Invalid code point
>>> '%c' % 0x110000
ValueError: %c arg not in range(0x110000)
F-strings also support the :c format code:
>>> code_point = 0x2665 # Heart suit ♥
>>> f'I {code_point:c} Python'
'I ♥ Python'
Build Configuration
Unicode features are controlled by several build-time flags in mpconfigport.h:
MICROPY_PY_BUILTINS_STR_UNICODEEnable Unicode string support. When enabled, strings can contain any valid Unicode character and string operations work on character boundaries rather than byte boundaries.
Default: Enabled at
MICROPY_CONFIG_ROM_LEVEL_EXTRA_FEATURESand above.MICROPY_PY_BUILTINS_STR_UNICODE_CHECKEnable UTF-8 validation during string operations. When disabled, string operations may produce incorrect results with invalid UTF-8 sequences.
Default: Follows
MICROPY_PY_BUILTINS_STR_UNICODEsetting.MICROPY_PY_BUILTINS_BYTES_DECODE_ERRORSEnable the
'ignore'and'replace'error handlers forbytes.decode(). When enabled, invalid UTF-8 bytes can be either skipped ('ignore') or replaced with U+FFFD ('replace').Default: Enabled at
MICROPY_CONFIG_ROM_LEVEL_EXTRA_FEATURESand above.
Example Configuration
For a constrained port with limited flash, disable error handlers:
#define MICROPY_PY_BUILTINS_BYTES_DECODE_ERRORS (0)
For a port with more resources, enable all Unicode features:
#define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_EXTRA_FEATURES)
// This automatically enables:
// - MICROPY_PY_BUILTINS_STR_UNICODE
// - MICROPY_PY_BUILTINS_BYTES_DECODE_ERRORS
Limitations
MicroPython’s Unicode support has some limitations compared to CPython:
Only UTF-8 and ASCII encodings are supported
No support for Unicode normalization
No locale-aware string operations
The
errorsparameter accepts only positional arguments (not keyword arguments)String methods like
upper(),lower(), etc. work correctly only for ASCIIThe MicroPython interactive REPL and
input()function currently have limited Unicode support. The line editor is unaware of the displayed width of characters: wide characters (for example many CJK characters) take two terminal columns, while a grapheme cluster (a base code point plus combining marks, or an emoji sequence) can span several code points yet occupy a single column. Because editing tracks code points rather than displayed columns, line-editing keys such as backspace and the left/right arrows may leave the cursor misaligned with the text shown on screen. A workaround is to place the Unicode text in a UTF-8 encoded MicroPython script and run it usingmpremote run <script.py>.