Skip to content

Ruby 字符串

Ruby 中的 String 对象用于存储和操作字符序列。在底层,这些字符根据字符串的编码(在现代 Ruby 中通常是 UTF-8)以字节表示。

字符串字面量可以使用单引号(')或双引号(`”“)创建。

单引号字符串执行最小化处理。大多数转义序列(如 \n)不会被解释,也不支持表达式插值。

'This is a simple Ruby string literal. \n is not a newline here.'

如果您需要在单引号字符串中包含撇号,请在其前面加上反斜杠:

'Won\'t you read O\'Reilly\'s book?'

双引号字符串支持各种转义序列和表达式插值。

"This string supports interpolation: #{1 + 1} and escape sequences like newlines \n and tabs \t."

在两种类型的引号中,反斜杠也可以转义另一个反斜杠,因此 \\ 会变成一个反斜杠字符。

您也可以使用 String.new 创建字符串:

empty_string = String.new
greeting = String.new("Hello")

考虑在 Ruby 文件的顶部使用 # frozen_string_literal: true 指示。这个指示(pragma)默认冻结文件中的所有字符串字面量,这可以提升性能并防止意外修改。

# frozen_string_literal: true
my_string = "hello"
# my_string << " world" # 这将引发一个 FrozenError

表达式替换允许使用 #{ 和 } 将任何 Ruby 表达式的值嵌入到双引号字符串(或其他支持插值的上下文,如 %Q{})中:

#!/usr/bin/env ruby
# frozen_string_literal: true
x, y, z = 12, 36, 72
puts "The value of x is #{x}."
puts "The sum of x and y is #{x + y}."
puts "The average was #{(x + y + z) / 3}."

这将产生以下结果:

The value of x is 12.
The sum of x and y is 48.
The average was 40.

Ruby 提供了百分号字符串语法(%),用于使用任意定界符创建具有不同行为的字符串。常见的形式包括:

  • %q{...} 或 %q(...) 或 %q[...] 等: 非插值字符串 (类似于单引号)。定界符可以是任意匹配的非字母数字字符对或成对匹配的字符,如 (), [], {}, <>。
  • %Q{...} 或 %{...}: 插值字符串 (类似于双引号)。
  • %x{...}: 插值字符串,会作为 shell 命令执行,并返回其标准输出 (类似于反引号 “)。
  • %w{word1 word2}: 由单词组成的非插值数组,按空白字符分割。
  • %W{word1 #{expr} word2}: 由单词组成的插值数组,按空白字符分割。
# Equivalent to "Ruby is fun."
str1 = %Q{Ruby is fun.}
# Equivalent to 'Ruby is fun.'
str2 = %q[Ruby is fun.]
# Equivalent to `ls -l` (executes command)
# output = %x(ls -l)
# Example with different delimiters
puts %q(Don't need to escape apostrophes here.)
puts %Q[Interpolation works: #{2 * 3}]

下表列出了常见的转义序列。它们在双引号字符串和类似上下文(例如 %Q{})中被解释。在单引号字符串中,只有 \' 和 \\ 具有特殊含义。

表示法描述
\反斜杠
’单引号
”双引号
\a响铃或警报 (0x07)
\b退格符 (0x08)
\e转义符 (0x1b)
\f换页符 (0x0c)
\n换行符 (0x0a)
\r回车符 (0x0d)
\s空格符 (0x20)
\t制表符 (0x09)
\v垂直制表符 (0x0b)
\nnn八进制表示法 (n 是 0-7)
\xnn十六进制表示法 (n 是 0-9, a-f, A-F)
\unnnnUnicode 字符 (nnnn 是十六进制)
\u{nnnn …}Unicode 字符 (一个或多个十六进制编码)

现代 Ruby(1.9 及更高版本)对字符编码具有强大的支持。字符串感知编码(encoding-aware),并且 UTF-8 是 Ruby 应用程序中最常用和推荐的编码。

Ruby 管理着几个默认编码设置:

  • Encoding.default_external: 指定从外部来源(例如文件、网络套接字)创建的字符串的默认编码。它通常根据您的系统 locale 设置(例如,LANG 环境变量,在现代系统上通常是 UTF-8)。
  • Encoding.default_internal: 如果设置,从外部来源读取的字符串将自动从 default_external 转码到此 default_internal 编码。除非特定内部处理需要不同的编码,否则通常将其保留为 nil。
  • 脚本编码: 您的 Ruby 源文件本身的编码。您应该在文件的顶部使用“魔法注释”来指定此编码,特别是当您的源代码在字符串字面量或注释中包含非 ASCII 字符时:
# encoding: utf-8
# Your Ruby code here...
# Or, more commonly combined with frozen string literal pragma:
# frozen_string_literal: true
# encoding: utf-8
MY_CONSTANT = "你好世界" # 中文的 Hello world

如果没有这个魔法注释,Ruby 可能会假定不同的编码(例如在没有 UTF-8 BOM 的情况下假定 US-ASCII),从而可能误解字符串字面量。UTF-8 是普遍推荐的脚本编码。

您可以使用 my_string.encoding 检查字符串的编码。要将字符串转换为其他编码,使用 my_string.encode('Target-Encoding') 或 my_string.encode(Encoding::TARGET_ENCODING)。如果您知道某个字符串的字节序列代表的编码与 Ruby 假定的不同,您可以使用 my_string.force_encoding('Correct-Encoding')。

#!/usr/bin/env ruby
# encoding: utf-8
s = "hello résumé"
puts "Original string: '#{s}', Encoding: #{s.encoding}"
begin
s_latin1 = s.encode(Encoding::ISO_8859_1)
puts "Encoded to Latin1: '#{s_latin1}', Encoding: #{s_latin1.encoding}"
rescue Encoding::UndefinedConversionError => e
puts "Error encoding to Latin1: #{e.message}"
# 如果 s 包含 ISO-8859-1 中无法表示的字符,则可能发生这种情况
end
# force_encoding 的示例 (谨慎使用)
byte_sequence = "\xC3\xA9".b # 'é' 的一个 UTF-8 序列,.b 使其变为 ASCII-8BIT
puts "Byte sequence: #{byte_sequence.inspect}, Encoding: #{byte_sequence.encoding}"
utf8_char = byte_sequence.force_encoding(Encoding::UTF_8)
puts "Forced to UTF-8: '#{utf8_char}', Encoding: #{utf8_char.encoding}"
# 在旧版本 Ruby (1.9 之前) 中使用的 $KCODE 变量已废弃且无效。

Ruby 中的字符串是 String 类的对象,该类提供了一系列丰富的操作方法。要调用 String 方法,您通常使用 String 对象的实例:

my_string = "Initial Value"
# or
# my_string = String.new("Initial Value")

示例:

#!/usr/bin/env ruby
# frozen_string_literal: true
my_str = String.new("THIS IS A TEST")
foo = my_str.downcase
puts "Lowercase string: #{foo}"

这将产生以下结果:

Lowercase string: this is a test

以下是一些常用的公共 String 方法(假定 str 是一个 String 对象)。有关完整列表,请参阅官方 Ruby 文档中关于 String 类的部分。

ID方法及描述
1str % arg
使用格式说明符格式化字符串 (类似于 sprintf)。arg 可以是单个值或用于多个替换的数组。示例: "Hello %s" % "World"。为了可读性,通常更推荐使用字符串插值 (#{}) 或 Kernel#sprintf。
2str * integer
返回一个新字符串,其中包含 str 重复 integer 次。
3str + other_str
将 other_str 连接到 str 的末尾,返回一个新字符串。
4str << obj
将 obj 连接到 str 的末尾,就地修改 str。如果 obj 是 0-255 之间的 Integer,它会被转换为一个字符。返回 str。
5str <=> other_str
比较 str 和 other_str,返回 -1 (小于)、0 (等于) 或 1 (大于)。比较是区分大小写且基于字节值的。
6str == obj
测试相等性。如果 obj 不是 String,返回 false。否则,如果 str <=> obj 是 0,则返回 true。
7str =~ regexp
将 str 与正则表达式 regexp 进行匹配。返回匹配的起始索引,如果未找到匹配则返回 nil。设置全局变量,例如 $& (匹配的字符串)。对于布尔结果可以考虑使用 str.match?(regexp),或者使用 str.match(regexp) 获取 MatchData 对象,这种方式对全局变量的影响较小。
8str.capitalize
返回一个新的字符串,其中第一个字符转换为大写,其余转换为小写。
9str.capitalize!
与 capitalize 相同,但就地修改 str。如果未进行任何更改,返回 str 或 nil。
10str.casecmp(other_str)
字符串的忽略大小写比较。返回 -1、0 或 1。str.casecmp?(other_str) (Ruby 2.4+ 版本) 返回 true 或 false。
11str.center(width, padstr=’ ‘)
返回一个长度为 width 的新字符串,其中 str 居中,并用 padstr 填充。
12str.chomp(separator=$/)
返回一个新字符串,其中从 str 的末尾移除给定的 separator (如果存在)。$/ 是输入记录分隔符,通常是换行符。如果 separator 是 "",它会移除所有尾部换行符。
13str.chomp!(separator=$/)
与 chomp 相同,但就地修改 str。返回 str 或 nil。
14str.chop
返回一个新字符串,其中最后一个字符被移除。如果字符串以 \r\n 结尾,则两者都被移除。
15str.chop!
与 chop 相同,但就地修改 str。返回 str 或 nil。
16str.concat(other_str)
str << other_str 的别名。
17str.count(char_set_1, …, char_set_n)
计算 str 中的字符。当只有一个参数时,计算 char_set_1 中字符出现的次数。当有多个参数时,计算所有 char_set 交集中存在的字符。字符集可以使用 ^ 表示否定,使用 - 表示范围 (例如 "a-z")。
18WARNING: This method uses a weak, often DES-based, hashing algorithm based on the system’s crypt(3). It is NOT suitable for secure password storage in modern applications. Consider using libraries like bcrypt-ruby for robust password hashing.
19str.delete(char_set_1, …, char_set_n)
返回 str 的一个副本,其中删除了其参数交集中的所有字符。
20str.delete!(char_set_1, …, char_set_n)
与 delete 相同,但就地修改 str。返回 str 或 nil。
21str.downcase
返回 str 的一个副本,其中所有大写字母都被替换为小写。如果支持 Unicode 大小写映射,则有效。
22str.downcase!
与 downcase 相同,但就地修改 str。返回 str 或 nil。
23str.dump
返回 str 的一个可打印版本,其中所有非打印字符都被 \nnn 表示法替换,特殊字符被转义。适用于调试或字符串字面量。
24str.each_char { |char| block }
将 str 的每个字符传递给块。如果没有提供块,则返回一个 Enumerator。
25str.each_byte { |byte| block }
将 str 的每个字节 (作为 0-255 的整数) 传递给块。如果没有提供块,则返回一个 Enumerator。
26str.each_line(separator=$/,chomp: false) { |line| block }
使用 separator 分割 str,将每个子字符串 (行) 传递给块。chomp 选项 (Ruby 2.6+ 版本) 会自动去除行的尾部空白。如果没有提供块,则返回一个 Enumerator。
27str.empty?
如果 str 的长度为零,返回 true。
28str.eql?(other)
如果两个字符串具有相同的长度和内容,则它们是 eql? 的。用于 Hash 的键相等性判断。
29str.gsub(pattern, replacement) or str.gsub(pattern) { |match| block }
返回 str 的一个副本,其中所有 pattern (String 或 Regexp) 的出现都被 replacement 或块的结果替换。
30str.gsub!(pattern, replacement) or str.gsub!(pattern) { |match| block }
与 gsub 相同,但就地修改 str。返回 str 或 nil。
31str[index] or str[start, length] or str[range] or str[regexp] or str[regexp, capture_group_index] or str[match_string]
元素引用 (切片)。访问字符或子字符串。负数索引从末尾开始计数。
32str[index] = new_val or str[start, length] = new_val … (similar to accessors)
元素赋值。替换字符串的一部分。
33str.hash
返回 str 的整数散列值。用于 Hash 对象。
34str.hex
将 str 的开头字符视为十六进制数字 (可选的 0x 前缀和符号),并返回相应的整数。出错时返回 0。
35str.include?(substring)
如果 str 包含给定的 substring,返回 true。
36str.index(substring_or_regexp [, offset])
返回 substring_or_regexp 在 str 中第一次出现的索引,如果指定了 offset 则从该位置开始搜索。如果未找到,返回 nil。
37str.insert(index, other_str)
将 other_str 插入到 str 中索引 index 处的字符之前。修改 str。
38str.inspect
返回 str 的一个可打印版本,其中特殊字符被转义 (类似于 dump,但常用于 puts 和 p)。
39str.intern or str.to_sym
返回与 str 对应的 Symbol,如果它不存在则创建它。
40str.length
返回 str 的长度,以字符为单位 (尊重编码)。别名: size。
41str.ljust(width, padstr=’ ‘)
返回一个长度为 width 的新字符串,其中 str 左对齐,并用 padstr 填充。
42str.lstrip
返回 str 的一个副本,其中开头的空白字符被移除。
43str.lstrip!
就地从 str 中移除开头的空白字符。返回 str 或 nil。
44str.match(pattern [, pos])
将 pattern 转换为 Regexp (如果尚未转换),并从 pos 位置开始与 str 进行匹配。返回一个 MatchData 对象或 nil。
45str.match?(pattern [, pos])
类似于 match,但返回 true 或 false,并且不更新 $~ 或创建 MatchData 对象,这使得它在布尔检查时速度更快 (Ruby 2.4+ 版本)。
46str.oct
将 str 的开头字符视为八进制数字 (可选的符号),并返回相应的整数。出错时返回 0。
47str.replace(other_str)
用 other_str 替换 str 的内容。就地修改 str。
48str.reverse
返回一个新的字符串,其中包含 str 的字符的反向顺序。
49str.reverse!
就地反转 str。返回 str。
50str.rindex(substring_or_regexp [, offset])
返回 substring_or_regexp 在 str 中最后一次出现的索引,如果指定了 offset 则从该位置向前搜索。如果未找到,返回 nil。
51str.rjust(width, padstr=’ ‘)
返回一个长度为 width 的新字符串,其中 str 右对齐,并用 padstr 填充。
52str.rstrip
返回 str 的一个副本,其中末尾的空白字符被移除。
53str.rstrip!
就地从 str 中移除末尾的空白字符。返回 str 或 nil。
54str.scan(pattern) or str.scan(pattern) { |match_capture_array| block }
遍历 str,匹配 pattern (Regexp 或 String)。对于每个匹配项,结果会添加到数组中或传递给块。如果 pattern 包含捕获组,则会为每个匹配项产生/返回一个捕获组数组。
55str.slice(…) / str.slice!(…)
str[...] 和 str[...] = ... (破坏性切片) 的别名。slice! 删除指定部分并返回它。
56str.split(pattern=$;, limit=0)
根据 pattern (String 或 Regexp 定界符) 将 str 分割成子字符串,返回一个数组。$; 是默认的字段分隔符 (默认为 nil,表示按空白字符分割)。limit 控制字段的数量。
57str.squeeze([other_char_sets]*)
返回一个新字符串,其中连续重复的相同字符 (可选地仅限于 other_char_sets 中的字符) 被替换为一个单独的字符。
58str.squeeze!([other_char_sets]*)
与 squeeze 相同,但就地修改 str。返回 str 或 nil。
59str.strip
返回 str 的一个副本,其中开头的和末尾的空白字符被移除。
60str.strip!
就地从 str 中移除开头的和末尾的空白字符。返回 str 或 nil。
61str.sub(pattern, replacement) or str.sub(pattern) { |match| block }
返回 str 的一个副本,其中 pattern 第一次出现的地方被替换。
62str.sub!(pattern, replacement) or str.sub!(pattern) { |match| block }
与 sub 相同,但就地修改 str。返回 str 或 nil。
63str.succ or str.next
返回 str 的后继字符串 (例如 “a”.succ -> “b”, “9”.succ -> “10”, “az”.succ -> “ba”)。
64str.succ! or str.next!
就地将 str 修改为其后继。返回 str。
65str.swapcase
返回 str 的一个副本,其中大写字母字符被转换为小写,反之亦然。如果支持 Unicode,则有效。
66str.swapcase!
与 swapcase 相同,但就地修改 str。返回 str 或 nil。
67str.to_f
将 str 的开头字符解释为浮点数,并返回结果。出错时返回 0.0。
68str.to_i(base=10)
将 str 的开头字符解释为给定 base (2-36) 的整数,并返回结果。出错时返回 0。
69str.to_s or str.to_str
返回 self (字符串本身)。
70str.tr(from_str, to_str)
返回 str 的一个副本,其中 from_str 中的字符被 to_str 中对应的字符替换。支持 ^ 否定和 c1-c2 范围。
71str.tr!(from_str, to_str)
与 tr 相同,但就地修改 str。返回 str 或 nil。
72str.tr_s(from_str, to_str)
处理方式类似于 tr,然后压缩翻译后字符的连续重复项。
73str.tr_s!(from_str, to_str)
与 tr_s 相同,但就地修改 str。返回 str 或 nil。
74str.unpack(format)
根据 format 字符串解码 str (可能包含二进制数据),返回提取值的数组。请参阅下面的 unpack 指令。
75str.upcase
返回 str 的一个副本,其中所有小写字母都被替换为大写。如果支持 Unicode 大小写映射,则有效。
76str.upcase!
与 upcase 相同,但就地修改 str。返回 str 或 nil。
77str.upto(other_str, exclusive_end=false) { |s| block }
使用 succ 从 str 迭代到 other_str (默认为包含结束值),将每个值传递给块。如果没有提供块,则返回一个 Enumerator。

String#unpack 方法使用由单字符指令组成的格式字符串来解码二进制数据。每个指令后可跟一个数字(重复计数)或 *(消耗剩余部分)。由于指令在某些情况下可能很复杂且依赖于平台(例如原生大小),请参阅 Ruby 文档以获取完整详细信息。

指令返回描述
AString任意二进制字符串 (移除尾部的 null 和空格)。
aString任意二进制字符串。
BString位字符串 (MSB 在前)。
bString位字符串 (LSB 在前)。
CInteger8 位无符号整数 (unsigned char)。
cInteger8 位有符号整数 (signed char)。
D, dFloat双精度浮点数,原生格式。
EFloat双精度浮点数,小端字节序。
eFloat单精度浮点数,小端字节序。
F, fFloat单精度浮点数,原生格式。
GFloat双精度浮点数,网络 (大端) 字节序。
gFloat单精度浮点数,网络 (大端) 字节序。
HString十六进制字符串 (MSB 字节的前半部分)。
hString十六进制字符串 (LSB 字节的前半部分)。
IInteger无符号整数,原生字节序 (大小可用 _ 或 ! 指定)。I_ 或 I! 表示原生 int 的大小。
iInteger有符号整数,原生字节序。i_ 或 i! 表示原生 int 的大小。
LInteger无符号 32 位整数,原生字节序 (L_, L!)。L> 表示大端,L< 表示小端。
lInteger有符号 32 位整数,原生字节序 (l_, l!)。l> 表示大端,l< 表示小端。
MStringQuoted-printable 编码字符串。
mStringBase64 编码字符串。
NInteger无符号 32 位整数,网络 (大端) 字节序。
nInteger无符号 16 位整数,网络 (大端) 字节序。
PString结构体指针 (固定长度字符串)。
pStringnull 终止字符串指针。
QInteger无符号 64 位整数,原生字节序 (Q_, Q!)。Q> 表示大端,Q< 表示小端。
qInteger有符号 64 位整数,原生字节序 (q_, q!)。q> 表示大端,q< 表示小端。
SInteger无符号 16 位整数,原生字节序 (S_, S!)。S> 表示大端,S< 表示小端。
sInteger有符号 16 位整数,原生字节序 (s_, s!)。s> 表示大端,s< 表示小端。
UIntegerUTF-8 字符作为整数码点。
uStringUU 编码字符串。
VInteger无符号 32 位整数,VAX (小端) 字节序。
vInteger无符号 16 位整数,VAX (小端) 字节序。
wIntegerBER 压缩整数。
XN/A向后跳过一个字节。
xN/A向前跳过一个字节 (null 字节)。
ZStringnull 终止字符串 (移除尾部的 null)。使用 * 时,读取直到第一个 null。
@N/A跳到长度参数给定的偏移量处。

unpack 方法对于解析二进制数据格式非常强大。

#!/usr/bin/env ruby
# encoding: ascii-8bit # 常用于二进制数据
puts "abc \0\0abc \0\0".unpack('A6Z6').inspect # => ["abc", "abc "]
puts "abc \0\0".unpack('a3a3').inspect # => ["abc", " \x00\x00"]
puts "abc \0abc \0".unpack('Z*Z*').inspect # => ["abc ", "abc "]
puts "aa".unpack('b8B8').inspect # => ["10000110", "01100001"] # 取决于 'a' 的 ASCII 值
puts "aaa".unpack('H2H2c').inspect # => ["61", "61", 97] # ASCII 'a' 是 0x61,十进制 97
puts "\xFE\xFF\xFE\xFF".unpack('sS').inspect # => [-2, 65534] # 在此示例中假定原生 short 为 16 位小端字节序
puts "now=20is".unpack('M*').inspect # => ["now is"]
puts "whole".unpack('x a x2 a X2 a X a X2 a').inspect # => ["h", "e", "l", "l", "o"] # 演示跳过
# 网络字节序整数示例
data = [1024, 20480].pack('Nn') # 将两个整数打包成一个二进制字符串
puts data.unpack('Nn').inspect # => [1024, 20480]

有关 pack 和 unpack 指令的更详细示例和解释,请查阅官方 Ruby 文档中关于 Array#pack 和 String#unpack 的部分。