如何在X11库中处理Compound Text编码?
解析X11 COMPOUND_TEXT并转换为UTF-8
COMPOUND_TEXT是X11早期设计的混合字符集编码,通过控制序列切换不同字符集来兼容多语言文本。要将其转换为UTF-8,核心是拆分字符集片段并逐个转码,以下是具体实现思路和代码示例:
核心处理逻辑
- 识别控制序列:COMPOUND_TEXT以
0x1B(ESC)开头的控制序列切换字符集,格式为ESC % <charset-code>,比如ESC % G对应ISO-8859-1,ESC % @对应GB2312。 - 拆分文本片段:按控制序列将整体文本拆分为「字符集标识+对应文本块」的组合。
- 片段转码:针对每个文本块,根据字符集标识用转码工具(如libiconv)转换为UTF-8。
- 拼接结果:将所有转码后的UTF-8片段拼接成最终字符串。
C语言实现示例(依赖libiconv)
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <iconv.h> #include <X11/Xlib.h> #include <X11/Xatom.h> // 解析COMPOUND_TEXT的字符集标识,返回字符集名称和剩余文本指针 const char* parse_ctext_charset(const unsigned char* ctext, const unsigned char** remaining) { *remaining = ctext; if (ctext[0] != 0x1B || ctext[1] != '%') { return "ISO-8859-1"; // 默认字符集 } switch (ctext[2]) { case 'G': return "ISO-8859-1"; case '@': return "GB2312"; case 'A': return "JIS_X0208-1983"; case 'B': return "ISO-2022-JP"; case 'C': return "ISO-2022-KR"; default: return "ISO-8859-1"; } *remaining = ctext + 3; } // 将COMPOUND_TEXT转换为UTF-8字符串 char* ctext_to_utf8(const unsigned char* ctext, size_t ctext_len) { char* utf8_buf = malloc(ctext_len * 4); // 预分配足够的UTF-8空间 if (!utf8_buf) return NULL; char* utf8_ptr = utf8_buf; const unsigned char* ptr = ctext; const unsigned char* end = ctext + ctext_len; while (ptr < end) { const char* charset = parse_ctext_charset(ptr, &ptr); // 定位当前字符集下的文本片段结尾(下一个控制序列或文本结束) const unsigned char* seg_end = ptr; while (seg_end < end && seg_end[0] != 0x1B) seg_end++; size_t seg_len = seg_end - ptr; // 转码当前片段到UTF-8 iconv_t cd = iconv_open("UTF-8", charset); if (cd == (iconv_t)-1) { perror("iconv_open failed"); free(utf8_buf); return NULL; } char* in_buf = (char*)ptr; size_t in_left = seg_len; char* out_buf = utf8_ptr; size_t out_left = (utf8_buf + ctext_len*4) - utf8_ptr; if (iconv(cd, &in_buf, &in_left, &out_buf, &out_left) == (size_t)-1) { // 转码失败时跳过当前片段或替换为占位符 *out_buf++ = '�'; } utf8_ptr = out_buf; iconv_close(cd); ptr = seg_end; } *utf8_ptr = '\0'; return utf8_buf; } // 获取窗口标题,优先处理UTF8_STRING和XA_STRING,最后处理COMPOUND_TEXT char* get_window_title(Display* dpy, Window win) { Atom wm_name = XInternAtom(dpy, "WM_NAME", False); Atom utf8_name = XInternAtom(dpy, "UTF8_STRING", False); Atom actual_type; int format; unsigned long nitems, bytes_after; unsigned char* prop = NULL; // 优先尝试UTF8_STRING if (XGetWindowProperty(dpy, win, utf8_name, 0, 1024, False, utf8_name, &actual_type, &format, &nitems, &bytes_after, &prop) == Success && prop) { char* title = strdup((char*)prop); XFree(prop); return title; } // 处理XA_STRING(ISO-8859-1转UTF-8) if (XGetWindowProperty(dpy, win, wm_name, 0, 1024, False, XA_STRING, &actual_type, &format, &nitems, &bytes_after, &prop) == Success && prop) { iconv_t cd = iconv_open("UTF-8", "ISO-8859-1"); char* utf8_buf = malloc(nitems * 4); char* in_buf = (char*)prop; size_t in_left = nitems; char* out_buf = utf8_buf; size_t out_left = nitems *4; iconv(cd, &in_buf, &in_left, &out_buf, &out_left); *out_buf = '\0'; iconv_close(cd); XFree(prop); return utf8_buf; } // 处理COMPOUND_TEXT Atom compound_text = XInternAtom(dpy, "COMPOUND_TEXT", False); if (XGetWindowProperty(dpy, win, wm_name, 0, 1024, False, compound_text, &actual_type, &format, &nitems, &bytes_after, &prop) == Success && prop) { char* utf8_title = ctext_to_utf8(prop, nitems); XFree(prop); return utf8_title; } return NULL; }
注意事项
- 上述代码只覆盖了常见的字符集控制序列,如需支持更多少见字符集,需参考X11 COMPOUND_TEXT规范补充
parse_ctext_charset函数的映射关系。 - 转码失败时可根据需求选择跳过无效字节或替换为通用占位符
�。 - 实际应用中优先处理
UTF8_STRING和XA_STRING,只有当这两种属性获取失败时再处理COMPOUND_TEXT,以降低处理成本。
内容的提问来源于stack exchange,提问作者Ivan Stepanov
相关产品推荐
相关产品推荐

