Qual é o oposto de chr () em Ruby?

100

Em muitas línguas, há um par de funções chr()e ord(), que convertem entre números e valores de caracteres. Em alguns idiomas, ord()é chamado asc().

Ruby tem Integer#chr, o que funciona muito bem:

>> 65.chr
A

Justo. Mas como você segue o outro caminho?

"A".each_byte do |byte|
   puts byte
end

estampas:

65

e isso é muito próximo do que eu quero. Mas prefiro evitar um loop - estou procurando algo curto o suficiente para ser legível ao declarar um const.

RJHunter
fonte

Respostas:

84

Se String # ord não existia no 1.9, existia no 2.0:

"A".ord #=> 65
Rob Cameron
fonte
33

Em Ruby até e incluindo a série 1.8, o seguinte produzirá 65 (para ASCII):

puts ?A
'A'[0]

O comportamento mudou no Ruby 1.9, ambos os itens acima produzirão "A" ao invés. A maneira correta de fazer isso no Ruby 1.9 é:

'A'[0].ord

Infelizmente, o ordmétodo não existe no Ruby 1.8.

Robert Gamble
fonte
É uma pena que o caminho "correto" no Ruby 1.9 seja tão longo, mas pelo menos aparecerá mais facilmente nas pesquisas por "ord". Obrigado pela sua resposta muito detalhada.
RJHunter
13

Experimentar:

'A'.unpack('c')
dylanfm
fonte
1
Agora que Ruby 1.9 mudou o significado de 'A' [0], este é o método mais portátil.
AShelly de
10

Eu gostaria de marcar o comentário de dylanfm e AShelly com +1, mas adicionar o [0]:

'A'.unpack('C')[0]

A chamada unpack retorna um Array contendo um único inteiro, que nem sempre é aceito onde um inteiro é desejado:

$ ruby ​​-e 'printf ("0x% 02X \ n", "A" .unpack ("C"))'
-e: 1: em `printf ': não é possível converter Array em Integer (TypeError)
    de -e: 1
$ ruby ​​-e 'printf ("0x% 02X \ n", "A" .unpack ("C") [0])'
0x41
$ 

Estou tentando escrever um código que funcione em Ruby 1.8.1, 1.8.7 e 1.9.2.

Editado para passar C para descompactar em maiúsculas, porque descompactar ("c") me dá -1 onde ord () me dá 255 (apesar de rodar em uma plataforma onde o char de C é assinado).

Martin Dorey
fonte
4

Acabei de descobrir isso ao montar uma versão Ruby pura de Stringprep via RFCs.

Cuidado com a chrfalha fora de [0,255], em vez disso, use substituições portáteis 1.9.x - 2.1.x:

[22] pry(main)> "\u0221".ord.chr
RangeError: 545 out of char range
from (pry):2:in 'chr'
[23] pry(main)> x = "\u0221".unpack('U')[0]
=> 545
[24] pry(main)> [x].pack('U')
=> "ȡ"
[25] pry(main)>

fonte
Obrigado, esta parece ser a única resposta que dá chare seu inverso no caso do Unicode corretamente
Munyari
3

Além disso, se você tiver o caractere em uma string e quiser decodificá-lo sem um loop:

puts 'Az'[0]
=> 65
puts 'Az'[1]
=> 122
Kent Fredric
fonte
2

E se

coloca? A

GregD
fonte
2

Você pode ter estes:

65.chr.ord
'a'.ord.chr
Eduardo santana
fonte
2

Se você não se importa em extrair os valores de uma matriz, você pode usar "A".bytes

Clark
fonte
0

Estou escrevendo o código para 1.8.6 e 1.9.3 e não consegui fazer nenhuma dessas soluções funcionar em ambos os ambientes :(

No entanto, me deparei com outra solução: http://smajnr.net/2009/12/ruby-1-8-nomethoderror-undefined-method-ord-for-string.html

Isso também não funcionou para mim, mas eu adaptei para meu uso:

unless "".respond_to?(:ord)
  class Fixnum
    def ord
      return self
    end
  end
end

Feito isso, o seguinte funcionará em ambos os ambientes

'A'[0].ord

Hantscolin
fonte
Quando o user18096 escreveu sua resposta, "A".unpack("C")[0]ele estava visando Ruby 1.8.1, Ruby 1.8.7 e Ruby 1.9.2. Ele falha em seu ambiente? Que tipo de falha?
RJHunter
Oi RJHunter, Estou tentando converter um caractere específico em uma string para seu valor numérico. O código a seguir funciona no 1.9.3, mas não no 1.8.6. self.status = tagAccountString[4].unpack('C')[0] Em 1.8.6 eu consigo Exception undefined method desempacotar 'para 0: Fixnum processando dados de tag buffer principal - saída' O código a seguir funciona (com minha solução proposta) em ambos os ambientes. self.status = tagAccountString[4].ord Qualquer conselho (por exemplo, uma solução melhor) é mais que bem
hantscolin
tagAccountString[4]retorna uma String em Rubies mais recentes, mas costumava retornar um Fixnum no Ruby 1.8. É por isso que você viu o erro undefined method unpack for 0:Fixnum,. Você pode usar status = tagAccountString[4,1].unpack('C')[0]ou mesmo status, = tagAccountString.unpack('xxxxC')se quiser sempre ignorar quatro caracteres e converter o próximo.
RJHunter
Obrigado RJHunter pela explicação e soluções alternativas. No entanto, como "minha" solução é mais legível e reutilizável, vou ficar com ela (a menos que haja uma boa razão para não também?)
hantscolin