Ответ 1
Казалось бы, Ruby думает, что строковая кодировка уже utf8, поэтому, когда вы делаете
line.encode!('UTF-8', :undef => :replace, :invalid => :replace, :replace => "")
он фактически ничего не делает, потому что конечная кодировка совпадает с текущей кодировкой (по крайней мере, моя интерпретация кода в transcode.c
)
Реальный вопрос заключается в том, являются ли ваши исходные данные действительными в некоторой кодировке, которая не является utf-8, или это данные, которые должны быть utf-8, но имеют несколько бородавок в нем, которые вы хотите сбросить.
В первом случае правильная вещь - сказать рубине, что это за кодировка. Это можно сделать при открытии файла
File.open('somefile', 'r:iso-8859-1')
откроет файл, интерпретируя его содержимое как iso-8859-1
Вы даже можете получить ruby для перекодирования для вас
File.open('somefile', 'r:iso-8859-1:utf-8')
откроет файл как iso-8859-1, но когда вы прочитаете данные из него, байты будут преобразованы в utf-8 для вас.
Вы также можете вызвать force_encoding
, чтобы сообщить ruby, что такое строковое кодирование (это вообще не изменяет байты, оно просто сообщает ruby, как их интерпретировать).
Во втором случае, когда вы просто хотите сбросить все неприятные вещи в ваш utf-8, вы не можете просто вызвать encode!
, как вы, потому что это не-op. В рубине 2.1 и выше вы можете использовать String # scrub, в предыдущих версиях вы можете сделать это
line.encode!('UTF-16', :undef => :replace, :invalid => :replace, :replace => "")
line.encode!('UTF-8')
Сначала мы конвертируем в utf-16. Поскольку это другая кодировка, рубин фактически заменит наши недопустимые последовательности. Затем мы можем преобразовать обратно в utf-8. Это не потеряет никаких дополнительных данных, потому что utf-8 и utf-16 - это всего лишь два разных способа кодирования одного и того же базового набора символов.