it-swarm.com.de

ruby 1.9: ungültige Bytefolge in UTF-8

Ich schreibe einen Crawler in Ruby (1.9), der viel HTML von vielen zufälligen Websites verbraucht.
Beim Versuch, Links zu extrahieren, entschied ich mich, .scan(/href="(.*?)"/i) anstelle von nokogiri/hpricot (major speedup) zu verwenden. Das Problem ist, dass ich jetzt viele "invalid byte sequence in UTF-8" -Fehler bekomme.
Soweit ich verstanden habe, hat die net/http-Bibliothek keine spezifischen Optionen für die Kodierung, und das, was kommt, ist grundsätzlich nicht richtig markiert.
Was wäre der beste Weg, um tatsächlich mit den eingehenden Daten zu arbeiten? Ich habe .encode mit den Ersetzen und ungültigen Optionen versucht, aber bisher kein Erfolg ...

106
Marc Seeger

In Ruby 1.9.3 ist es möglich, String.encode zu verwenden, um die ungültigen UTF-8-Sequenzen zu "ignorieren". Hier ist ein Ausschnitt, der sowohl in 1.8 ( iconv ) als auch in 1.9 ( String # encode ) funktioniert:

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

oder wenn Sie wirklich störende Eingaben haben, können Sie eine doppelte Konvertierung von UTF-8 nach UTF-16 und zurück nach UTF-8 durchführen:

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
170
ecerulm

Die akzeptierte Antwort noch die andere Antwort funktioniert für mich. Ich habe diesen Beitrag der vorgeschlagen 

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

Dies hat das Problem für mich behoben. 

77
Amir Raminfar

Meine aktuelle Lösung ist zu laufen: 

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

Dies wird zumindest die Ausnahmen beseitigen, die mein Hauptproblem waren

23
Marc Seeger

Versuche dies:

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
8

Ich empfehle Ihnen, einen HTML-Parser zu verwenden. Finden Sie einfach den schnellsten.

Das Analysieren von HTML ist nicht so einfach, wie es scheint.

Browser analysieren ungültige UTF-8-Sequenzen in UTF-8-HTML-Dokumenten, indem sie das " " -Symbol einfügen. Sobald die ungültige UTF-8-Sequenz im HTML-Code analysiert wurde, ist der resultierende Text eine gültige Zeichenfolge.

Selbst innerhalb von Attributwerten müssen Sie HTML-Entitäten wie amp dekodieren

Hier ist eine großartige Frage, die zusammenfasst, warum Sie HTML mit einem regulären Ausdruck nicht zuverlässig analysieren können: RegEx stimmen mit offenen Tags überein, außer in XHTML enthaltenen Tags .

4
Eduardo

Das scheint zu funktionieren:

def sanitize_utf8(string)
  return nil if string.nil?
  return string if string.valid_encoding?
  string.chars.select { |c| c.valid_encoding? }.join
end
3
Spajus
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
rusllonrails

Ich habe String gefunden, bei dem es eine Mischung aus Englisch, Russisch und einigen anderen Alphabeten gab. Ich brauche nur Russisch und Englisch, und das funktioniert derzeit für mich:

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
Nakilon

Während die Lösung von Nakilon funktioniert, zumindest so weit, dass sie den Fehler überwindet, hatte ich in meinem Fall dieses seltsam aufgebaute Zeichen, das aus Microsoft Excel stammte, in CSV konvertiert, das sich in Ruby als kyrillischer K-Code ( Ruby war ein mutiges K. Um das zu beheben, habe ich 'iso-8859-1' verwendet. CSV.parse(f, :encoding => "iso-8859-1"), was aus meinen ausgeflippten deaky kyrillischen K's einen viel handhabbareren /\xCA/ machte, den ich dann mit string.gsub!(/\xCA/, '') entfernen konnte.

1
boulder_ruby

Stellen Sie vor der Verwendung von scan sicher, dass der Content-Type-Header der angeforderten Seite text/html ist, da es Verknüpfungen zu Bildern wie Bildern geben kann, die nicht in UTF-8 kodiert sind. Die Seite könnte auch nicht-html sein, wenn Sie eine href in etwas wie einem <link>-Element gefunden haben. Wie Sie dies überprüfen können, hängt von der verwendeten HTTP-Bibliothek ab. Stellen Sie dann sicher, dass das Ergebnis nur mit String#ascii_only? (nicht UTF-8) erstellt wird, da HTML nur ascii verwenden soll. Entitäten können ansonsten verwendet werden. Wenn beide Tests erfolgreich sind, können Sie scan verwenden.

0
Adrian