編碼

基礎

字元編碼,通常簡稱為編碼,是對應於

有些字元集只包含 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

方法 Encoding.default_external 傳回預設外部編碼

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.default_internal # => nil

方法 Encoding.default_internal= 設定預設內部編碼

Encoding.default_internal = 'US-ASCII' # => "US-ASCII"
Encoding.default_internal              # => #<Encoding:US-ASCII>

相容編碼

方法 Encoding.compatible? 傳回兩個給定物件是否編碼相容(也就是是否可以串接);傳回串接字串的編碼,或不相容時傳回 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#encodingRegexp#encoding 方法來擷取編碼。

不過,這些編碼的預設編碼為

檔案系統編碼

檔案系統編碼是來自檔案系統的字串的預設編碼

Encoding.find("filesystem") # => #<Encoding:UTF-8>

區域設定編碼

區域設定編碼是來自環境(而非檔案系統)的字串的預設編碼

Encoding.find('locale') # => #<Encoding:IBM437>

串流編碼

某些串流物件可以有兩個編碼;這些物件包括

這兩個編碼為

外部編碼

外部編碼,是一個編碼物件,指定從串流讀取的位元組如何解釋為字元。

預設外部編碼為

預設外部編碼由方法 Encoding.default_external 傳回,並可由

您也可以使用方法 Encoding.default_external= 設定預設外部編碼,但這麼做可能會造成問題;在變更前後建立的字串可能會有不同的編碼。

對於 IO 或 File 物件,外部編碼可以由

對於 IO、File、ARGF 或 StringIO 物件,外部編碼可以由

內部編碼

內部編碼,是一個編碼物件或 nil,指定從串流讀取的字元如何轉換為內部編碼中的字元;這些字元會變成一個字串,其編碼設定為內部編碼。

預設內部編碼為 nil(沒有轉換)。它由方法 Encoding.default_internal 傳回,並可由

您也可以使用方法 Encoding.default_internal= 設定預設內部編碼,但這麼做可能會造成問題;在變更前後建立的字串可能會有不同的編碼。

對於 IO 或 File 物件,內部編碼可以由

對於 IO、File、ARGF 或 StringIO 物件,內部編碼可以由

腳本編碼

Ruby 腳本具有腳本編碼,可透過下列方式擷取

__ENCODING__ # => #<Encoding:UTF-8>

預設腳本編碼為 UTF-8;Ruby 原始檔可以在檔案第一行(或第一行有 shebang 時的第二行)使用魔術註解設定其腳本編碼。註解必須包含 codingencoding 字詞,後接冒號、空白和 Encoding 名稱或別名

# encoding: ISO-8859-1
__ENCODING__ #=> #<Encoding:ISO-8859-1>

轉碼

轉碼 是將字元序列從一種編碼轉換為另一種編碼的程序。

盡可能保持字元不變,但代表這些字元的位元組可能會變更。

無法在目標編碼中表示的字元處理方式,可由 @Encoding+Options 指定。

轉碼字串

下列各方法會轉碼字串

轉碼串流

下列各方法可能會轉碼串流;是否轉碼取決於外部和內部編碼

這個範例將一個字串寫入檔案,並將其編碼為 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 核心中的許多方法接受關鍵字引數作為編碼選項。

有些選項會指定或使用替換字串,用於某些轉碼作業。替換字串可以是任何編碼,只要可以轉換為目標字串的編碼即可。

這些關鍵字值對會指定編碼選項