ruby 1.9:UTF-8の無効なバイトシーケンス


109

私はRuby(1.9)でクローラーを書いています。このクローラーは、多くのランダムサイトから大量のHTMLを消費します。
リンクを抽出するとき.scan(/href="(.*?)"/i)に、nokogiri / hpricot(大幅なスピードアップ)の代わりに使用することにしました。問題は、多くの " invalid byte sequence in UTF-8"エラーが発生することです。
私が理解したことから、net/httpライブラリにはエンコーディング固有のオプションがなく、入ってくるものは基本的に適切にタグ付けされていません。
その着信データを実際に処理する最良の方法は何でしょうか?.encode置換と無効なオプションを設定して試しましたが、今のところ成功していません...


文字を壊す可能性がありますが、他のライブラリに対して文字列を有効に保ちます:valid_string = untrusted_string.unpack( 'C *')。pack( 'U *')
Marc Seeger

正確な問題があるため、同じ他の解決策を試してみました。愛はない。マークスを試してみましたが、すべてが文字化けしているようです。よろしいです'U*'元に戻しますか'C*'
ジョーダンフェルドスタイン、

いいえ、それはしません:)私は、それをあちこちにあるセンテンスよりもクラッシュしないようにサードパーティのライブラリが気になるウェブクローラーで単にそれを使用しました。
Marc Seeger

回答:


172

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

3
問題のある入力では、UTF-8からUTF-16への二重変換とその後UTF-8への二重変換も使用します file_contents.encode!('UTF-16', 'UTF-8', :invalid => :replace, :replace => '') file_contents.encode!('UTF-8', 'UTF-16')
RubenLaguna

7
のオプションもありますforce_encoding。ISO8859-1をUTF-8として読み取った場合(つまり、その文字列に無効なUTF-8が含まれている場合)、_ string.force_encoding( "ISO8859-1"を使用してISO8859-1として「再解釈」)して、そのまま機能します。その文字列を実際のエンコーディングで使用します。
RubenLaguna

3
そのダブルエンコードのトリックは私のベーコンを救ったばかりです!なぜそれが必要なのかしら?
johnf

1
それらの行はどこに置くべきですか?
Lefsler 2012

5
二重変換はエンコーディング変換を強制するため(そしてそれを使用して無効な文字をチェックするため)機能すると思います。ソース文字列がすでにUTF-8でエンコードされている場合、呼び出し.encode('UTF-8')は何もせず、チェックは実行されません。エンコードのRubyコアドキュメント。ただし、まずUTF-16に変換すると、無効なバイトシーケンスのすべてのチェックが強制的に実行され、必要に応じて置換が行われます。
Jo Hund 2013

79

受け入れられた答えも他の答えも私にとってはうまくいきます。提案したこの投稿を見つけました

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

これで問題が解決しました。


1
これで問題が解決し、非推奨ではないメソッドを使用するのが好きです(現在、Ruby 2.0を使用しています)。
La-comadreja 14

1
これだけが機能します!上記のソリューションをすべて試しましたが、「fdsfdsf dfsf sfds fs sdf <div> hello <p> fooo ??? {!@#$%^&*()_ +} < / p> </ div> \ xEF \ xBF \ xBD \ xef \ xbf \ x9c <div> \ xc2 \ x90 </ div> \ xc2 \ x90 "
Chihung Yu

1
「バイナリ」の2番目の引数は何ですか?
Henley Chiu

24

私の現在の解決策は実行することです:

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

これは少なくとも私の主な問題であった例外を取り除くでしょう


3
私はこの方法を組み合わせて使用​​してvalid_encoding?います。これは、何かが間違っているときに検出されるようです。val.unpack('C*').pack('U*') if !val.valid_encoding?
アーロンヒブラルター2012年

これは私のために働いた。私の\xB0背中を度の記号に変換しました。でもvalid_encoding?本当は戻ってきますが、私はまだそうでないかチェックし、上のアミールの答えを使用して問題のあるキャラクターを取り除きます:string.encode!('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '')。私もforce_encodingルートを試しましたが、失敗しました。
ハムスター2014

これは素晴らしい。ありがとう。
d_ethier

8

これを試して:

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

私の場合はベストアンサー!ありがとう
Aldo

4

HTMLパーサーを使用することをお勧めします。最速のものを見つけてください。

HTMLの解析は、見かけほど簡単ではありません。

ブラウザは、UTF-8 HTMLドキュメント内の無効なUTF-8シーケンスを解析し、「 」記号を挿入するだけです。したがって、HTMLの無効なUTF-8シーケンスが解析されると、結果のテキストは有効な文字列になります。

属性値の内部でも、ampなどのHTMLエンティティをデコードする必要があります

正規表現でHTMLを確実に解析できない理由をまとめた素晴らしい質問を次に示します。RegExは、 XHTML自己完結型タグを除くオープンタグと一致します。


2
正規表現は約10倍高速であり、HTMLを正しく解析したくないがリンクを抽出したいだけなので、そのまま維持したいと思います。ok_string = bad_string.encode( "UTF-8"、{:invalid =>:replace、:undef =>:replace})を実行するだけで、ルビの無効な部分を置き換えることができるはずですが、そうではないようです仕事:(
マーク・シーガー、

3

これはうまくいくようです:

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

2

英語、ロシア語、その他のアルファベットが混在する文字列に遭遇したため、例外が発生しました。私はロシア語と英語だけを必要とし、これは現在私のために働きます:

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

Nakilonの解決策は機能しますが、少なくともエラーを回避する限り、私の場合、Microsoft Excelから発生した奇妙なf-uped文字をCSVに変換し、それを(これを取得する)キリル文字Kとして登録しましたルビーは太字のKでした。これを修正するには、「iso-8859-1」を使用しました。CSV.parse(f, :encoding => "iso-8859-1")、これははるかに管理に私の気紛れdeakyキリル文字のKさんになって/\xCA/、私は、その後に削除可能性があり、string.gsub!(/\xCA/, '')


繰り返しますが、Nakilonの(およびその他の)修正はキリル文字(ハハ)に由来するキリル文字に対するものでしたが、この出力はxlsから変換されたcsvの標準出力です!
boulder_ruby 2012年

0

を使用する前にscan、要求されたページのContent-Typeヘッダーがtext/htmlであることを確認してください。UTF-8でエンコードされていない画像などへのリンクが存在する可能性があるためです。要素のhrefようなものを取得した場合、ページは非HTMLになる可能性もあります<link>。これを確認する方法は、使用しているHTTPライブラリによって異なります。次に、結果がasciiのみであることを確認しますString#ascii_only?(HTMLはasciiを使用することしか想定されていないため、UTF-8ではなく、エンティティはそれ以外で使用できます)。これらのテストの両方に合格した場合、使用しても安全scanです。


ありがとう、しかしそれは私の問題ではありません:)私はとにかくURLのホスト部分のみを抽出し、フロントページのみをヒットします。私の問題は、私の入力が明らかにUTF-8ではなく、1.9エンコーディングfooが問題になっていることです
Marc Seeger

@Marc Seeger:「私の入力」とはどういう意味ですか?標準入力、URL、またはページ本文?
エイドリアン

HTMLはUTF-8でエンコードできます:en.wikipedia.org/wiki/Character_encodings_in_HTML
Eduardo

私の入力=ページ本文@Eduardo:わかっています。私の問題は、net / httpからのデータが時々悪いエンコーディングを持っているように見えることです
Marc Seeger

実際にWebページのエンコーディングが悪いのは珍しいことではありません。応答ヘッダーは、それが1つのエンコーディングであると言っているかもしれませんが、実際には別のエンコーディングを提供しています。
サンケンシティ、2012年

-1

データを「気にしない」場合は、次のようにすることができます。

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

valid_encoding?合格したばかりだった。私は検索フィールドなので、同じ奇妙さを何度も見つけたので、システムが壊れないようにするために、次のようなものを使用しました。この情報を送信する前にユーザーエクスペリエンスを自動検証するように制御していないため(自動フィードバックで「ダミーアップ!」と言うなど)、それを取り込んで取り除き、空の結果を返すことができます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.