Почему Iconv работает по-разному в irb и интерпретаторе Ruby?

Мне нужно преобразовать латинские символы, такие как éáéíóúÀÉÍÓÚ и т. д., в строку, аналогичную без специальных акцентов или проводных символов:

é -> e
è -> e
Ä -> A

У меня есть файл с именем "test.rb":

require 'iconv'

puts Iconv.iconv("ASCII//translit", "utf-8", 'è').join

Когда я вставляю эти строки в irb, он работает, возвращая «e», как и ожидалось.

Бег:

$ ruby test.rb

Я получаю "?" в качестве вывода.

Я использую irb 0.9.5 (04.05.13) и Ruby 1.8.7 (2011-06-30 patchlevel 352) [i386-linux].


person zambotn    schedule 09.12.2011    source источник
comment
да, кодируется как utf-8. Это также системная кодировка по умолчанию. Я также думаю, что это не могло работать, потому что, если бы у меня была хорошая память, магический комментарий #encoding был введен в 1.9. хотя я только что попробовал, и это не работает.   -  person zambotn    schedule 09.12.2011


Ответы (1)


Ruby 1.8.7 не отличался многобайтовыми символами, как 1.9+. В общем случае он обрабатывает строку как набор байтов, а не символов. Если вам нужна лучшая обработка таких символов, подумайте об обновлении до версии 1.9+.

У Джеймса Грея есть серия статей о работе с многобайтовыми символами в Ruby 1.8. Я настоятельно рекомендую потратить время на их прочтение. Это сложная тема, так что вы захотите прочитать всю серию, которую он написал пару раз.

Кроме того, для поддержки кодировки 1.8 требуется установленный флаг $KCODE:

$KCODE = "U"

поэтому вам нужно добавить это в код, работающий в 1.8.

Вот небольшой пример кода:

#encoding: UTF-8

require 'rubygems'
require 'iconv'

chars = "éáéíóúÀÉÍÓÚ"

puts Iconv.iconv("ASCII//translit", "utf-8", chars)

puts chars.split('')
puts chars.split('').join

Используя ruby ​​1.8.7 (2011-06-30 patchlevel 352) [x86_64-darwin10.7.0] и запуская его в IRB, я получаю:

1.8.7 :001 > #encoding: UTF-8
1.8.7 :002 >   
1.8.7 :003 >   require 'iconv'
true
1.8.7 :004 > 
1.8.7 :005 >   chars = "\303\251\303\241\303\251\303\255\303\263\303\272\303\200\303\211\303\215\303\223\303\232"
"\303\251\303\241\303\251\303\255\303\263\303\272\303\200\303\211\303\215\303\223\303\232"
1.8.7 :006 > 
1.8.7 :007 >   puts Iconv.iconv("ASCII//translit", "utf-8", chars)
'e'a'e'i'o'u`A'E'I'O'U
nil
1.8.7 :008 > 
1.8.7 :009 >   puts chars.split('')
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
nil
1.8.7 :010 > puts chars.split('').join
éáéíóúÀÉÍÓÚ

В строке 9 вывода я сказал Ruby разделить строку на символы, которые в версии 1.8.7 представляли собой байты. Результирующий '?' означает, что он не знал, что делать с выводом. Строку 10 я приказал разбить, в результате чего получился массив байтов, который join затем пересобрал в обычную строку, что позволило нормально перевести многобайтовые символы.

Запуск того же кода с использованием Ruby 1.9.2 показывает лучшее, более ожидаемое и желательное поведение:

1.9.2p290 :001 > #encoding: UTF-8
1.9.2p290 :002 >   
1.9.2p290 :003 >   require 'iconv'
true
1.9.2p290 :004 > 
1.9.2p290 :005 >   chars = "éáéíóúÀÉÍÓÚ"
"éáéíóúÀÉÍÓÚ"
1.9.2p290 :006 > 
1.9.2p290 :007 >   puts Iconv.iconv("ASCII//translit", "utf-8", chars)
'e'a'e'i'o'u`A'E'I'O'U
nil
1.9.2p290 :008 > 
1.9.2p290 :009 >   puts chars.split('')
é
á
é
í
ó
ú
À
É
Í
Ó
Ú
nil
1.9.2p290 :010 > puts chars.split('').join
éáéíóúÀÉÍÓÚ

Ruby поддерживал многобайтность символов через расширение split('').

Обратите внимание, что в обоих случаях Iconv.iconv поступил правильно, создав символы, визуально похожие на введенные символы. Хотя начальный апостроф выглядит неуместно, он служит напоминанием о том, что изначально символы были подчеркнуты.

Для получения дополнительной информации см. ссылки справа на связанные вопросы или попробуйте этот поиск SO для [ruby] [iconv]

person the Tin Man    schedule 09.12.2011
comment
Хорошо, спасибо большое, я понял немного больше! Теперь у меня 2 проблемы: первая я не могу обновить версию, потому что программа должна работать и на 1.8.7, вторая в заголовке: если я запускаю ваш скрипт с помощью ruby (вместо IRB ) вывод отличается и кажется, что Iconv в этом случае не работает. - person zambotn; 10.12.2011
comment
А также мой вывод для 7-й строки отличается: у меня eaeiouAEIOU вместо вашего 'e'a'e'i'o'u`A'E'I'O'U. - person zambotn; 10.12.2011
comment
Ruby 1.9+ автоматически выполняет require 'rubygems', поэтому вам нужно будет добавить это в свой скрипт с 1.8.7. Я добавлю его в свой образец, чтобы уменьшить путаницу. - person the Tin Man; 12.12.2011
comment
@ user1089668, посмотрите ссылку на статьи Джеймса Грея, которые я также добавил в пример. - person the Tin Man; 12.12.2011
comment
я потратил много времени, чтобы решить проблему: я убедил своего босса обновить рубин! знак равно - person zambotn; 03.02.2012