2012-04-24 14 views
7

、私は、文字列のコードポイントを得ることができます。ruby​​ 1.9 - `string.codepoints.to_a`の最も簡単な逆数は何ですか?ルビー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(:<<) 

UPDATE(ニクラス答えへの応答)

興味深い議論。 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/ 

packはUTF-8を返しながら、injectコールは、ISO-8859-1文字列を返します。エラーを防ぐために、私はpack("U*").encode(__ENCODING__)を使用することができましたが、それは余分な作業をします。

UPDATE 2

どうやら文字列#< <は常に文字列のエンコーディングに応じて、正しく追加されません。だからpackはまだ最高の選択肢だと思う。

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

ソースコードとしてUTF-8を使用することもできます。 –

+0

'codepoints'は、非UnicodeエンコーディングのUnicodeコードポイントを返さないことに注意してください(例えば、GB18030はUnicodeのすべてをエンコードしていてもこの目的のために" Unicode "ではありません)。 –

答えて

10

独自の試みの最も明白な適応は

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

リテラル初期空の文字列を保持することが可能であるエンコーディングを持っている場合にのみ動作しますので、これは、しかし、良い解決策ではないだろうUnicode文字の全範囲。次は失敗:

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

だから私は実際に私はあなたが「のみUTF-8で動作します」によって何を意味するのか分からない

codepoints.pack("U*") 

をお勧めします。それはUTF-8エンコーディングでRuby文字列を作成しますが、UTF-8はUnicode文字列全体を保持できるので、何が問題なのですか?お守り:あなたの配列の値とEncoding.default_internalの値に応じ

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 
+0

ニース、それを考えていたはずです。私はまだ組み込みのメソッドがあるのだろうかと思っています。 – Kelvin

+0

@Kelvin:更新を確認してください。 '.pack(" U * ")'は行く方法です。 –

+0

'pack'を使うというあなたの提案に私の質問に答えました。注射はもっと一般的な解決策だと私は思う。 – Kelvin

2

を、あなたは試してみてください:

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

あなたはエンコーディングの注意する必要があります。次の点に注意してください、それはコードポイントが0..127か128..256であるかどうかに応じて、US-ASCIIまたはASCII-8BITのいずれかを返すのが好きなので、デフォルトでは

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> 

、256.chrは失敗します。

これは、8ビット値のポイントをカバーする必要があります。エンコードで

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> 

:あなたは255(おそらくUnicodeのコードポイント)より大きな値を持っている場合は、次の操作を行うことができます。

: - :default_internal "UTF-8"、> 255 Unicode値が正常に動作します(ただし下記参照)必要があるためにセットASCII-8BITとUTF-8が混在していないようです

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> 

今では面白いです

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とUTF-8は限りASCII-8BITのコードポイントとして、連結することができ、すべての0..127である:

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である知っていると仮定すると:

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> 

を私はこれはperha、少し遅くはいえ(役に立てば幸い:あなたを与える

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

ps) - これは同じ質問に対する答えを探しているので、私はそれを自分で調べました。

関連する問題