7

Ruby 1.9.3 では、文字列のコードポイントを取得できます。

> "foo\u00f6".codepoints.to_a
 => [102, 111, 111, 246] 

他の方向、つまり整数配列から文字列に移動する組み込みメソッドはありますか?

私は知っています:

# not acceptable; only works with UTF-8
[102, 111, 111, 246].pack("U*")

# works, but not very elegant
[102, 111, 111, 246].inject('') {|s, cp| s << cp }

# concise, but I need to unshift that pesky empty string to "prime" the inject call
['', 102, 111, 111, 246].inject(:<<)

更新 (ニクラスの回答への応答)

興味深い議論。 pack("U*")は常に UTF-8 文字列をinject返しますが、バージョンはファイルのソース エンコーディングで文字列を返します。

#!/usr/bin/env ruby
# encoding: iso-8859-1

p [102, 111, 111, 246].inject('', :<<).encoding
p [102, 111, 111, 246].pack("U*").encoding
# this raises an Encoding::CompatibilityError
[102, 111, 111, 246].pack("U*") =~ /\xf6/

私の場合、inject呼び出しは ISO-8859-1 文字列をpack返しますが、UTF-8 を返します。エラーを防ぐために使用できますpack("U*").encode(__ENCODING__)が、それでは余分な作業が必要になります。

更新 2

どうやら String#<< は、文字列のエンコーディングによっては常に正しく追加されるとは限りません。したがって、パックは依然として最良の選択肢のようです。

[225].inject(''.encode('utf-16be'), :<<)  # fails miserably
[225].pack("U*").encode('utf-16be')  # works
4

2 に答える 2

10

あなた自身の試みの最も明白な適応は

[102, 111, 111, 246].inject('', :<<)

ただし、これは、最初の空の文字列リテラルにUnicode文字範囲全体を保持できるエンコーディングがある場合にのみ機能するため、適切な解決策ではありません。以下は失敗します:

#!/usr/bin/env ruby
# encoding: iso-8859-1
p "\u{1234}".codepoints.to_a.inject('', :<<)

だから私は実際にお勧めします

codepoints.pack("U*")

「UTF-8でのみ機能する」とはどういう意味かわかりません。UTF-8エンコーディングでRuby文字列を作成しますが、UTF-8はUnicode文字範囲全体を保持できるので、何が問題なのでしょうか。観察:

irb(main):010:0> s = [0x33333, 0x1ffff].pack("U*")
=> "\u{33333}\u{1FFFF}"
irb(main):011:0> s.encoding
=> #<Encoding:UTF-8>
irb(main):012:0> [0x33333, 0x1ffff].pack("U*") == [0x33333, 0x1ffff].inject('', :<<)
=> true
于 2012-04-24T19:34:43.890 に答える
2

配列の値と の値に応じて、次のEncoding.default_internalことを試してください。

[102, 111, 111, 246].map(&:chr).inject(:+)

エンコードには注意が必要です。次の点に注意してください。

irb(main):001:0> 0.chr.encoding
=> #<Encoding:US-ASCII>
irb(main):002:0> 127.chr.encoding
=> #<Encoding:US-ASCII>
irb(main):003:0> 128.chr.encoding
=> #<Encoding:ASCII-8BIT>
irb(main):004:0> 255.chr.encoding
=> #<Encoding:ASCII-8BIT>
irb(main):005:0> 256.chr.encoding
RangeError: 256 out of char range
        from (irb):5:in `chr'
        from (irb):5
        from C:/Ruby200/bin/irb:12:in `<main>'
irb(main):006:0>

デフォルトでは、コードポイントが 0..127 か 128..256 かに応じて、US-ASCII または ASCII-8BIT を返すのが好きなため、256.chr は失敗します。

これは、8ビット値のポイントをカバーする必要があります。値が 255 より大きい場合 (おそらく Unicode コードポイント)、次の操作を実行できます。

irb(main):006:0> Encoding.default_internal = "utf-8"
=> "utf-8"
irb(main):007:0> 256.chr.encoding
=> #<Encoding:UTF-8>
irb(main):008:0> 256.chr.codepoints
=> [256]
irb(main):009:0>

Encoding.default_internal を「utf-8」に設定すると、255 を超える Unicode 値が正常に動作するはずです (ただし、以下を参照してください)。

irb(main):009:0> 65535.chr.encoding
=> #<Encoding:UTF-8>
irb(main):010:0> 65535.chr.codepoints
=> [65535]
irb(main):011:0> 65536.chr.codepoints
=> [65536]
irb(main):012:0> 65535.chr.bytes
=> [239, 191, 191]
irb(main):013:0> 65536.chr.bytes
=> [240, 144, 128, 128]
irb(main):014:0>

興味深いことに、ASCII-8BIT と UTF-8 は混在していないようです。

irb(main):014:0> (0..127).to_a.map(&:chr).inject(:+).encoding
=> #<Encoding:US-ASCII>
irb(main):015:0> (0..128).to_a.map(&:chr).inject(:+).encoding
=> #<Encoding:ASCII-8BIT>
irb(main):016:0> (0..255).to_a.map(&:chr).inject(:+).encoding
=> #<Encoding:ASCII-8BIT>
irb(main):017:0> ((0..127).to_a + (256..1000000).to_a).map(&:chr).inject(:+).encoding
RangeError: invalid codepoint 0xD800 in UTF-8
        from (irb):17:in `chr'
        from (irb):17:in `map'
        from (irb):17
        from C:/Ruby200/bin/irb:12:in `<main>'
irb(main):018:0> ((0..127).to_a + (256..0xD7FF).to_a).map(&:chr).inject(:+).encoding
=> #<Encoding:UTF-8>
irb(main):019:0> (0..256).to_a.map(&:chr).inject(:+).encoding
Encoding::CompatibilityError: incompatible character encodings: ASCII-8BIT and UTF-8
        from (irb):19:in `+'
        from (irb):19:in `each'
        from (irb):19:in `inject'
        from (irb):19
        from C:/Ruby200/bin/irb:12:in `<main>'
irb(main):020:0>

ASCII-8BIT コードポイントがすべて 0..127 である限り、ASCII-8BIT と UTF-8 を連結できます。

irb(main):020:0> 256.chr.encoding
=> #<Encoding:UTF-8>
irb(main):021:0> (0.chr.force_encoding("ASCII-8BIT") + 256.chr).encoding
=> #<Encoding:UTF-8>
irb(main):022:0> 255.chr.encoding
=> #<Encoding:ASCII-8BIT>
irb(main):023:0> (255.chr + 256.chr).encoding
Encoding::CompatibilityError: incompatible character encodings: ASCII-8BIT and UTF-8
        from (irb):23
        from C:/Ruby200/bin/irb:12:in `<main>'
irb(main):024:0>

これにより、あなたの質問に対する究極の解決策が得られます。

irb(main):024:0> (0..0xD7FF).to_a.map {|c| c.chr("utf-8")}.inject(:+).encoding
=> #<Encoding:UTF-8>
irb(main):025:0>

したがって、UTF-8が必要だと仮定すると、最も一般的な答えは次のとおりだと思います。

[102, 111, 111, 246].map {|c| c.chr("utf-8")}.inject(:+)

値が 0..255 であることがわかっていると仮定すると、これは簡単です。

[102, 111, 111, 246].map(&:chr).inject(:+)

あなたに与える:

irb(main):027:0> [102, 111, 111, 246].map {|c| c.chr("utf-8")}.inject(:+)
=> "fooö"
irb(main):028:0> [102, 111, 111, 246].map(&:chr).inject(:+)
=> "foo\xF6"
irb(main):029:0> [102, 111, 111, 246].map {|c| c.chr("utf-8")}.inject(:+).encoding
=> #<Encoding:UTF-8>
irb(main):030:0> [102, 111, 111, 246].map(&:chr).inject(:+).encoding
=> #<Encoding:ASCII-8BIT>
irb(main):031:0>

これが役立つことを願っています(おそらく少し遅れていますが)-同じ質問に対する答えを探しているのを見つけたので、自分で調べました。

于 2013-09-20T20:56:48.867 に答える