|
| 1 | +.. _unicode_support: |
| 2 | + |
| 3 | +Unicode Support |
| 4 | +=============== |
| 5 | + |
| 6 | +MicroPython provides Unicode support for strings, with the level of support |
| 7 | +depending on the build configuration. |
| 8 | + |
| 9 | +Terminology |
| 10 | +----------- |
| 11 | + |
| 12 | +This document uses the following Unicode terms: |
| 13 | + |
| 14 | +- **Code point**: a single Unicode value in the range U+0000 to U+10FFFF, for |
| 15 | + example U+0041 ``A`` or U+1F600 😀. MicroPython strings are sequences of |
| 16 | + code points. |
| 17 | +- **Character**: informally used to mean a code point. Be aware that a |
| 18 | + user-perceived character (a *grapheme*) may consist of several code points, |
| 19 | + such as a base letter followed by combining marks. |
| 20 | +- **Byte**: a single 8-bit value. In UTF-8 each code point is stored as one to |
| 21 | + four bytes (see below). |
| 22 | + |
| 23 | +Operations such as ``len()``, indexing and slicing act on code points, not on |
| 24 | +graphemes or display width, so a base letter followed by a combining mark counts |
| 25 | +as two code points. |
| 26 | + |
| 27 | +Character Encoding |
| 28 | +------------------ |
| 29 | + |
| 30 | +MicroPython uses UTF-8 encoding for all strings. When Unicode support is enabled |
| 31 | +(``MICROPY_PY_BUILTINS_STR_UNICODE``), strings can contain any valid Unicode code |
| 32 | +point from U+0000 to U+10FFFF. |
| 33 | + |
| 34 | +ASCII characters (0-127) are stored in a single byte, making them as memory-efficient |
| 35 | +as on systems without Unicode support. Multi-byte UTF-8 code points use 2-4 bytes |
| 36 | +depending on the code point: |
| 37 | + |
| 38 | +- U+0000 to U+007F: 1 byte (ASCII) |
| 39 | +- U+0080 to U+07FF: 2 bytes |
| 40 | +- U+0800 to U+FFFF: 3 bytes |
| 41 | +- U+10000 to U+10FFFF: 4 bytes |
| 42 | + |
| 43 | +Encoding and Decoding |
| 44 | +---------------------- |
| 45 | + |
| 46 | +The :meth:`bytes.decode` and :meth:`str.encode` methods support the following encodings: |
| 47 | + |
| 48 | +- UTF-8 (``'utf-8'`` or ``'utf8'``) |
| 49 | +- ASCII (``'ascii'``) |
| 50 | + |
| 51 | +Other encodings (such as ``'latin-1'``, ``'utf-16'``, etc.) are not supported and |
| 52 | +will raise ``LookupError``. |
| 53 | + |
| 54 | +Example:: |
| 55 | + |
| 56 | + >>> '日本語'.encode('utf-8') |
| 57 | + b'\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e' |
| 58 | + >>> b'\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e'.decode('utf-8') |
| 59 | + '日本語' |
| 60 | + |
| 61 | +Error Handling |
| 62 | +~~~~~~~~~~~~~~ |
| 63 | + |
| 64 | +When decoding bytes that contain invalid UTF-8 sequences, the ``errors`` parameter |
| 65 | +of :meth:`bytes.decode` controls the behavior: |
| 66 | + |
| 67 | +- ``'strict'`` (default): Raise ``UnicodeError`` |
| 68 | +- ``'ignore'``: Skip invalid bytes (requires ``MICROPY_PY_BUILTINS_BYTES_DECODE_ERRORS``) |
| 69 | +- ``'replace'``: Replace invalid bytes with U+FFFD � (requires ``MICROPY_PY_BUILTINS_BYTES_DECODE_ERRORS``) |
| 70 | + |
| 71 | +Example:: |
| 72 | + |
| 73 | + >>> # Strict mode (default) raises an error |
| 74 | + >>> b'hello\xffworld'.decode('utf-8') |
| 75 | + UnicodeError: invalid UTF-8 |
| 76 | + |
| 77 | + >>> # Ignore mode skips invalid bytes |
| 78 | + >>> b'hello\xffworld'.decode('utf-8', 'ignore') |
| 79 | + 'helloworld' |
| 80 | + |
| 81 | + >>> # Replace mode substitutes replacement character |
| 82 | + >>> b'hello\xffworld'.decode('utf-8', 'replace') |
| 83 | + 'hello�world' |
| 84 | + |
| 85 | +For memory-conscious applications, consider using ``'ignore'`` mode when processing |
| 86 | +untrusted or partially corrupted data, as it avoids raising exceptions while still |
| 87 | +recovering valid text. |
| 88 | + |
| 89 | +The same ``errors`` handling applies when decoding any bytes-like object, including |
| 90 | +via the ``str()`` constructor (for example ``str(buf, 'utf-8', 'replace')`` where |
| 91 | +``buf`` is a ``bytes``, ``bytearray``, ``memoryview`` or ``array`` object). |
| 92 | + |
| 93 | + |
| 94 | +String Methods |
| 95 | +-------------- |
| 96 | + |
| 97 | +When Unicode support is enabled, string methods operate on code points rather than bytes: |
| 98 | + |
| 99 | +- :meth:`str.center` - Counts code points for width calculation |
| 100 | +- ``len(s)`` - Returns number of code points (not bytes) |
| 101 | +- String indexing and slicing work on code-point boundaries |
| 102 | +- No support for display width calculations (East Asian width, combining characters, etc.) |
| 103 | + |
| 104 | +Example:: |
| 105 | + |
| 106 | + >>> s = 'Hello 世界' |
| 107 | + >>> len(s) # 8 code points |
| 108 | + 8 |
| 109 | + >>> len(s.encode()) # 12 bytes |
| 110 | + 12 |
| 111 | + >>> s.center(12) # Centered by code-point count |
| 112 | + ' Hello 世界 ' |
| 113 | + |
| 114 | +String Formatting |
| 115 | +----------------- |
| 116 | + |
| 117 | +The ``%c`` format specifier and ``{:c}`` format code support full Unicode: |
| 118 | + |
| 119 | +- Accepts code points from 0 to 0x10FFFF |
| 120 | +- Properly encodes multi-byte UTF-8 code points |
| 121 | +- Raises ``ValueError`` for invalid code points |
| 122 | + |
| 123 | +Example:: |
| 124 | + |
| 125 | + >>> '%c' % 65 # ASCII |
| 126 | + 'A' |
| 127 | + >>> '%c' % 0x03B1 # Greek α |
| 128 | + 'α' |
| 129 | + >>> '%c' % 0x1F600 # Emoji 😀 |
| 130 | + '😀' |
| 131 | + >>> '{:c}'.format(0x4E2D) # Chinese 中 |
| 132 | + '中' |
| 133 | + |
| 134 | + >>> # Invalid code point |
| 135 | + >>> '%c' % 0x110000 |
| 136 | + ValueError: %c arg not in range(0x110000) |
| 137 | + |
| 138 | +F-strings also support the ``:c`` format code:: |
| 139 | + |
| 140 | + >>> code_point = 0x2665 # Heart suit ♥ |
| 141 | + >>> f'I {code_point:c} Python' |
| 142 | + 'I ♥ Python' |
| 143 | + |
| 144 | +Build Configuration |
| 145 | +------------------- |
| 146 | + |
| 147 | +Unicode features are controlled by several build-time flags in ``mpconfigport.h``: |
| 148 | + |
| 149 | +``MICROPY_PY_BUILTINS_STR_UNICODE`` |
| 150 | + Enable Unicode string support. When enabled, strings can contain any valid |
| 151 | + Unicode character and string operations work on character boundaries rather |
| 152 | + than byte boundaries. |
| 153 | + |
| 154 | + Default: Enabled at ``MICROPY_CONFIG_ROM_LEVEL_EXTRA_FEATURES`` and above. |
| 155 | + |
| 156 | +``MICROPY_PY_BUILTINS_STR_UNICODE_CHECK`` |
| 157 | + Enable UTF-8 validation during string operations. When disabled, string |
| 158 | + operations may produce incorrect results with invalid UTF-8 sequences. |
| 159 | + |
| 160 | + Default: Follows ``MICROPY_PY_BUILTINS_STR_UNICODE`` setting. |
| 161 | + |
| 162 | +``MICROPY_PY_BUILTINS_BYTES_DECODE_ERRORS`` |
| 163 | + Enable the ``'ignore'`` and ``'replace'`` error handlers for |
| 164 | + :meth:`bytes.decode`. When enabled, invalid UTF-8 bytes can be either |
| 165 | + skipped (``'ignore'``) or replaced with U+FFFD (``'replace'``). |
| 166 | + |
| 167 | + Default: Enabled at ``MICROPY_CONFIG_ROM_LEVEL_EXTRA_FEATURES`` and above. |
| 168 | + |
| 169 | +Example Configuration |
| 170 | +~~~~~~~~~~~~~~~~~~~~~ |
| 171 | + |
| 172 | +For a constrained port with limited flash, disable error handlers:: |
| 173 | + |
| 174 | + #define MICROPY_PY_BUILTINS_BYTES_DECODE_ERRORS (0) |
| 175 | + |
| 176 | +For a port with more resources, enable all Unicode features:: |
| 177 | + |
| 178 | + #define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_EXTRA_FEATURES) |
| 179 | + // This automatically enables: |
| 180 | + // - MICROPY_PY_BUILTINS_STR_UNICODE |
| 181 | + // - MICROPY_PY_BUILTINS_BYTES_DECODE_ERRORS |
| 182 | + |
| 183 | +Limitations |
| 184 | +----------- |
| 185 | + |
| 186 | +MicroPython's Unicode support has some limitations compared to CPython: |
| 187 | + |
| 188 | +- Only UTF-8 and ASCII encodings are supported |
| 189 | +- No support for Unicode normalization |
| 190 | +- No locale-aware string operations |
| 191 | +- The ``errors`` parameter accepts only positional arguments (not keyword arguments) |
| 192 | +- String methods like ``upper()``, ``lower()``, etc. work correctly only for ASCII |
| 193 | +- The MicroPython interactive REPL and ``input()`` function currently have limited |
| 194 | + Unicode support. The line editor is unaware of the displayed width of characters: |
| 195 | + wide characters (for example many CJK characters) take two terminal columns, while a |
| 196 | + grapheme cluster (a base code point plus combining marks, or an emoji sequence) can |
| 197 | + span several code points yet occupy a single column. Because editing tracks code |
| 198 | + points rather than displayed columns, line-editing keys such as backspace and the |
| 199 | + left/right arrows may leave the cursor misaligned with the text shown on screen. |
| 200 | + A workaround is to place the Unicode text in a UTF-8 encoded MicroPython script and |
| 201 | + run it using ``mpremote run <script.py>``. |
0 commit comments