Почему этот рубин использует \u001 для 1 и как его изменить?
Я написал классический код fizzbuzz.
Спецификация:
describe "can determine fizzbuxx output for" do
it "3" do
expect(Fizzbuzz.nums(3)).to eq "123fizz"
end
end
Код:
class Fizzbuzz
def self.nums(n)
result=""
(1..n).each do |inner|
puts "inner #{inner}"
result << inner
if (inner % 3)==0
result << 'fizz'
end
if (inner % 5)==0
result << 'buzz'
end
end
result
end
end
Почему я получаю \u001
... вместо 1
?
Failures:
1) can determine fizzbuxx output for 3
Failure/Error: expect(Fizzbuzz.nums(3)).to eq "123fizz"
expected: "123fizz"
got: "\u0001\u0002\u0003fizz"
(compared using ==)
# ./fizzbuzz_spec.rb:5:in `block (2 levels) in <top (required)>'
Это какая-то проблема с utf-8?
3 ответа
В рубине для строки concatenation: <<
Если объект является целым числом, он рассматривается как кодовая точка и преобразуется в символ перед объединением. А начиная с Ruby 1.9 кодировкой по умолчанию является UTF-8. Кодовые точки должны быть закодированы с помощью UTF-8:
1.chr(Encoding::UTF_8)
# => "\u0001"
Итак, когда вы делаете это:
"" << 1
Руби на самом деле считают 1
в качестве кодовой точки и попытаться преобразовать ее в строку с кодировкой UTF-8, и фактически вместо этого сделает это:
"" << 1.chr(Encoding::UTF_8)
#=> "\u0001"
Итак, чтобы решить вашу проблему. Вы можете явно преобразовать Fixnum в String, изменив код на:
result << inner.to_s
или со строковой интерполяцией:
result << "#{inner}"
Это ожидаемое поведение String#<<
метод. Из документации по Ruby:
Объединяет данный объект на ул. Если объект является целым числом, он рассматривается как кодовая точка и преобразуется в символ перед объединением.
Вы должны явно преобразовать целое число в строку перед добавлением:
'' << 1 # => "\u0001"
'' << 1.to_s => "1"
Для небольших чисел их типом является FixNum [Содержит целочисленные значения, которые могут быть представлены в машинном слове (минус 1 бит). ]
result << inner
Явно преобразуйте его в строку, и вы получите желаемый результат.
result << inner.to_s