ruby 1.9: неверная последовательность байтов в UTF-8 - PullRequest
108 голосов
/ 06 июня 2010

Я пишу сканер в Ruby (1.9), который потребляет много HTML со многих случайных сайтов.
При попытке извлечь ссылки, я решил просто использовать .scan(/href="(.*?)"/i) вместо nokogiri / hpricot (значительное ускорение). Проблема в том, что теперь я получаю много ошибок "invalid byte sequence in UTF-8".
Из того, что я понял, библиотека net/http не имеет каких-либо специфических параметров кодирования, и то, что входит, в основном не помечено должным образом. Как лучше всего работать с этими входящими данными? Я попытался .encode с установленными параметрами замены и неверными, но пока безуспешно ...

Ответы [ 11 ]

171 голосов
/ 16 января 2012

В Ruby 1.9.3 можно использовать String.encode, чтобы «игнорировать» недопустимые последовательности UTF-8. Вот фрагмент кода, который будет работать как в 1.8 ( iconv ), так и в 1.9 ( String # encode ):

require 'iconv' unless String.method_defined?(:encode)
if String.method_defined?(:encode)
  file_contents.encode!('UTF-8', 'UTF-8', :invalid => :replace)
else
  ic = Iconv.new('UTF-8', 'UTF-8//IGNORE')
  file_contents = ic.iconv(file_contents)
end

или, если у вас действительно проблемный ввод, вы можете выполнить двойное преобразование из UTF-8 в UTF-16 и обратно в UTF-8:

require 'iconv' unless String.method_defined?(:encode)
if String.method_defined?(:encode)
  file_contents.encode!('UTF-16', 'UTF-8', :invalid => :replace, :replace => '')
  file_contents.encode!('UTF-8', 'UTF-16')
else
  ic = Iconv.new('UTF-8', 'UTF-8//IGNORE')
  file_contents = ic.iconv(file_contents)
end
79 голосов
/ 27 августа 2013

Принятый ответ, ни другой ответ не работает для меня. Я нашел этот пост , который предложил

string.encode!('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '')

Это решило проблему для меня.

23 голосов
/ 14 января 2012

Мое текущее решение - запустить:

my_string.unpack("C*").pack("U*")

Это по крайней мере избавит от исключений, которые были моей главной проблемой

8 голосов
/ 12 мая 2014

Попробуйте это:

def to_utf8(str)
  str = str.force_encoding('UTF-8')
  return str if str.valid_encoding?
  str.encode("UTF-8", 'binary', invalid: :replace, undef: :replace, replace: '')
end
4 голосов
/ 06 июня 2010

Я рекомендую вам использовать анализатор HTML. Просто найдите самый быстрый.

Разбор HTML не так прост, как может показаться.

Браузеры анализируют недопустимые последовательности UTF-8 в HTML-документах UTF-8, просто помещая символ « ». Таким образом, после того, как недопустимая последовательность UTF-8 в HTML будет проанализирована, получающийся текст является допустимой строкой.

Даже внутри значений атрибутов вы должны декодировать HTML-объекты, такие как amp

Вот отличный вопрос, который подводит итог, почему вы не можете надежно анализировать HTML с помощью регулярного выражения: RegEx соответствует открытым тегам, кроме автономных тегов XHTML

3 голосов
/ 24 июля 2014
attachment = file.read

begin
   # Try it as UTF-8 directly
   cleaned = attachment.dup.force_encoding('UTF-8')
   unless cleaned.valid_encoding?
     # Some of it might be old Windows code page
     cleaned = attachment.encode( 'UTF-8', 'Windows-1252' )
   end
   attachment = cleaned
 rescue EncodingError
   # Force it to UTF-8, throwing out invalid bits
   attachment = attachment.force_encoding("ISO-8859-1").encode("utf-8", replace: nil)
 end
3 голосов
/ 15 мая 2013

Это похоже на работу:

def sanitize_utf8(string)
  return nil if string.nil?
  return string if string.valid_encoding?
  string.chars.select { |c| c.valid_encoding? }.join
end
2 голосов
/ 08 января 2012

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

ec1 = Encoding::Converter.new "UTF-8","Windows-1251",:invalid=>:replace,:undef=>:replace,:replace=>""
ec2 = Encoding::Converter.new "Windows-1251","UTF-8",:invalid=>:replace,:undef=>:replace,:replace=>""
t = ec2.convert ec1.convert t
1 голос
/ 16 октября 2012

Хотя решение Nakilon работает, по крайней мере, до того, как обойти ошибку, в моем случае у меня был этот странный f-up символ, происходящий из Microsoft Excel, преобразованный в CSV, который регистрировался в ruby ​​как (получаю это) кириллицу K, который в ruby ​​был жирным K. Чтобы исправить это, я использовал 'iso-8859-1', а именно. CSV.parse(f, :encoding => "iso-8859-1"), который превратил мои причудливые дрянные кириллические буквы K в гораздо более управляемый /\xCA/, который я мог затем удалить с помощью string.gsub!(/\xCA/, '')

0 голосов
/ 29 августа 2013

Если вам не нужны данные, вы можете просто сделать что-то вроде:

search_params = params[:search].valid_encoding? ? params[:search].gsub(/\W+/, '') : "nothing"

Я просто использовал valid_encoding?, чтобы пройти мимо. У меня есть поле поиска, и я снова и снова обнаруживал одну и ту же странность, поэтому использовал что-то вроде: просто чтобы система не сломалась. Так как я не контролирую пользовательский опыт для автоматической проверки перед отправкой этой информации (например, автоматическая обратная связь, чтобы сказать «пустышка!»), Я могу просто взять ее, раздеть и вернуть пустые результаты.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...