Я пишу искателя в Ruby (1.9), который потребляет много HTML из множества случайных сайтов.
При попытке извлечь ссылки я решил использовать .scan(/href="(.*?)"/i)
вместо nokogiri/hpricot (основное ускорение). Проблема в том, что теперь я получаю много ошибок "invalid byte sequence in UTF-8
".
Из того, что я понял, библиотека net/http
не имеет каких-либо специфических параметров кодирования, а материал, который входит, в основном не помечен должным образом.
Каким будет лучший способ работать с этими входящими данными? Я попробовал .encode
с установленными параметрами замены и недопустимых параметров, но пока ничего не добился успеха...
Ruby 1.9: неверная последовательность байтов в UTF-8
Ответ 1
В 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
Ответ 2
Принятый ответ или другой ответ работают на меня. Я нашел этот пост, который предложил
string.encode!('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '')
Это исправило проблему для меня.
Ответ 3
Мое текущее решение:
my_string.unpack("C*").pack("U*")
Это, по крайней мере, избавится от исключений, которые были моей основной проблемой.
Ответ 4
Попробуйте следующее:
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
Ответ 5
Я рекомендую вам использовать парсер HTML. Просто найдите самый быстрый.
Анализ HTML не так прост, как может показаться.
Браузеры анализируют недопустимые последовательности UTF-8, в HTML-документах UTF-8, просто помещая символ "". Поэтому, как только некорректная последовательность UTF-8 в HTML будет проанализирована, результирующий текст является допустимой строкой.
Даже внутри значений атрибутов вы должны декодировать объекты HTML, такие как amp
Вот большой вопрос, который подводит итог, почему вы не можете достоверно разобрать HTML с регулярным выражением: Открывать теги RegEx, за исключением автономных тегов XHTML
Ответ 6
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
Ответ 7
Я столкнулся с строкой, в которой были смешения английского, русского и некоторых других алфавитов, что вызвало исключение. Мне нужен только русский и английский, и сейчас это работает для меня:
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
Ответ 8
В то время как решение Nakilon работает, по крайней мере, до того, как пройти мимо ошибки, в моем случае у меня был этот странный f-ed-персонаж, полученный из Microsoft Excel, преобразованный в CSV, который регистрировался в рубине как (получить) кириллицу K, который в рубине был выделен жирным шрифтом K. Чтобы исправить это, я использовал iso-8859-1. CSV.parse(f, :encoding => "iso-8859-1")
, что превратило мою причудливую деактирующую кириллицу K в гораздо более управляемую /\xCA/
, которую я мог бы удалить с помощью string.gsub!(/\xCA/, '')
Ответ 9
Это работает:
def sanitize_utf8(string)
return nil if string.nil?
return string if string.valid_encoding?
string.chars.select { |c| c.valid_encoding? }.join
end
Ответ 10
Прежде чем использовать scan
, убедитесь, что заголовок требуемой страницы Content-Type
text/html
, так как могут быть ссылки на такие вещи, как изображения, которые не кодируются в UTF-8. Страница также может быть не-html, если вы выбрали href
в чем-то вроде элемента <link>
. Как это проверить, зависит от того, какую библиотеку HTTP вы используете. Затем убедитесь, что результатом является только ascii с String#ascii_only?
(а не UTF-8, потому что HTML должен использоваться только ascii, объекты могут использоваться иначе). Если оба этих теста пройдут, безопасно использовать scan
.
Ответ 11
Если вы не "заботитесь" о данных, вы можете просто сделать что-то вроде:
search_params = params[:search].valid_encoding? ? params[:search].gsub(/\W+/, '') : "nothing"
Я просто использовал valid_encoding?
, чтобы передать его. Mine - поле поиска, и поэтому я снова и снова обнаруживал ту же странность, поэтому я использовал что-то вроде: просто чтобы система не сломалась. Поскольку я не контролирую работу пользователя, чтобы авторизовать перед отправкой этой информации (например, автоответчик, чтобы сказать "манекен!" ), Я могу просто взять его, снять и вернуть пустые результаты.