1. 从一个字母占几个字节说起俄语字符编码的认知起点很多人第一次接触俄语文本处理都会卡在一个看似简单的问题上一个俄语字母到底占几个字节如果你用len()去量一个俄语字符串可能会得到和预期完全不同的数字。这个困惑的根源就在于字符编码和 Unicode 码点之间的映射关系。俄语使用的是西里尔字母Cyrillic共 33 个字母。这些字母在 Unicode 标准中有明确的码点分配但它们在内存中的实际存储长度取决于你用的编码方案。比如在 UTF-8 下大部分常用俄语字母占 2 个字节而在 UTF-16 下占 2 个字节在 UTF-32 下占 4 个字节。如果你用单字节编码比如某些遗留的 Windows-1251那每个字母就只占 1 个字节但代价是和其他语言混排时极易乱码。这篇文章想做的事情很具体把俄语字母和 Unicode 码点之间的对应关系彻底理清楚并且给出可以直接在代码里用的映射表、转换方法和排查技巧。不管你是做多语言文本处理、国际化测试、还是单纯在爬取俄语网页时遇到了乱码这些内容都能直接派上用场。我会从码点分布规律讲起然后给出完整的对照表再深入到实际编码转换中的坑最后分享几个我在处理俄语文本时踩过的真实教训。2. 西里尔字母在 Unicode 中的码点分布规律2.1 基础俄语字母集中在 U0410 到 U044FUnicode 对西里尔字母的分配是有明显规律的。最基础的 32 个俄语字母不包括 ё集中在一个连续的区间里大写字母U0410 到 U042F对应 А 到 Я小写字母U0430 到 U044F对应 а 到 я这个区间的排列顺序和字母表顺序完全一致。也就是说А 是 U0410Б 是 U0411В 是 U0412以此类推。大写和小写之间刚好差了 0x2032这和拉丁字母的大小写差值是一样的。这个规律非常有用——你可以通过简单的加减法在大写和小写之间转换而不需要查表。但这里有个例外需要特别注意ё 这个字母。它不在上述连续区间内大写 Ё 是 U0401小写 ё 是 U0451。这两个码点位于基础区间之外和拉丁字母中的某些扩展字符混在一起。很多排序算法和大小写转换逻辑如果只处理了 U0410 到 U044F 这个区间就会漏掉 ё导致排序结果不符合俄语习惯。2.2 扩展西里尔字母与历史遗留字符除了基础俄语字母Unicode 还包含了大量扩展西里尔字符分布在多个区块中区块范围名称主要用途U0400–U04FFCyrillic基础俄语及部分斯拉夫语言字母U0500–U052FCyrillic Supplement扩展西里尔字母U2DE0–U2DFFCyrillic Extended-A中世纪西里尔字符UA640–UA69FCyrillic Extended-B旧式西里尔字母U1C80–U1C8FCyrillic Extended-C历史变体对于日常俄语处理来说你主要关注 U0400–U04FF 这个区块就够了。但如果你在处理历史文献、教会斯拉夫语文本或者某些少数民族语言如哈萨克语、蒙古语用的西里尔字母就需要扩展到其他区块。我遇到过一种情况某批文档里混入了 U0500 区间的字符用常规的正则表达式[А-Яа-я]匹配时完全漏掉了这些字符导致后续处理出现空白。2.3 码点、UTF-8 字节序列与 UTF-16 代理对的关系理解这三者的关系是避免乱码的关键。以字母 АU0410为例Unicode 码点U0410十进制 1040UTF-8 编码0xD0 0x90两个字节UTF-16 编码0x0410一个码元两个字节UTF-32 编码0x00000410四个字节再看一个稍微复杂的例子字母 ёU0451Unicode 码点U0451十进制 1105UTF-8 编码0xD1 0x91两个字节UTF-16 编码0x0451一个码元UTF-32 编码0x00000451这里的关键点是UTF-8 是变长编码俄语字母固定占 2 个字节因为它们的码点都在 U0080 到 U07FF 范围内。这个范围内的字符UTF-8 用两个字节表示第一个字节的高位是110第二个字节的高位是10。你可以用这个规律快速判断一个字节序列是不是俄语字母。注意不要用len()函数的返回值来判断字符数量。在 Python 3 中len(Привет)返回 6因为 Python 3 的字符串是 Unicode 码点序列。但在 Python 2 中同样的字符串用 UTF-8 编码后len()返回 12。这个差异是很多历史代码出问题的根源。3. 完整对照表与快速查询方法3.1 俄语 33 个字母的 Unicode 码点全表下面这张表是我在实际工作中反复使用的包含了俄语全部 33 个字母包括 ё的大写、小写、Unicode 码点和 UTF-8 字节序列。建议直接收藏或存成 CSV 备用。序号大写小写大写码点小写码点大写 UTF-8小写 UTF-81АаU0410U0430D0 90D0 B02БбU0411U0431D0 91D0 B13ВвU0412U0432D0 92D0 B24ГгU0413U0433D0 93D0 B35ДдU0414U0434D0 94D0 B46ЕеU0415U0435D0 95D0 B57ЁёU0401U0451D0 81D1 918ЖжU0416U0436D0 96D0 B69ЗзU0417U0437D0 97D0 B710ИиU0418U0438D0 98D0 B811ЙйU0419U0439D0 99D0 B912КкU041AU043AD0 9AD0 BA13ЛлU041BU043BD0 9BD0 BB14МмU041CU043CD0 9CD0 BC15НнU041DU043DD0 9DD0 BD16ОоU041EU043ED0 9ED0 BE17ПпU041FU043FD0 9FD0 BF18РрU0420U0440D0 A0D1 8019СсU0421U0441D0 A1D1 8120ТтU0422U0442D0 A2D1 8221УуU0423U0443D0 A3D1 8322ФфU0424U0444D0 A4D1 8423ХхU0425U0445D0 A5D1 8524ЦцU0426U0446D0 A6D1 8625ЧчU0427U0447D0 A7D1 8726ШшU0428U0448D0 A8D1 8827ЩщU0429U0449D0 A9D1 8928ЪъU042AU044AD0 AAD1 8A29ЫыU042BU044BD0 ABD1 8B30ЬьU042CU044CD0 ACD1 8C31ЭэU042DU044DD0 ADD1 8D32ЮюU042EU044ED0 AED1 8E33ЯяU042FU044FD0 AFD1 8F从这张表里可以观察到一个明显的规律大写字母的 UTF-8 第一个字节都是D0第二个字节从90到AF除了 Ё 是D0 81。小写字母则分两类а 到 п 是D0 B0到D0 BFр 到 я 是D1 80到D1 8F。这个规律在手动排查乱码时非常管用。3.2 用代码生成映射字典手动查表效率低我通常会在项目里直接生成一个映射字典。下面这段 Python 代码可以生成完整的俄语字母到码点的映射def build_russian_unicode_map(): 生成俄语字母到 Unicode 码点的映射字典 mapping {} # 基础区间А-Я 和 а-я不含 Ё/ё for i in range(32): upper_code 0x0410 i lower_code 0x0430 i upper_char chr(upper_code) lower_char chr(lower_code) mapping[upper_char] upper_code mapping[lower_char] lower_code # 单独处理 Ё 和 ё mapping[Ё] 0x0401 mapping[ё] 0x0451 return mapping # 使用示例 ru_map build_russian_unicode_map() print(fА - U{ru_map[А]:04X}) # А - U0410 print(fё - U{ru_map[ё]:04X}) # ё - U0451 print(f共 {len(ru_map)} 个字母) # 共 33 个字母这段代码的逻辑很直接先处理连续区间再单独补上 ё。如果你需要反向映射码点到字母把字典的键值对调即可。在实际项目中我建议把这个映射存成 JSON 文件方便不同语言的服务共享。3.3 反向查询从码点还原字母有时候你拿到的是一个码点或字节序列需要反查它对应哪个字母。下面这个函数可以处理多种输入格式def code_to_russian_letter(code): 从码点或 UTF-8 字节序列还原俄语字母 if isinstance(code, int): # 直接是码点 return chr(code) elif isinstance(code, bytes): # 是 UTF-8 字节序列 return code.decode(utf-8) elif isinstance(code, str): # 是十六进制字符串如 0410 或 U0410 code code.replace(U, ).replace(u, ) return chr(int(code, 16)) else: raise ValueError(f不支持的输入类型: {type(code)}) # 测试 print(code_to_russian_letter(0x0410)) # А print(code_to_russian_letter(b\xd0\x90)) # А print(code_to_russian_letter(0410)) # А print(code_to_russian_letter(U0451)) # ё这个函数覆盖了最常见的三种输入场景整数码点、字节序列、十六进制字符串。我在处理日志文件时经常遇到十六进制字符串格式的码点用这个函数可以快速还原。4. 编码转换中的典型陷阱与排查链路4.1 乱码的三种典型表现与根因定位俄语文本乱码通常表现为三种形式每种形式对应不同的根因第一种问号或方块。文本显示为?????或。这通常意味着目标系统不支持西里尔字符或者字体缺失。根因是编码转换时用了不支持俄语的字符集比如把 UTF-8 文本强行用 ASCII 解码。第二种拉丁字母混杂。文本显示为Привет这样的形式。这是典型的 UTF-8 被误读为 Latin-1 的结果。每个俄语字母的 UTF-8 字节序列被逐个当作 Latin-1 字符解释就变成了这种双字母的乱码。第三种完全无关的字符。文本显示为Привет变成了Ïðèâåò这样的形式。这通常是 Windows-1251 和 Latin-1 之间的误转换。Windows-1251 是俄语的传统单字节编码它的码位分配和 Latin-1 完全不同。排查的第一步是确认原始编码。如果你能拿到原始字节序列用chardet或charset-normalizer这类库可以自动检测。但自动检测不是万能的我遇到过检测置信度只有 0.6 的情况最后还是靠人工比对码点才确定。4.2 用 Python 复现并修复一次真实乱码假设你从某个接口拿到一段文本显示为Привет你想还原它。下面是完整的排查和修复过程# 第一步观察乱码特征 garbled Привет print(f乱码长度: {len(garbled)}) # 12 # 第二步尝试用 Latin-1 编码回字节 raw_bytes garbled.encode(latin-1) print(f字节序列: {raw_bytes.hex()}) # 输出: d09fd180d0b8d0b2d0b5d182 # 第三步用 UTF-8 解码 fixed raw_bytes.decode(utf-8) print(f修复结果: {fixed}) # Привет这个过程的逻辑是乱码产生的路径是UTF-8 字节 → 被当作 Latin-1 解码 → 显示为乱码。修复就是反向操作乱码 → 用 Latin-1 编码回字节 → 用 UTF-8 解码。但这里有个前提原始字节序列必须完整保留。如果中间经过了有损转换比如某些字符被替换成了问号那就无法完全还原了。这也是为什么我在处理多语言文本时坚持在管道中全程使用 UTF-8绝不在中间环节做编码转换。4.3 Windows-1251 与 UTF-8 的互转陷阱Windows-1251 是俄语环境中历史遗留的单字节编码至今仍能在一些旧系统和文件中遇到。它和 UTF-8 之间的转换有一个容易忽略的坑Windows-1251 中有些码位对应的是标点符号和特殊字符而不是字母。比如字节0xA0在 Windows-1251 中是不断行空格NBSP在 Latin-1 中也是 NBSP但在 UTF-8 中0xA0是一个无效的起始字节。如果你用bytes.decode(cp1251)去解码一段实际上是 UTF-8 的文本会得到一堆看似合理但完全错误的字符。# 错误示范用 cp1251 解码 UTF-8 文本 utf8_text Привет wrong utf8_text.encode(utf-8).decode(cp1251) print(wrong) # Привет # 正确做法明确指定编码 correct utf8_text.encode(utf-8).decode(utf-8) print(correct) # Привет我的经验是永远不要依赖默认编码。在 Python 中open()函数不指定encoding参数时会使用系统默认编码这在跨平台时是灾难性的。我现在的习惯是所有文件读写都显式写encodingutf-8所有网络请求都显式设置response.encoding utf-8。5. 实际项目中的处理策略与性能考量5.1 文本清洗时如何正确识别俄语字符在做文本清洗时经常需要判断一个字符是不是俄语字母。最直接的方法是用 Unicode 码点范围判断def is_russian_letter(char): 判断字符是否为俄语字母 code ord(char) # 基础区间 if 0x0410 code 0x044F: return True # Ё 和 ё if code in (0x0401, 0x0451): return True return False # 测试 print(is_russian_letter(А)) # True print(is_russian_letter(ё)) # True print(is_russian_letter(A)) # False print(is_russian_letter(1)) # False但这个方法有个局限它只覆盖了基础俄语字母。如果你需要包含扩展西里尔字符需要扩大范围。更通用的做法是用unicodedata模块查询字符的名称import unicodedata def is_cyrillic(char): 判断字符是否属于西里尔字母 try: name unicodedata.name(char) return CYRILLIC in name except ValueError: return False print(is_cyrillic(А)) # True print(is_cyrillic(Ә)) # True哈萨克语用的西里尔字母 print(is_cyrillic(A)) # False用unicodedata.name()的好处是覆盖面广缺点是性能比直接比较码点慢。在需要处理大量文本时我通常先用码点范围做快速筛选对边界情况再用unicodedata确认。5.2 大小写转换与排序的俄语特殊性俄语的大小写转换有一个容易出错的地方ё 的大小写转换。Python 的str.upper()和str.lower()对 ё 的处理是正确的print(ё.upper()) # Ё print(Ё.lower()) # ё但如果你自己实现了大小写转换逻辑只处理了 U0410–U044F 区间就会漏掉 ё。我在一个排序功能里就踩过这个坑排序结果中 ё 被排到了错误的位置因为排序键没有正确处理 ё 的码点。俄语字母表的顺序是А Б В Г Д Е Ё Ж З И Й К Л М Н О П Р С Т У Ф Х Ц Ч Ш Щ Ъ Ы Ь Э Ю Я。注意 ё 排在 е 之后、ж 之前。但 Unicode 码点中ё 是 U0451比 я 的 U044F 还大。如果直接按码点排序ё 会被排到最后这不符合俄语习惯。正确的做法是自定义排序键# 俄语字母表顺序 russian_alphabet АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ order_map {char: i for i, char in enumerate(russian_alphabet)} order_map.update({char.lower(): i for i, char in enumerate(russian_alphabet)}) def russian_sort_key(text): 按俄语字母表顺序生成排序键 return [order_map.get(char, 999) for char in text] words [ёж, ель, жук, азбука] sorted_words sorted(words, keyrussian_sort_key) print(sorted_words) # [азбука, ель, ёж, жук]这个排序键的逻辑是把每个字符映射到它在俄语字母表中的位置然后按位置序列排序。这样 ё 就会正确地排在 е 和 ж 之间。5.3 处理大量俄语文本时的内存与速度优化当处理大量俄语文本时有几个性能优化点值得注意第一避免频繁的编码转换。每次encode()和decode()都有开销。如果一段文本需要在多个处理步骤中传递尽量保持统一的编码格式只在输入和输出时做转换。第二用字节级操作替代字符级操作。在某些场景下直接操作 UTF-8 字节序列比操作 Unicode 字符串更快。比如判断一个字节序列是否全是俄语字母可以用字节范围判断def is_russian_bytes(data): 判断字节序列是否全由俄语字母组成UTF-8 i 0 while i len(data): b data[i] if b 0xD0: if i 1 len(data) and (0x90 data[i1] 0xBF): i 2 continue elif b 0xD1: if i 1 len(data) and (0x80 data[i1] 0x8F): i 2 continue return False return True这个方法比先解码再逐字符判断要快因为它避免了创建 Unicode 字符串的开销。但它的可读性较差只在性能敏感的场景下推荐使用。第三用str.translate()做批量替换。如果你需要把俄语字母映射到其他字符比如转写为拉丁字母str.translate()比循环替换快得多# 俄语到拉丁字母的简易转写表 translit_map str.maketrans( АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстуфхцчшщъыьэюя, ABVGDEEJZIJKLMNOPRSTUFHTSCHSHSHCH_Y_EYUAabvgdeejzijklmnoprstufhtschshshch_y_eyua ) text Привет, мир! transliterated text.translate(translit_map) print(transliterated) # Privet, mir!这个转写表是简化版实际项目中可能需要更精确的转写规则比如 ГОСТ 7.79-2000 标准。但str.translate()的性能优势是明显的它底层用 C 实现比 Python 循环快一个数量级。6. 几个我踩过的坑和对应的解决方案6.1 数据库排序规则导致的俄语排序错误有一次在一个项目里俄语单词的排序结果始终不对。排查后发现是数据库的排序规则collation设置成了utf8_general_ci这个规则对西里尔字母的排序是按码点来的没有考虑俄语字母表的特殊顺序。改成utf8_unicode_ci或专门的俄语排序规则后问题解决。这个坑的教训是数据库层面的排序规则和应用程序层面的排序逻辑要一致。如果数据库排序规则不支持俄语的特殊顺序要么在应用层做排序要么换用支持俄语的排序规则。6.2 正则表达式中\w对俄语字母的匹配行为Python 的re模块中\w默认匹配 Unicode 字母包括俄语字母。但在某些其他语言的正则引擎中\w只匹配 ASCII 字母。如果你写了一个正则\w来提取单词在 Python 中能匹配俄语在 JavaScript 中默认不能需要加u标志。import re text Привет мир # Python 中 \w 默认匹配 Unicode 字母 print(re.findall(r\w, text)) # [Привет, мир] # 如果只想匹配俄语字母用明确的字符类 print(re.findall(r[А-Яа-яЁё], text)) # [Привет, мир]我的建议是不要依赖\w的默认行为而是用明确的字符类。这样代码在不同语言和不同配置下行为一致减少意外。6.3 前端显示时的字体回退问题即使编码完全正确前端显示俄语时也可能出问题。最常见的是字体回退如果页面指定的字体不包含西里尔字形浏览器会回退到系统字体。如果系统字体也没有就会显示方块。解决方案是在 CSS 的font-family中明确指定包含西里尔字母的字体body { font-family: Noto Sans, PT Sans, Arial, sans-serif; }Noto Sans和PT Sans都包含完整的西里尔字母集。Arial作为最后的回退在大多数系统上也有西里尔字形。这个顺序确保了即使前面的字体加载失败也有可用的回退。6.4 文件 BOM 导致的解析异常UTF-8 文件可以带 BOM字节顺序标记也可以不带。带 BOM 的文件开头会有EF BB BF三个字节。如果你用utf-8编码读取带 BOM 的文件BOM 会被当作一个特殊字符UFEFF保留在字符串开头可能导致后续处理出错。# 读取带 BOM 的文件 with open(russian.txt, r, encodingutf-8) as f: content f.read() print(repr(content[:10])) # \ufeffПривет... # 正确做法用 utf-8-sig 编码自动处理 BOM with open(russian.txt, r, encodingutf-8-sig) as f: content f.read() print(repr(content[:10])) # Привет...utf-8-sig编码在读取时会自动去掉 BOM在写入时会自动添加 BOM。对于需要跨平台交换的文本文件我通常建议用utf-8-sig因为 Windows 上的某些工具如旧版记事本需要 BOM 才能正确识别 UTF-8。7. 把这些知识固化成可复用的工具函数7.1 一个完整的俄语文本处理工具类把前面散落的逻辑整合成一个工具类方便在项目中直接调用import unicodedata class RussianTextUtils: 俄语文本处理工具类 # 俄语字母表顺序 ALPHABET АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ # 码点范围 UPPER_RANGE (0x0410, 0x042F) LOWER_RANGE (0x0430, 0x044F) YO_UPPER 0x0401 YO_LOWER 0x0451 classmethod def is_russian_letter(cls, char): 判断是否为俄语字母 code ord(char) if cls.UPPER_RANGE[0] code cls.UPPER_RANGE[1]: return True if cls.LOWER_RANGE[0] code cls.LOWER_RANGE[1]: return True if code in (cls.YO_UPPER, cls.YO_LOWER): return True return False classmethod def to_unicode_escape(cls, text): 将俄语文本转为 Unicode 转义序列 return .join( f\\u{ord(c):04x} if cls.is_russian_letter(c) else c for c in text ) classmethod def sort_key(cls, text): 生成俄语字母表排序键 order {c: i for i, c in enumerate(cls.ALPHABET)} order.update({c.lower(): i for i, c in enumerate(cls.ALPHABET)}) return [order.get(c, 999) for c in text] classmethod def extract_russian(cls, text): 提取文本中的所有俄语字母 return .join(c for c in text if cls.is_russian_letter(c)) # 使用示例 utils RussianTextUtils() print(utils.to_unicode_escape(Привет)) # \u041f\u0440\u0438\u0432\u0435\u0442 print(utils.extract_russian(Hello Привет 123)) # Привет print(sorted([ёж, ель, азбука], keyutils.sort_key)) # [азбука, ель, ёж]这个工具类封装了判断、转义、排序、提取四个最常用的功能。你可以根据项目需要继续扩展比如加上转写、大小写转换、编码检测等方法。7.2 批量验证文本编码的脚本在处理未知来源的俄语文本时我通常会先跑一个验证脚本确认编码是否正确def validate_russian_encoding(file_path): 验证文件中的俄语文本编码 results [] # 尝试多种编码 encodings [utf-8, utf-8-sig, cp1251, koi8-r, iso-8859-5] for enc in encodings: try: with open(file_path, r, encodingenc) as f: content f.read(1000) # 只读前1000字符 # 统计俄语字母比例 russian_count sum(1 for c in content if RussianTextUtils.is_russian_letter(c)) total_letters sum(1 for c in content if c.isalpha()) if total_letters 0: ratio russian_count / total_letters results.append((enc, ratio, content[:50])) except (UnicodeDecodeError, LookupError): continue # 按俄语字母比例排序 results.sort(keylambda x: x[1], reverseTrue) for enc, ratio, sample in results: print(f编码: {enc}, 俄语字母比例: {ratio:.2%}, 样本: {sample[:30]}...) return results[0][0] if results else None # 使用 best_encoding validate_russian_encoding(unknown_russian.txt) print(f推荐编码: {best_encoding})这个脚本的逻辑是用多种编码尝试读取文件统计每种编码下俄语字母占所有字母的比例。比例最高的编码最可能是正确的。这个方法比单纯的chardet检测更可靠因为它利用了俄语文本中字母占比高的特点。7.3 在 CI 中自动检查编码问题如果你的项目涉及多语言文本可以在 CI 流程中加入编码检查防止编码问题流入生产环境import os import sys def check_project_encoding(root_dir): 检查项目中所有文本文件的编码 issues [] for dirpath, _, filenames in os.walk(root_dir): for filename in filenames: if not filename.endswith((.txt, .csv, .json, .md)): continue filepath os.path.join(dirpath, filename) try: with open(filepath, r, encodingutf-8) as f: content f.read() # 检查是否有替换字符UFFFD表示解码时丢失了数据 if \ufffd in content: issues.append(f{filepath}: 包含替换字符可能有编码问题) # 检查是否有 BOM 但用了 utf-8 而非 utf-8-sig if content.startswith(\ufeff): issues.append(f{filepath}: 包含 BOM建议用 utf-8-sig 读取) except UnicodeDecodeError as e: issues.append(f{filepath}: UTF-8 解码失败 - {e}) if issues: print(发现编码问题) for issue in issues: print(f - {issue}) sys.exit(1) else: print(编码检查通过) # 在 CI 中调用 check_project_encoding(./data)这个检查脚本会遍历项目中的文本文件检查是否有替换字符、BOM 问题或解码失败。把它加入 CI 流程可以在代码合并前就发现编码问题避免上线后才发现乱码。8. 关于俄语 Unicode 处理的一些个人体会处理俄语文本这些年最大的体会是编码问题从来不是单纯的技术问题而是流程问题。大多数乱码不是因为某个函数用错了而是因为数据在多个系统之间流转时某一环没有明确指定编码。我的做法是在项目规范里强制要求所有文本文件必须用 UTF-8 编码所有网络请求必须显式设置字符集所有数据库连接必须指定字符集。这三条规则执行下来编码相关的 bug 减少了九成以上。另一个体会是不要相信自动检测。chardet这类工具在文本长度足够时准确率不错但对于短文本比如一个单词或一个句子误判率很高。我遇到过chardet把一段俄语文本检测成保加利亚语的情况因为两种语言都用西里尔字母统计特征很接近。最终的解决方案还是靠明确的编码声明而不是自动检测。最后分享一个实用技巧如果你需要快速查看一个字符的 Unicode 码点在 Python 交互环境里用hex(ord(А))就能得到0x410。反过来chr(0x410)就能得到А。这两个函数我在排查编码问题时用得最多比任何工具都直接。