第14章 字符串类

系统掌握String创建、格式化、长度/索引、连接比较、分割、检索替换、Enumerator与encode/nkf编码转换,明确bytes、characters和shell边界。

学习目标

  • 能解释%Q、%q与Here Document与sprintf方法与反引号命令在“第14章 字符串类”中的责任边界
  • 能围绕“怎样区分字符串内容、字节、编码、可变性与外部命令结果?”运行正常与故障轨迹并定位首个分岔
  • 能用“第14章 字符串类的输入样本、接收者与方法、关键状态前后值、正常与失败输出、异常或退出状态,以及复位后的再次运行记录。”证明“每次连接、切片、比较和转换都保留明确的编码与数据来源。”

来源、版次与运行边界

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

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

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

围绕“怎样区分字符串内容、字节、编码、可变性与外部命令结果?”,本页验收“第14章 字符串类的输入样本、接收者与方法、关键状态前后值、正常与失败输出、异常或退出状态,以及复位后的再次运行记录。”。先预测正常轨迹,再只注入“把反引号命令输出当成可信字符串,忽略命令失败和外部编码”;若无法定位第一处状态分岔,就拒绝当前解释。

从“String就是字符数组”这个近似开始

Ruby String同时持有byte sequence与Encoding标签,并且可变。所谓length、index、slice、Regexp与output都受encoding影响;它不是简单的Unicode code point Array,更不是用户感知grapheme集合。贯穿全章。

先预测:text.lengthtext.bytesize为何可能不同?UTF-8中文一个character通常占多个bytes;协议限制10 bytes与UI限制10 characters不是同一问题。先命名单位再调用API。

字符串的创建:%Q、%q与Here Document

Double-quoted String处理escape与interpolation,single-quoted处理更少escape;%Q(delimiter)近似double,%q近似single,可避开大量quote escaping。Delimiter可用配对括号或单字符,但nested与terminator规则要清楚。

name = "Ruby"
expanded = %Q(hello #{name}\nnext)
literal = %q(hello #{name}\nnext)
 
p expanded
p literal

Here Document用terminator包多行文本,quoted terminator控制interpolation;<<-等形式影响terminator indentation,现代squiggly heredoc在Ruby 2.3可用性需按版本确认。Template中的user input仍需目标context escaping,interpolation不提供HTML/SQL/shell安全。

message = <<MESSAGE
title: Ruby
status: ready
MESSAGE
 
puts message

应根据内容选择,不以“最短写法”为唯一标准。

使用sprintf方法与反引号命令

sprintf/format按format string控制width、precision、base和type;format与arguments不匹配会失败或产生意外输出。适合日志字段和报告,但machine serialization用JSON/CSV。

p format("%04d %.2f", 23, 3.14159)
p format("%<name>s:%<score>.1f", name: "A", score: 91.25)

Backticks执行shell并返回stdout,exit status在$?,stderr处理另行决定。它把command parsing交给shell,interpolate user input会注入;使用Open3.capture3system(command, arg1, arg2)的argument-vector形式,并设置timeout/output上限。Command output仍是带encoding的String。

获取字符串的长度与索引

length/size按当前encoding解释characters,bytesize按bytes;each_byteeach_char提供不同unit。Grapheme cluster(emoji组合、附加音标)可能包含多个codepoints,用户界面截断需Unicode segmentation library/规则。

string[index]、slice、Range与Regexp forms返回String/nil的版本语义需验证;Ruby 2.x早期版本曾有整数codepoint差异。不允许把byte offset误作character index。

text = "Ruby中文"
p length: text.length
p bytes: text.bytesize
p first: text[0]
p slice: text[0, 4]

Binary protocol使用ASCII-8BIT与byteslice,文本使用valid encoding与character APIs。Regexp match offsets在目标version/encoding下也要实测,跨系统locator最好同时记录byte offset和line/column生成规则。

字符串的连接与比较

+返回new String,<</concat修改receiver,interpolation创建结果,Array#join适合多片段组合。影响aliasing与performance。

parts = ["ruby", "array", "string"]
joined = parts.join("/")
 
buffer = String.new
parts.each { |part| buffer << part << "\n" }
p joined: joined, buffer: buffer

示例用String.new明确创建可变buffer;较新Ruby常见unary plus复制frozen String,但目标版本需确认。重复result = result + piece产生中间Strings;buffer mutation需确保receiver不被共享/frozen。

==比较内容并考虑encoding compatibility,<=>用于lexicographic ordering,casecmp做case-insensitive比较但Unicode/locale规则有限。用户语言排序、case folding与normalization是独立i18n问题,不能用downcase后<=>替代locale collation。

字符串的分割与换行符的使用方法

Split的separator可String/Regexp,limit决定最大fields和trailing empty处理;默认whitespace有特殊规则。是parser的一部分。

line = "a,b,,c,"
p line.split(",")
p line.split(",", -1)
p line.lines

Structured CSV含quote、embedded comma/newline,必须用CSV library。Line endings可能是LF、CRLF或CR;each_line/lines按record separator,chomp移除separator而strip还删除其它whitespace。保留原文件时不要chomp后统一补LF。

字符串的检索与替换

include?给boolean,index给position/nil,Regexp#match给MatchData;选择caller需要的evidence。避免先boolean后重复搜索。

sub替换首个match,gsub替换全部;可传replacement String或block。Backreference和escape规则使dynamic replacement复杂,block形式更安全清楚。Bang variants修改receiver且无变化时可能返回nil。

source = "id=12 id=34"
result = source.gsub(/\d+/) { |digits| "[#{digits}]" }
 
p source: source
p result: result

大量替换应评估Regexp backtracking与output expansion上限;untrusted input先限制length。Normalization(NFC等)会改变codepoint sequence,搜索前是否normalize属于product policy。

Enumerator对象:连接与reverse

each_lineeach_chareach_byte无block时返回Enumerator,可with_index/lazy组合。区分record、character和byte processing。

Reverse按String字符解释,但grapheme cluster可能被拆;面向用户text reverse不是简单String#reverse。Array of Strings连接优先join,减少separator和最后一个元素的branch。Binary bytes转换先force_encoding/validate的具体责任要在boundary明确。

encode方法与nkf库

String#encoding是标签,force_encoding只改解释不转换bytes;encode(target, source, options)执行transcoding。是可靠文本系统的核心。

raw = File.binread(path)
text = raw.force_encoding("Windows-31J")
raise EncodingError, "invalid source" unless text.valid_encoding?
 
utf8 = text.encode("UTF-8")

实际source encoding来自protocol/metadata,不应猜测。Invalid/undefined conversion可raise或使用replace option;替换会丢信息,必须计数/记录policy。NKF标准库处理日文编码、MIME等转换,但新代码优先String#encode明确source/target,只有需要NKF特有功能时使用并测试。

正式节点与章专属证据

  • :第 1 个正式节点要能回到“每次连接、切片、比较和转换都保留明确的编码与数据来源。”,并说明故障发生前后的第一处差异。
  • :第 2 个正式节点要能回到“每次连接、切片、比较和转换都保留明确的编码与数据来源。”,并说明故障发生前后的第一处差异。
  • :第 3 个正式节点要能回到“每次连接、切片、比较和转换都保留明确的编码与数据来源。”,并说明故障发生前后的第一处差异。
  • :第 4 个正式节点要能回到“每次连接、切片、比较和转换都保留明确的编码与数据来源。”,并说明故障发生前后的第一处差异。
  • :第 5 个正式节点要能回到“每次连接、切片、比较和转换都保留明确的编码与数据来源。”,并说明故障发生前后的第一处差异。
  • :第 6 个正式节点要能回到“每次连接、切片、比较和转换都保留明确的编码与数据来源。”,并说明故障发生前后的第一处差异。
  • :第 7 个正式节点要能回到“每次连接、切片、比较和转换都保留明确的编码与数据来源。”,并说明故障发生前后的第一处差异。
  • :第 8 个正式节点要能回到“每次连接、切片、比较和转换都保留明确的编码与数据来源。”,并说明故障发生前后的第一处差异。
  • :第 9 个正式节点要能回到“每次连接、切片、比较和转换都保留明确的编码与数据来源。”,并说明故障发生前后的第一处差异。
  • :第 10 个正式节点要能回到“每次连接、切片、比较和转换都保留明确的编码与数据来源。”,并说明故障发生前后的第一处差异。

对象与状态模型

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

怎样区分字符串内容、字节、编码、可变性与外部命令结果?

输入与接收者

固定%Q、%q与Here Document所需的原始值、Ruby 版本和调用入口。

状态变化

在执行前记录接收者身份,并声明sprintf方法与反引号命令的允许状态。

观察证据

保存第14章 字符串类的初值、参数、编码或资源位置。

正式节点:%Q、%q与Here Document、sprintf方法与反引号命令、字符串的长度与索引、字符串的连接与比较、字符串的分割、换行符的使用方法、字符串的检索与替换、Enumerator对象、连接与reverse、encode方法与nkf库

控制与消息轨迹

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

  1. 01固定%Q、%q与Here Document的输入和接收者
  2. 02执行sprintf方法与反引号命令并记录状态
  3. 03观察字符串的长度与索引的返回或副作用
  4. 04用encode方法与nkf库核对不变量并复位

运行不变量:每次连接、切片、比较和转换都保留明确的编码与数据来源。

边界故障探针

一次只破坏一个前提

基线满足:每次连接、切片、比较和转换都保留明确的编码与数据来源。

本章回顾:所有文本操作先命名单位与编码

  1. Literal/%Q/%q/heredoc决定interpolation与escape;format string是shape协议,backticks是process boundary。
  2. Length/index区分bytes、characters、graphemes,missing/range与版本语义要验证。
  3. Connection明确new/mutation,comparison区分codepoint与locale,split明确empty/trailing/schema。
  4. Search返回所需evidence,replacement控制Regexp/backreference/output,Enumerator明确yield unit。
  5. Force_encoding不转码;外部bytes按已知source验证并encode为内部encoding,output再显式转换。

练习与答案

练习

  1. 问题 1:建立正常轨迹。 回答“怎样区分字符串内容、字节、编码、可变性与外部命令结果?”,并写出四步执行记录。
  1. 问题 2:注入单一故障。 只制造“把反引号命令输出当成可信字符串,忽略命令失败和外部编码”,应从哪里开始定位?
  1. 问题 3:覆盖正式节点。 用一个证据包串联%Q、%q与Here Document、sprintf方法与反引号命令、字符串的长度与索引、字符串的连接与比较、字符串的分割、换行符的使用方法、字符串的检索与替换、Enumerator对象、连接与reverse、encode方法与nkf库,说明为什么结论可由另一位读者独立复核。

名词解释

名词解释

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

%Q、%q与Here Document

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

sprintf方法与反引号命令

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

字符串的长度与索引

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

字符串的连接与比较

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

字符串的分割

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

换行符的使用方法

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

字符串的检索与替换

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

Enumerator对象

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

连接与reverse

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

encode方法与nkf库

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

资料与写作方式声明

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

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

讨论

评论区加载中…