编码¶ ↑
基础¶ ↑
一个字符编码,通常缩写为编码,是以下两者之间的映射:
-
一个 8 位字节序列(每个字节在
0..255
范围内)。 -
特定字符集中的字符。
一些字符集只包含 1 字节字符;例如,US-ASCII 有 256 个 1 字节字符。这个字符串,以 US-ASCII 编码,有六个字符,存储为六个字节
s = 'Hello!'.encode('US-ASCII') # => "Hello!" s.encoding # => #<Encoding:US-ASCII> s.bytes # => [72, 101, 108, 108, 111, 33]
其他编码可能涉及多字节字符。例如,UTF-8 编码超过一百万个字符,每个字符用一到四个字节编码。这些字符中值最低的对应于 ASCII 字符,因此是 1 字节字符。
s = 'Hello!' # => "Hello!" s.bytes # => [72, 101, 108, 108, 111, 33]
其他字符,例如欧元符号,是多字节的。
s = "\u20ac" # => "€" s.bytes # => [226, 130, 172]
编码类¶ ↑
编码对象¶ ↑
Ruby 编码由 Encoding 类中的常量定义。每个常量只能有一个 Encoding 实例。方法 Encoding.list
返回一个 Encoding 对象数组(每个常量一个)。
Encoding.list.size # => 103 Encoding.list.first.class # => Encoding Encoding.list.take(3) # => [#<Encoding:ASCII-8BIT>, #<Encoding:UTF-8>, #<Encoding:US-ASCII>]
名称和别名¶ ↑
方法 Encoding#name
返回 Encoding 的名称。
Encoding::ASCII_8BIT.name # => "ASCII-8BIT" Encoding::WINDOWS_31J.name # => "Windows-31J"
一个 Encoding 对象有零个或多个别名;方法 Encoding#names
返回一个包含名称和所有别名的数组。
Encoding::ASCII_8BIT.names # => ["ASCII-8BIT", "BINARY"] Encoding::WINDOWS_31J.names #=> ["Windows-31J", "CP932", "csWindows31J", "SJIS", "PCK"]
方法 Encoding.aliases
返回所有别名/名称对的哈希表。
Encoding.aliases.size # => 71 Encoding.aliases.take(3) # => [["BINARY", "ASCII-8BIT"], ["CP437", "IBM437"], ["CP720", "IBM720"]]
方法 Encoding.name_list
返回所有编码名称和别名的数组。
Encoding.name_list.size # => 175 Encoding.name_list.take(3) # => ["ASCII-8BIT", "UTF-8", "US-ASCII"]
方法 name_list
返回的条目比方法 list
多,因为它包含名称及其别名。
方法 Encoding.find
返回给定名称或别名的 Encoding,如果存在。
Encoding.find("US-ASCII") # => #<Encoding:US-ASCII> Encoding.find("US-ASCII").class # => Encoding
默认编码¶ ↑
上面的方法 Encoding.find
也为这些特殊名称中的每一个返回一个默认 Encoding。
-
external
:默认外部 Encoding。Encoding.find("external") # => #<Encoding:UTF-8>
-
internal
:默认内部 Encoding(可能为nil
)。Encoding.find("internal") # => nil
-
locale
:来自环境的字符串的默认 Encoding。Encoding.find("locale") # => #<Encoding:UTF-8> # Linux Encoding.find("locale") # => #<Encoding:IBM437> # Windows
-
filesystem
:来自文件系统的字符串的默认 Encoding。Encoding.find("filesystem") # => #<Encoding:UTF-8>
方法 Encoding.default_external
返回默认外部 Encoding。
Encoding.default_external # => #<Encoding:UTF-8>
方法 Encoding.default_external=
设置该值。
Encoding.default_external = 'US-ASCII' # => "US-ASCII" Encoding.default_external # => #<Encoding:US-ASCII>
方法 Encoding.default_internal
返回默认内部 Encoding。
Encoding.default_internal # => nil
方法 Encoding.default_internal=
设置默认内部 Encoding。
Encoding.default_internal = 'US-ASCII' # => "US-ASCII" Encoding.default_internal # => #<Encoding:US-ASCII>
兼容编码¶ ↑
方法 Encoding.compatible?
返回两个给定对象是否编码兼容(即,它们是否可以连接);返回连接字符串的 Encoding,如果 incompatible 则返回 nil
。
rus = "\u{442 435 441 442}" eng = 'text' Encoding.compatible?(rus, eng) # => #<Encoding:UTF-8> s0 = "\xa1\xa1".force_encoding('iso-8859-1') # => "\xA1\xA1" s1 = "\xa1\xa1".force_encoding('euc-jp') # => "\x{A1A1}" Encoding.compatible?(s0, s1) # => nil
字符串编码¶ ↑
一个 Ruby String
对象有一个编码,它是 Encoding 类的实例。可以通过方法 String#encoding
获取编码。
字符串字面量的默认编码是脚本编码;请参阅 脚本编码。
's'.encoding # => #<Encoding:UTF-8>
使用方法 String.new
创建的字符串的默认编码是
-
对于字符串对象参数,该字符串的编码。
-
对于字符串字面量,脚本编码;请参阅 脚本编码。
在任何情况下,都可以指定任何编码
s = String.new(encoding: 'UTF-8') # => "" s.encoding # => #<Encoding:UTF-8> s = String.new('foo', encoding: 'ASCII-8BIT') # => "foo" s.encoding # => #<Encoding:ASCII-8BIT>
可以更改字符串的编码
s = "R\xC3\xA9sum\xC3\xA9" # => "Résumé" s.encoding # => #<Encoding:UTF-8> s.force_encoding('ISO-8859-1') # => "R\xC3\xA9sum\xC3\xA9" s.encoding # => #<Encoding:ISO-8859-1>
更改分配的编码不会改变字符串的内容;它只改变内容的解释方式
s # => "R\xC3\xA9sum\xC3\xA9" s.force_encoding('UTF-8') # => "Résumé"
字符串的实际内容也可以更改;请参阅 字符串转码。
以下是一些有用的查询方法
s = "abc".force_encoding("UTF-8") # => "abc" s.ascii_only? # => true s = "abc\u{6666}".force_encoding("UTF-8") # => "abc晦" s.ascii_only? # => false s = "\xc2\xa1".force_encoding("UTF-8") # => "¡" s.valid_encoding? # => true s = "\xc2".force_encoding("UTF-8") # => "\xC2" s.valid_encoding? # => false
符号和正则表达式编码¶ ↑
存储在 Symbol
或 Regexp
对象中的字符串也具有编码;可以使用方法 Symbol#encoding
或 Regexp#encoding
获取编码。
但是,它们的默认编码是
-
US-ASCII,如果所有字符都是 US-ASCII。
-
否则为脚本编码;请参阅 (脚本脚本编码] 在
Encoding)[rdoc-ref:encodings.rdoc
。
文件系统编码¶ ↑
文件系统编码是来自文件系统的字符串的默认编码
Encoding.find("filesystem") # => #<Encoding:UTF-8>
区域设置编码¶ ↑
区域设置编码是来自环境的字符串的默认编码,而不是来自文件系统的字符串
Encoding.find('locale') # => #<Encoding:IBM437>
流编码¶ ↑
某些流对象可以具有两种编码;这些对象包括以下实例
两种编码是
-
外部编码,用于标识流的编码。
-
内部编码,用于指定从流中构造的字符串使用的编码(如果非
nil
)。
外部编码¶ ↑
外部编码是一个 Encoding 对象,它指定如何将从流中读取的字节解释为字符。
默认外部编码为
-
文本流为 UTF-8。
-
二进制流为 ASCII-8BIT。
默认外部编码由方法 Encoding.default_external
返回,可以通过以下方式设置:
-
Ruby 命令行选项
--external_encoding
或-E
。
您也可以使用方法 Encoding.default_external=
设置默认外部编码,但这样做可能会导致问题;更改前后创建的字符串可能具有不同的编码。
对于 IO 或 File 对象,外部编码可以通过以下方式设置:
-
创建对象时使用
external_encoding
或encoding
打开选项;请参阅 打开选项。
对于 IO、File、ARGF 或 StringIO 对象,外部编码可以通过以下方式设置:
-
方法
set_encoding
或(除了 ARGF)set_encoding_by_bom
。
内部编码¶ ↑
内部编码是一个 Encoding 对象或 nil
,它指定如何将从流中读取的字符转换为内部编码中的字符;这些字符成为一个编码设置为内部编码的字符串。
默认内部编码为 nil
(无转换)。它由方法 Encoding.default_internal
返回,可以通过以下方式设置:
-
Ruby 命令行选项
--internal_encoding
或-E
。
您也可以使用方法 Encoding.default_internal=
设置默认内部编码,但这样做可能会导致问题;更改前后创建的字符串可能具有不同的编码。
对于 IO 或 File 对象,内部编码可以通过以下方式设置:
-
创建对象时使用
internal_encoding
或encoding
打开选项;请参阅 打开选项。
对于 IO、File、ARGF 或 StringIO 对象,内部编码可以通过以下方式设置:
-
方法
set_encoding
。
脚本编码¶ ↑
Ruby 脚本具有脚本编码,可以通过以下方式检索:
__ENCODING__ # => #<Encoding:UTF-8>
默认脚本编码为 UTF-8;Ruby 源文件可以通过文件第一行(或第二行,如果第一行有 shebang)上的魔术注释设置其脚本编码。注释必须包含单词 coding
或 encoding
,后跟冒号、空格和 Encoding
名称或别名
# encoding: ISO-8859-1 __ENCODING__ #=> #<Encoding:ISO-8859-1>
转码¶ ↑
转码 是将字符序列从一种编码更改为另一种编码的过程。
尽可能地,字符保持不变,但表示它们的字节可能会改变。
无法在目标编码中表示的字符的处理方式可以通过 @Encoding+Options 指定。
转码字符串¶ ↑
以下每个方法都将转码一个字符串
-
String#encode
: 根据给定的编码和选项将self
转码为一个新字符串。 -
String#encode!
: 与String#encode
相似,但会就地转码self
。 -
String#scrub
: 通过用给定的或默认替换字符串替换无效字节序列,将self
转码为一个新字符串。 -
String#scrub!
: 与String#scrub
相似,但会就地转码self
。 -
String#unicode_normalize
: 根据 Unicode 规范化将self
转码为一个新字符串。 -
String#unicode_normalize!
: 与String#unicode_normalize
相似,但会就地转码self
。
转码流¶ ↑
这些方法中的每一个都可能转码一个流;它是否这样做取决于外部和内部编码
-
IO.foreach
: 将给定流的每一行都生成到块中。 -
IO.new
: 为给定的整数文件描述符创建并返回一个新的 IO 对象。 -
IO.open
: 创建一个新的 IO 对象。 -
IO.pipe
: 创建一对连接的读取器和写入器 IO 对象。 -
IO.popen
: 创建一个 IO 对象来与子进程交互。 -
IO.read
: 返回一个包含来自给定流的所有或部分字节的字符串。 -
IO.readlines
: 返回一个字符串数组,这些字符串来自给定流的每一行。 -
IO.write
: 将给定的字符串写入给定的流。
此示例将字符串写入文件,以 ISO-8859-1 编码,然后将文件读入一个新字符串,以 UTF-8 编码
s = "R\u00E9sum\u00E9" path = 't.tmp' ext_enc = 'ISO-8859-1' int_enc = 'UTF-8' File.write(path, s, external_encoding: ext_enc) raw_text = File.binread(path) transcoded_text = File.read(path, external_encoding: ext_enc, internal_encoding: int_enc) p raw_text p transcoded_text
输出
"R\xE9sum\xE9" "Résumé"
编码选项¶ ↑
Ruby 核心中的许多方法接受关键字参数作为编码选项。
一些选项指定或利用一个替换字符串,用于某些转码操作。替换字符串可以是任何可以转换为目标字符串编码的编码。
这些关键字-值对指定编码选项
-
对于无效字节序列
-
:invalid: nil
(默认): 抛出异常。 -
:invalid: :replace
: 用替换字符串替换每个无效字节序列。
示例
s = "\x80foo\x80" s.encode('ISO-8859-3') # Raises Encoding::InvalidByteSequenceError. s.encode('ISO-8859-3', invalid: :replace) # => "?foo?"
-
-
对于未定义的字符
-
:undef: nil
(默认): 抛出异常。 -
:undef: :replace
: 用替换字符串替换每个未定义字符。
示例
s = "\x80foo\x80" "\x80".encode('UTF-8', 'ASCII-8BIT') # Raises Encoding::UndefinedConversionError. s.encode('UTF-8', 'ASCII-8BIT', undef: :replace) # => "�foo�"
-
-
替换字符串
-
:replace: nil
(默认): 将替换字符串设置为默认值: 对于 Unicode 编码为"\uFFFD"
(“�”),否则为'?'
。 -
:replace: some_string
: 将替换字符串设置为给定的some_string
;覆盖:fallback
。
示例
s = "\xA5foo\xA5" options = {:undef => :replace, :replace => 'xyzzy'} s.encode('UTF-8', 'ISO-8859-3', **options) # => "xyzzyfooxyzzy"
-
-
替换回退
可以指定以下之一
-
:fallback: nil
(默认): 无替换回退。 -
:fallback: hash_like_object
: 将替换回退设置为给定的hash_like_object
;替换字符串为hash_like_object[X]
。 -
:fallback: method
: 将替换回退设置为给定的method
;替换字符串为method(X)
。 -
:fallback: proc
: 将替换回退设置为给定的proc
;替换字符串为proc[X]
。
示例
s = "\u3042foo\u3043" hash = {"\u3042" => 'xyzzy'} hash.default = 'XYZZY' s.encode('ASCII', fallback: h) # => "xyzzyfooXYZZY" def (fallback = "U+%.4X").escape(x) self % x.unpack("U") end "\u{3042}".encode("US-ASCII", fallback: fallback.method(:escape)) # => "U+3042" proc = Proc.new {|x| x == "\u3042" ? 'xyzzy' : 'XYZZY' } s.encode('ASCII', fallback: proc) # => "XYZZYfooXYZZY"
-
-
XML 实体
可以指定以下之一
-
:xml: nil
(默认): 不处理 XML 实体。 -
:xml: :text
: 将源文本视为 XML;用每个未定义字符的大写十六进制数字字符引用替换每个未定义字符,但-
&
被替换为&
。 -
<
被替换为<
。 -
>
被替换为>
。
-
-
:xml: :attr
: 将源文本视为 XML 属性值;用每个未定义字符的大写十六进制数字字符引用替换每个未定义字符,但-
替换字符串
r
被双引号包围 ("r"
)。 -
每个嵌入的双引号被替换为
"
。 -
&
被替换为&
。 -
<
被替换为<
。 -
>
被替换为>
。
-
示例
s = 'foo"<&>"bar' + "\u3042" s.encode('ASCII', xml: :text) # => "foo\"<&>\"barあ" s.encode('ASCII', xml: :attr) # => "\"foo"<&>"barあ\""
-
-
换行符
可以指定以下之一
-
:cr_newline: true
: 用回车符 ("\r"
) 替换每个换行符 ("\n"
)。 -
:crlf_newline: true
: 用回车符/换行符字符串 ("\r\n"
) 替换每个换行符 ("\n"
)。 -
:universal_newline: true
: 用换行符 ("\n"
) 替换每个回车符 ("\r"
) 和每个回车符/换行符字符串 ("\r\n"
)。
示例
s = "\n \r \r\n" # => "\n \r \r\n" s.encode('ASCII', cr_newline: true) # => "\r \r \r\r" s.encode('ASCII', crlf_newline: true) # => "\r\n \r \r\r\n" s.encode('ASCII', universal_newline: true) # => "\n \n \n"
-