第19章 Encoding类

建立bytes、String encoding标签、source magic comment、Regexp兼容、IO external/internal encoding与严格transcoding的完整边界。

学习目标

  • 能解释Ruby的编码与字符串与脚本编码与魔法注释在“第19章 Encoding类”中的责任边界
  • 能围绕“怎样从原始字节、编码标签、有效性与转码错误定位乱码根因?”运行正常与故障轨迹并定位首个分岔
  • 能用“第19章 Encoding类的输入样本、接收者与方法、关键状态前后值、正常与失败输出、异常或退出状态,以及复位后的再次运行记录。”证明“来源编码有可信元数据,转换严格,任何替换性数据损失都有记录。”

来源、版次与运行边界

“第19章 Encoding类”以作者维护的第 5 版支持页核定 2016 年 3 月 12 日首刷、两位作者、松本行弘监修以及四部分 23 章目录;逐章程序清单练习答案勘误只作为公开支持材料,不被冒充为原书全文。

对“第19章 Encoding类”而言,中文解释、示例、交互、练习和答案均为独立教学重写;站内中文章名是与官方 23 章顺序对应的课程映射,不宣称是日文小节的逐字翻译,也不从公开程序清单复制整段实现。

“第19章 Encoding类”固定在 Ruby 2.3 语境;Ruby 2.3.0 官方文档稳定版发布说明用于核对当时可用的语言和标准库行为。现代 Ruby 的差异只能另列迁移说明,不能静默改变本页示例的版本结论。

围绕“怎样从原始字节、编码标签、有效性与转码错误定位乱码根因?”,本页验收“第19章 Encoding类的输入样本、接收者与方法、关键状态前后值、正常与失败输出、异常或退出状态,以及复位后的再次运行记录。”。先预测正常轨迹,再只注入“对未知字节直接 force_encoding 为 UTF-8 并把标签变化当作转码成功”;若无法定位第一处状态分岔,就拒绝当前解释。

从“乱码就force_encoding UTF-8”开始纠正

force_encoding只改变String的Encoding标签,不改变任何byte;标签错了只会把问题延后。要求source metadata来自protocol、BOM或可信配置,而不是肉眼猜。

先预测:Windows-31J bytes直接force UTF-8后valid_encoding?可能false;即使偶然true,字符也可能错。正确流程先标记实际source encoding,验证,再encode("UTF-8")

Ruby的编码与字符串

String包含byte sequence与Encoding object。bytesize是bytes,length按当前encoding解释characters;ASCII-only String在许多编码间兼容,但不能据此推断source。是基础。

text = "Ruby中文"
 
p encoding: text.encoding
p valid: text.valid_encoding?
p bytes: text.bytesize
p characters: text.length

Encoding不是语言检测;同一bytes在多个single-byte encodings都可能valid。Unicode normalization NFC/NFD也不是encoding conversion:它改变codepoint composition,搜索/identity是否normalize是独立domain policy。

脚本编码与魔法注释

Ruby parser必须知道source file encoding,modern Ruby默认UTF-8;magic comment如# encoding: Windows-31J在文件开头声明。不能与IO encoding混淆。

团队应统一UTF-8 source并由editor/CI验证。Magic comment不是把文件转码;声明必须与实际bytes一致。Shebang与magic comment位置有规则,目标Ruby 2.3需验证。

__ENCODING__可观察当前source encoding。不同source files各自解析literals,运行时连接时仍检查String encoding compatibility。

Encoding类

Encoding对象描述Ruby支持的编码,Encoding.find按name/alias查找,Encoding.list列出。避免用户任意name触发不可预测转换。

source_encoding = Encoding.find("Windows-31J")
target_encoding = Encoding::UTF_8
 
raw = File.binread(path)
tagged = raw.force_encoding(source_encoding)
raise EncodingError, "invalid source bytes" unless tagged.valid_encoding?
 
text = tagged.encode(target_encoding)

Default external/internal encodings受process locale/arguments影响,不应成为可复现程序的隐式配置;I/O boundary显式指定。Encoding objects immutable-like,可比较identity/name,但serialization使用canonical name。

正则表达式与编码

Regexp pattern与target String必须encoding-compatible。不同于是否pattern grammar正确。

pattern = /[[:alpha:]]+/
match = pattern.match("Ruby日本語")
p match && match[0]

Character class、case-insensitive与dot对Unicode的支持受Ruby/Onigmo版本和options影响。Binary String使用byte-oriented pattern,不应混入text pattern。出现CompatibilityError先追两边encoding/validity,不要统一force UTF-8。

IO类与编码:外部编码与内部编码

External encoding描述stream bytes,internal encoding是读取后自动transcode目标;"r:Windows-31J:UTF-8"可表达二者。把转换放在adapter。

File.open(path, "r:Windows-31J:UTF-8") do |file|
  file.each_line do |line|
    raise "unexpected internal encoding" unless line.encoding == Encoding::UTF_8
  end
end

Mode string支持和encoding names按平台/Ruby版本验证。Binary mode禁止自动transcode。Network protocol若headers声明charset,先验证allowlist再设置source;缺失charset使用协议default,不随locale猜。

编码的设定与作用

IO#set_encoding可配置external/internal和invalid/undef options。Strict default让坏data尽早失败;:replace需给replacement、计数并保留raw locator。是业务决策。

Replacement可能把两个不同identifiers变成同一String,安全/主键数据禁止silent replace。Display-only feed可替换但打标;批处理把坏record隔离并继续,最终status仍反映partial failure。

编码转换与错误证据

encode可能抛InvalidByteSequenceError(source bytes非法)或UndefinedConversionError(target无对应字符)。帮助定位而不泄漏数据。

def decode_strict(raw, source, target = Encoding::UTF_8)
  tagged = raw.dup.force_encoding(source)
  raise Encoding::InvalidByteSequenceError unless tagged.valid_encoding?
  tagged.encode(target)
end

手工raise内建异常构造细节不理想,production定义domain DecodeError并保留cause/context。Round-trip decoded.encode(source)只在encoding可逆且normalization/replace未发生时可作为证据;不是所有转换lossless。

正式节点与章专属证据

  • :第 1 个正式节点要能回到“来源编码有可信元数据,转换严格,任何替换性数据损失都有记录。”,并说明故障发生前后的第一处差异。
  • :第 2 个正式节点要能回到“来源编码有可信元数据,转换严格,任何替换性数据损失都有记录。”,并说明故障发生前后的第一处差异。
  • :第 3 个正式节点要能回到“来源编码有可信元数据,转换严格,任何替换性数据损失都有记录。”,并说明故障发生前后的第一处差异。
  • :第 4 个正式节点要能回到“来源编码有可信元数据,转换严格,任何替换性数据损失都有记录。”,并说明故障发生前后的第一处差异。
  • :第 5 个正式节点要能回到“来源编码有可信元数据,转换严格,任何替换性数据损失都有记录。”,并说明故障发生前后的第一处差异。
  • :第 6 个正式节点要能回到“来源编码有可信元数据,转换严格,任何替换性数据损失都有记录。”,并说明故障发生前后的第一处差异。
  • :第 7 个正式节点要能回到“来源编码有可信元数据,转换严格,任何替换性数据损失都有记录。”,并说明故障发生前后的第一处差异。

对象与状态模型

从输入、接收者到可观察证据

怎样从原始字节、编码标签、有效性与转码错误定位乱码根因?

输入与接收者

固定Ruby的编码与字符串所需的原始值、Ruby 版本和调用入口。

状态变化

在执行前记录接收者身份,并声明脚本编码与魔法注释的允许状态。

观察证据

保存第19章 Encoding类的初值、参数、编码或资源位置。

正式节点:Ruby的编码与字符串、脚本编码与魔法注释、Encoding类、正则表达式与编码、IO类与编码、外部编码与内部编码、编码的设定与作用

控制与消息轨迹

在相同初值下定位首个分岔

  1. 01固定Ruby的编码与字符串的输入和接收者
  2. 02执行脚本编码与魔法注释并记录状态
  3. 03观察Encoding类的返回或副作用
  4. 04用编码的设定与作用核对不变量并复位

运行不变量:来源编码有可信元数据,转换严格,任何替换性数据损失都有记录。

边界故障探针

一次只破坏一个前提

基线满足:来源编码有可信元数据,转换严格,任何替换性数据损失都有记录。

本章回顾:Encoding是边界协议,不是乱码补丁

  1. String由bytes和Encoding标签组成;validity只证明当前解释合法,source仍需可信metadata。
  2. Source magic comment只影响parser/literals,不影响runtime File/network input。
  3. Regexp与String需encoding-compatible;binary/text分别使用byte/character grammar。
  4. IO external/internal encoding把decode/transcode放入adapter,core统一使用UTF-8等内部representation。
  5. Invalid/undefined/compatibility分开处理,replace是可审计data loss,关键字段默认strict/quarantine。

练习与答案

练习

  1. 问题 1:建立正常轨迹。 回答“怎样从原始字节、编码标签、有效性与转码错误定位乱码根因?”,并写出四步执行记录。
  1. 问题 2:注入单一故障。 只制造“对未知字节直接 force_encoding 为 UTF-8 并把标签变化当作转码成功”,应从哪里开始定位?
  1. 问题 3:覆盖正式节点。 用一个证据包串联Ruby的编码与字符串、脚本编码与魔法注释、Encoding类、正则表达式与编码、IO类与编码、外部编码与内部编码、编码的设定与作用,说明为什么结论可由另一位读者独立复核。

名词解释

名词解释

本章出现的专业名词,用大白话再讲一遍。

Ruby的编码与字符串

“第19章 Encoding类”中的正式节点;必须说明它接收什么、改变什么,以及用什么结果复核。

脚本编码与魔法注释

“第19章 Encoding类”中的正式节点;必须说明它接收什么、改变什么,以及用什么结果复核。

Encoding类

“第19章 Encoding类”中的正式节点;必须说明它接收什么、改变什么,以及用什么结果复核。

正则表达式与编码

“第19章 Encoding类”中的正式节点;必须说明它接收什么、改变什么,以及用什么结果复核。

IO类与编码

“第19章 Encoding类”中的正式节点;必须说明它接收什么、改变什么,以及用什么结果复核。

外部编码与内部编码

“第19章 Encoding类”中的正式节点;必须说明它接收什么、改变什么,以及用什么结果复核。

编码的设定与作用

“第19章 Encoding类”中的正式节点;必须说明它接收什么、改变什么,以及用什么结果复核。

资料与写作方式声明

本章以Ruby 基础教程第 5 版作者支持资料合法公开试读核定可见范围,并以目录限定未公开部分,并结合正文列出的技术资料独立重写;不宣称复现原书正文,也不沿用原作表述。

原作版权归作者与出版社所有;本站原创教学结构与表述仅供学习交流。

讨论

评论区加载中…