Erlang/OTP 字符集与源文件编码完整指南:Latin-1 与 UTF-8 的底层规则与实践
编程语言语言运行时标准库编译器并发编程【免费下载链接】otpErlang/OTP项目地址https://gitcode.com/gh_mirrors/ot/otp点击查看免费下载导读在 Erlang/OTP 中源文件采用何种字符集、以什么编码保存直接决定了字符串字面量、注释、原子与函数名中能否出现非 ASCII 字符也决定了这些字符在编译与运行时的行为。本文以 Erlang/OTP 官方参考手册中《Character Set and Source File Encoding》一节为骨架系统讲解 Erlang 词法层面对 ISO-8859-1Latin-1与 Unicode 字符的支持边界、源文件coding注释的解析规则与默认值并深入到 epp.erl 的源码实现与 epp_SUITE.erl 的测试用例帮助你彻底掌握编码声明写在哪里、何时生效、哪些字符能写、哪些不能写这一套完整规则。字符集Character SetErlang 的 token 语法允许使用完整的 ISO-8859-1Latin-1字符集。这一点在词法层面有两大直接体现所有 Latin-1 可打印字符都可以直接使用并且在代码中直接显示无需借助反斜杠转义约定如\x{...}不带引号的原子atom和变量variable可以使用全部 Latin-1 字母例如变量名När、原子här、öre都是合法的 token。Latin-1 字符分类表下表来自参考手册按八进制与十进制范围给出 Latin-1 字符在 Erlang 词法中的分类。这张表对理解哪个范围属于字母、哪个范围属于标点非常关键因为字母决定能否出现在未加引号的原子和变量名中八进制十进制示例字符类别200 - 237128 - 159控制字符Control characters240 - 277160 - 191- ¿标点字符Punctuation characters300 - 326192 - 214À - Ö大写字母Uppercase letters327215×标点字符Punctuation character330 - 336216 - 222Ø - Þ大写字母Uppercase letters337 - 366223 - 246ß - ö小写字母Lowercase letters367247÷标点字符Punctuation character370 - 377248 - 255ø - ÿ小写字母Lowercase letters表Latin-1 字符类别可以看到字母区间集中在192-214、216-222大写与223-246、248-255小写而215×与247÷被单独归类为标点——这意味着它们不能作为未引号原子的组成部分。仓库中的词法测试 erl_scan_SUITE.erliso88591/1用例就专门用$Á $á $é $ë、$N $ä $r等 Latin-1 字母构造变量名与原子名如här、öre进行解析与回显打印验证印证了这一分类在实际扫描器中的行为。超出 Latin-1 范围的 Unicode 字符哪些 token 允许在 Latin-1 范围之外的 Unicode 字符仅允许出现在以下 token 中字符串字面量String literals例如√π字符字面量Character literals例如$∑代码中的注释Comments带引号的原子Quoted atoms例如μs函数名Function names例如s_to_μs(S) - S * 1_000_000.其中值得注意的边界是原子用作模块名module names、应用名application names和节点名node names时仍被限制在 Latin-1 范围内。也就是说即使μs这样的带引号原子在词法上合法它也不适合作为模块名或节点名使用这一点在跨节点通信、应用发布等场景下需要特别留意。变更记录Erlang/OTP R16B起字符串字面量、字符字面量和注释开始支持 UnicodeErlang/OTP 20起原子与函数名开始支持 Unicode。因此如果你的代码依赖 Unicode 原子或函数名请确保运行环境不低于 OTP 20。扫描器层面的实现佐证在源码层面词法扫描器 erl_scan.erl 通过scan_comment/6等函数对注释内容逐字符进行 Unicode 合法性检查注释中的每个字符都必须满足?CHAR(C)且?UNICODE(C)为真否则会抛出{illegal, character}扫描错误。这正是注释可以包含任意 Unicode 字符这一规则在实现中的落地方式也解释了为什么在注释里写一个超出合法范围的字节会直接导致编译失败。源文件编码Source File Encoding编码声明规则Erlang 源文件的编码encoding由源文件前两行中的某条注释选定。具体规则如下在源文件的前两行中第一个匹配正则表达式coding\s*[:]\s*([-a-zA-Z0-9])的字符串决定编码如果匹配到的字符串不是合法的编码名称则被忽略合法的编码只有Latin-1和UTF-8两种且字符大小写可以任意选择latin-1、Latin-1、LATIN-1、utf-8、UTF-8等写法均有效当源文件中不存在合法的coding注释时默认编码为 UTF-8。编码声明示例下面两个例子都选择 Latin-1 作为源文件编码%% For this file we have chosen encoding Latin-1%% -*- coding: latin-1 -*-第二种-*- ... -*-写法沿用了 Emacs 的文件局部变量约定因此在 Emacs 中编辑该文件时也会被自动识别。两种写法等价与:分隔符均可例如coding utf-8、coding: UTF-8同样有效。变更记录Erlang/OTP 17.0起Erlang 源文件的默认编码从 Latin-1 改为 UTF-8。这对老项目有实际影响如果一个源文件既没有coding声明、又以 Latin-1 保存那么在 OTP 17 上会被当作 UTF-8 读取文件中的高位字节字符可能被解析失败。给存量代码文件补上%% -*- coding: latin-1 -*-声明是最稳妥的迁移方式。编码解析的源码实现编码声明的前两行限制、正则匹配语义与非法即忽略的行为都在标准库 epp.erl 中落地实现默认编码-define(DEFAULT_ENCODING, utf8).与default_encoding() - ?DEFAULT_ENCODING.见 epp.erl 及 L715-L720即无声明默认 UTF-8识别算法read_encoding_from_binary/1,2与read_encoding_from_file/3通过com/4、com_c/4、com_oding/3、com_sep/3等一组状态机函数逐字节扫描恰好匹配coding后跟:或、再跟空白与编码名编码名判定com_encoding/1中只有[latin,1|_]返回latin1、[utf,8|_]返回utf8其余一律throw(no)视为无有效编码见 epp.erl前两行限制com_nl/4在换行计数达到 2 时即停止扫描且该扫描器每次最多只读取前 16 个 32 字节块共 512 字节作为探测窗口注释限定默认要求coding字符串出现在注释中in_comment_only选项默认true这正是编码由注释选定的语义来源。公开 APIepp 模块提供的编码工具epp.erl 围绕编码暴露了一组稳定的公共 API自 OTP R16B 起可用在工具链与构建脚本中非常实用epp:default_encoding/0返回当前默认编码utf8epp:encoding_to_string/1将编码转为可识别的声明字符串latin1 - coding: latin-1utf8 - coding: utf-8epp:read_encoding/1,2从文件读取编码返回latin1 | utf8 | noneepp:read_encoding_from_binary/1,2从二进制中探测编码支持{in_comment_only, boolean()}选项置为false时不再要求出现在注释内epp:set_encoding/1,2按文件中的编码声明设置 I/O 设备的编码选项无声明时使用传入的默认值。测试用例对规则的验证epp_SUITE.erl 中的otp_10302/1用例对应 OTP-10302Unicode 字符扫描/解析缺陷修复用几十个断言逐一锁定了编码声明的全部细节是理解规则边界的最佳素材utf8 encoding(coding: UTF-8, File)与latin1 encoding(coding: Latin-1, File)——大小写不敏感utf8 encoding(ccoding: UTF-8, File)—— 只要首次匹配命中前缀字符串不影响结果com_c遇到不匹配字符会跳过继续扫描utf8 encoding(coding utf-8, File)——分隔符与空白可灵活变化none encoding(coding: utf-16 coding: utf-8, File)——第一个匹配项非法utf-16时整体忽略不会继续找第二个none encoding(Coding: utf-8, File)—— 以大写C开头不匹配因为com_c精确匹配小写codingnone encoding_com(coding: utf-8, File)无%%前缀与none encoding_nocom(\n\n coding: utf-8, File)第三行才出现——必须位于前两行的注释中utf8 encoding(-*- coding: utf-8 -*-, File)—— Emacs 风格声明同样被识别none encoding(coding: \nutf-8, File)—— 编码名与coding之间不能换行。同时prefix/3帮助函数会对字符串前追加 0 到 99 个空格逐一验证证明只要声明还在前两行内任意前缀空白都不影响识别。测试还验证了epp:encoding_to_string/1与epp:default_encoding/0的行为。实践建议新项目一律采用 UTF-8OTP 17 之后默认编码即 UTF-8无需任何声明建议在文件首行或次行显式写上%% -*- coding: utf-8 -*-以提升可读性并兼容老工具链。存量 Latin-1 文件务必补声明若文件以 Latin-1 保存且含高位字符必须在前两行加入%% -*- coding: latin-1 -*-否则 OTP 17 会按 UTF-8 解析导致编译错误或字符错乱。模块名/应用名/节点名保持 Latin-1 或纯 ASCII即使 OTP 20 后原子与函数名支持 Unicode模块、应用与节点名称仍受 Latin-1 限制跨节点通信场景下应规避非 ASCII 名称。借助eppAPI 做编码探测构建脚本或代码生成器可用epp:read_encoding_from_binary/1在内存中快速判断一段源码的编码避免文件读写与编码不一致问题。总结Erlang 源文件编码机制由词法字符集边界与coding注释声明两部分组成Latin-1 字符可自由用于标识符与各类字面量Latin-1 之外的 Unicode 字符仅限字符串、字符字面量、注释、带引号原子与函数名源文件编码则由前两行注释中首个合法的coding: latin-1或coding: utf-8决定缺省为 UTF-8。从 epp.erl 的状态机解析到 epp_SUITE.erl 的全面断言再到 erl_scan.erl 的注释字符校验整套规则在标准库中都有明确而完备的实现与测试支撑。理解并善用这套规则可以避免大量跨编码环境下的诡异编译错误写出真正健壮、可移植的 Erlang 代码。赞分享编程语言语言运行时标准库编译器并发编程【免费下载链接】otpErlang/OTP项目地址https://gitcode.com/gh_mirrors/ot/otp点击查看免费下载相关推荐跨设备书架同步设置入门换设备阅读如何接着上次读跨设备书架同步设置入门换设备阅读如何接着上次读 boo/books 是 GitHub 推荐项目精选中的一个电子书仓库除了集中存放大量技术类 PDF 书稿还教程炉石传说HsMod插件55项功能全面解析与高效使用指南炉石传说HsMod插件55项功能全面解析与高效使用指南 HsMod是基于BepInEx框架开发的炉石传说功能增强插件为玩家提供了超过55项实用功能从游戏速游戏开发Front-End-Checklist 中的 charset 规则确保页面声明 UTF-8 字符编码的完整指南Front End Checklist 中的 charset 规则确保页面声明 UTF 8 字符编码的完整指南 在 Front End Checklist 这上一篇FunASR 训练与微调实战指南从数据集构建、Smoke Test 到检查点恢复与模型导出下一篇使用 Oracle AI Vector Search 构建 Haystack RAGOracleDocumentStore 与检索器实战指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联
返回资讯列表 →