prize18-6 Exercise 3: Truncate Without Splitting a Cluster =========================================================== Plan: split into clusters, keep limit - 1 of them and add the ellipsis when the text is too long (so the result has exactly limit clusters), and return the text unchanged when it already fits. The naive slice s[:limit] counts code points, so it can cut a cluster in two. ex3.py (needs the third-party regex module): import regex def graphemes(s): return regex.findall(r"\X", s) def fits(s, limit): return len(graphemes(s)) <= limit def truncate(s, limit, marker="\u2026"): g = graphemes(s) if len(g) <= limit: return s return "".join(g[:limit - 1]) + marker for s in ["hello world", "cafe\u0301 au lait", "go \U0001F468\u200D\U0001F469\u200D\U0001F467 now"]: t = truncate(s, 6) print(ascii(t), len(graphemes(t)), len(t)) print(ascii("go \U0001F468\u200D\U0001F469\u200D\U0001F467 now"[:6])) Real output (Python 3.14): 'hello\u2026' 6 6 'cafe\u0301 \u2026' 6 7 'go \U0001f468\u200d\U0001f469\u200d\U0001f467 \u2026' 6 10 'go \U0001f468\u200d\U0001f469' Each result has exactly 6 clusters, though 6, 7 and 10 code points: the accent and the family stay whole. The naive slice (last line) cuts the family emoji after two members, so only two of the three remain.