Строка#encode не исправлена ошибка" недопустимая последовательность байтов в UTF-8"


Я знаю, что существует множество подобных вопросов об этой ошибке, и я пробовал многие из них без успеха. Проблема, с которой я сталкиваюсь, включает байт xA1 и бросает

ArgumentError: недопустимая последовательность байтов в UTF-8

Я попробовал следующее, но безуспешно:

"xA1".encode('UTF-8', :undef => :replace, :invalid => :replace,
    :replace => "").sub('', '')
"xA1".encode('UTF-8', :undef => :replace, :invalid => :replace,
    :replace => "").force_encoding('UTF-8').sub('', '')
"xA1".encode('UTF-8', :undef => :replace, :invalid => :replace,
    :replace => "").encode('UTF-8').sub('', '')

Каждая строка выдает ошибку за меня. Что я делаю не так?

Обновление:

Приведенных выше строк не только в СИБ. Однако я изменил свое приложение для кодирования строки файла CVS, использующие тот же метод#encode и аргументы, и я получаю ту же ошибку при чтении строки из файла (Примечание: это работает, если вы выполняете операции над той же строкой без использования IO).

bad_line = "col1tcol2tbadxa1"

bad_line.sub('', '') # does NOT fail
puts bad_line # => col1 col2    bad?

tmp = Tempfile.new 'foo' # write the line to a file to emulate real problem
tmp.puts bad_line
tmp.close

tmp2 = Tempfile.new 'bar'

begin
  IO.foreach tmp.path do |line|
    line.encode!('UTF-8', :undef => :replace, :invalid => :replace, :replace => "")
    line.sub('', '') # fail: invalid byte sequence in UTF-8
    tmp2.puts line
  end
  tmp2.close

  # this would fail if the above error didn't halt execution
  CSV.foreach(tmp2.path) do |row|
    puts row.inspect # fail: invalid byte sequence in UTF-8
  end
ensure
  tmp.unlink
  tmp2.close
  tmp2.unlink
end
2 11

2 ответа:

Похоже, что ruby думает, что кодировка строки уже utf8, поэтому когда вы делаете

line.encode!('UTF-8', :undef => :replace, :invalid => :replace, :replace => "")

На самом деле он ничего не делает, потому что кодировка назначения совпадает с текущей кодировкой (по крайней мере, это моя интерпретация кода в transcode.c)

Реальный вопрос здесь заключается в том, допустимы ли ваши исходные данные в какой-то кодировке, которая не является utf-8, или это данные, которые должны быть utf-8, но в них есть несколько бородавок, которые вы хотите отбросить.

В в первом случае правильнее всего будет сказать ruby, что это за кодировка. Вы можете сделать это, когда откроете файл

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, что такое кодировка строки (это не изменяет байты вообще, это просто говорит руби, как их интерпретировать).

Во втором случае, когда вы просто хотите сбросить все неприятные вещи, попавшие в ваш utf-8, вы не можете просто вызвать encode! , потому что это не op. в ruby 2.1 и выше, вы можете использовать String#scrub , в предыдущих версиях вы можете сделать это

line.encode!('UTF-16', :undef => :replace, :invalid => :replace, :replace => "")
line.encode!('UTF-8')

Мы сначала преобразуем в utf-16. Поскольку это другая кодировка, ruby фактически заменит наши недопустимые последовательности. Затем мы можем вернуться к utf-8. От этого мы ничего не потеряем. дополнительные данные, потому что utf-8 и utf-16-это просто два разных способа кодирования одного и того же базового набора символов.

Возможно, вы используете этот код в IRB. У меня было много проблем с кодированием с IRB. В этом случае попробуйте сохранить этот код в виде файла .rb и запустить его из командной строки.