ruby 1.9: séquence d'octets invalide en UTF-8

j'écris un crawler dans Ruby (1.9) qui consomme beaucoup de HTML à partir de beaucoup de sites aléatoires.

En essayant d'extraire des liens, j'ai décidé d'utiliser .scan(/href="(.*?)"/i) au lieu de nokogiri/hpricot (speedup majeur). Le problème est que je reçois beaucoup de " invalid byte sequence in UTF-8 erreurs".

D'après ce que j'ai compris, la bibliothèque net/http n'a pas d'options spécifiques d'encodage et la substance qui entre n'est pas correctement étiquetée.

Quelle serait la meilleure façon de travailler avec les données entrantes? J'ai essayé .encode avec les options remplacer et invalider définies, mais aucun succès jusqu'à présent...

102
demandé sur user2333073 2010-06-06 04:35:34

11 réponses

dans Ruby 1.9.3 il est possible d'utiliser la chaîne.Encoder pour "ignorer" les séquences UTF-8 invalides. Voici un extrait qui fonctionnera à la fois en 1.8 ( iconv ) et 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

ou si vous avez des entrées vraiment gênantes, vous pouvez faire une double conversion de UTF-8 en UTF-16 et revenir à 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
168
répondu ecerulm 2013-11-21 18:32:41

la réponse acceptée ou l'autre réponse ne fonctionne pas pour moi. J'ai trouvé ce post qui suggérait

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

cela a réglé le problème pour moi.

72
répondu Amir Raminfar 2016-01-21 21:30:20

ma solution actuelle est d'exécuter:

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

Cela permettra au moins de se débarrasser des exceptions qui était mon principal problème

23
répondu Marc Seeger 2012-01-13 20:44:54

essayez ceci:

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
6
répondu Ranjithkumar Ravi 2014-05-12 13:45:08

je vous recommande D'utiliser un analyseur HTML. Il suffit de trouver le plus rapide.

L'analyse HTML n'est pas aussi facile qu'il n'y paraît.

les navigateurs analysent les séquences UTF-8 invalides, dans les documents HTML UTF-8, en mettant simplement le symbole"". Ainsi, une fois que la séquence UTF-8 invalide dans le HTML est analysée, le texte résultant est une chaîne valide.

même à l'intérieur des valeurs d'attribut vous devez décoder les entités HTML comme amp

ici est une grande question qui résume pourquoi vous ne pouvez pas analyser de façon fiable HTML avec une expression régulière: RegEx match ouvert à l'exception des balises XHTML autonome tags

4
répondu Eduardo 2017-05-23 12:34:30
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
répondu rusllonrails 2014-07-24 09:16:05

j'ai rencontré la corde, qui a eu des mélanges de l'anglais, le russe et quelques autres alphabets, qui a causé l'exception. Je n'ai besoin que du russe et de l'anglais, et cela fonctionne actuellement pour moi:

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
2
répondu Nakilon 2012-01-08 13:51:34

alors que la solution de Nakilon fonctionne, au moins aussi loin que passer l'erreur, dans mon cas, j'ai eu ce caractère bizarre f-ed up provenant de Microsoft Excel converti en CSV qui s'enregistrait dans ruby comme un (get this) cyrillic K qui dans ruby était un K en caractères gras.pour corriger cela, j'ai utilisé 'iso-8859-1' viz. CSV.parse(f, :encoding => "iso-8859-1") , qui a transformé mes k's deaky deaky en un /\xCA/ beaucoup plus gérable, que je pourrais alors enlever avec string.gsub!(/\xCA/, '')

1
répondu boulder_ruby 2012-10-16 03:53:22

cela semble fonctionner:

def sanitize_utf8(string)
  return nil if string.nil?
  return string if string.valid_encoding?
  string.chars.select { |c| c.valid_encoding? }.join
end
1
répondu Spajus 2013-05-15 12:41:51

avant d'utiliser scan , assurez-vous que l'en-tête Content-Type de la page demandée est text/html , car il peut y avoir des liens vers des choses comme des images qui ne sont pas encodées dans UTF-8. La page pourrait aussi être non-html si vous avez trouvé un href dans quelque chose comme un élément <link> . La façon de vérifier cela varie selon la bibliothèque HTTP que vous utilisez. Ensuite, assurez-vous que le résultat est seulement ascii avec String#ascii_only? (pas UTF-8 parce que HTML est seulement censé utiliser ascii, entities peut être utilisé autrement). Si ces deux tests sont positifs, il est sans danger d'utiliser scan .

0
répondu Adrian 2010-06-06 00:45:59

si vous ne vous "souciez" pas des données, vous pouvez juste faire quelque chose comme:

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

je viens d'utiliser valid_encoding? pour passer. Le mien est un champ de recherche, et donc je trouvais la même bizarrerie encore et encore donc j'ai utilisé quelque chose comme: juste pour avoir le système ne casse pas. Puisque je ne contrôle pas l'expérience utilisateur d'autovalidate avant d'envoyer cette information (comme auto feedback pour dire " dummy up!") Je peux juste la prendre dans, bande de sortir et retour vide résultats.

-1
répondu pjammer 2013-08-29 14:13:14