26

ある種の入力から読み取った文字列があります。

私の知る限りでは、UTF8です。わかった:

string.force_encoding("utf8")

しかし、この文字列に実際には正当なUTF8ではないバイトが含まれている場合は、今すぐ知り、アクションを実行したいと思います。

通常、force_encoding( "utf8")は、そのようなバイトに遭遇した場合に発生しますか?そうはならないと思います。

#encodeを実行している場合は、ソースエンコーディング(またはデスティネーションエンコーディング)で無効な文字を処理するための便利なオプションから選択できます。

しかし、私は#encodeを実行しているのではなく、#force_encodingを実行しています。そのようなオプションはありません。

それは理にかなっていますか

string.force_encoding("utf8").encode("utf8")

すぐに例外を取得するには?通常、utf8からutf8のエンコードは意味がありません。しかし、おそらくこれは、無効なバイトがある場合にすぐにレイズさせる方法ですか?または、:replaceオプションなどを使用して、無効なバイトで別のことを行いますか?

しかし、いや、それもうまくいくようには思えない。

誰か知ってる?

1.9.3-p0 :032 > a = "bad: \xc3\x28 okay".force_encoding("utf-8")
=> "bad: \xC3( okay"
1.9.3-p0 :033 > a.valid_encoding?
=> false

わかりましたが、これらの不良バイトを見つけて削除するにはどうすればよいですか?奇妙なことに、これは発生しません:

1.9.3-p0 :035 > a.encode("utf-8")
 => "bad: \xC3( okay"

私が別のエンコーディングに変換しているとしたら、そうなるでしょう!

1.9.3-p0 :039 > a.encode("ISO-8859-1")
Encoding::InvalidByteSequenceError: "\xC3" followed by "(" on UTF-8

または、私がそれを言った場合、それは「?」に置き換えられます =>

1.9.3-p0 :040 > a.encode("ISO-8859-1", :invalid => :replace)
=> "bad: ?( okay"

したがって、rubyは、utf-8の不良バイトを認識し、別のエンコーディングに変換するときにemを別のものに置き換えることができます。しかし、別のエンコーディングに変換したくないので、utf8のままにしておきたいのですが、そこに無効なバイトがある場合はレイズしたい場合や、無効なバイトを置換文字に置き換えたい場合があります。

これを行うためにルビーを取得する方法はありませんか?

更新これは2.1で最終的にrubyに追加され、これを行うために2.1プレビューリリースにString#scrubが存在すると思います。だからそれを探してください!

4

9 に答える 9

16

(更新: https://github.com/jrochkind/scrub_rbを参照)

だから私はここで必要なものへのソリューションをコーディングしました: https://github.com/jrochkind/ensure_valid_encoding/blob/master/lib/ensure_valid_encoding.rb

しかし、これが実際に stdlib に組み込まれていることに気付いたのはごく最近のことです。直感に反して、「バイナリ」を「ソースエンコーディング」として渡す必要があるだけです。

a = "bad: \xc3\x28 okay".force_encoding("utf-8")
a.encode("utf-8", "binary", :undef => :replace)
=> "bad: �( okay"

ええ、それはまさに私が欲しかったものです。つまり、この IS は 1.9 stdlib に組み込まれていることがわかりました。これは文書化されておらず、それを知っている人はほとんどいません (または、英語を話す人で知っている人はほとんどいないのでしょうか?)。どこかのブログでこのような議論をしていたのを見たので、他の誰かがそれを知っていました!

于 2013-03-12T01:49:46.840 に答える
6

Ruby 2.1 では、stdlib は最終的にこれをサポートしscrubます。

http://ruby-doc.org/core-2.1.0/String.html#method-i-scrub

于 2014-06-10T12:06:13.607 に答える
4

スクリプトファイル自体が UTF8 として保存されていることを確認し、次のことを試してください。

# encoding: UTF-8
p [a = "bad: \xc3\x28 okay", a.valid_encoding?]
p [a.force_encoding("utf-8"), a.valid_encoding?]
p [a.encode!("ISO-8859-1", :invalid => :replace), a.valid_encoding?]

これにより、私のwindows7システムでは次のようになります

["bad: \xC3( okay", false]
["bad: \xC3( okay", false]
["bad: ?( okay", true]

あなたの悪い文字が置き換えられるので、次のようにすぐに行うことができます

a = "bad: \xc3\x28 okay".encode!("ISO-8859-1", :invalid => :replace)
=> "bad: ?( okay"

編集:ここでは、任意のエンコーディングで機能するソリューションです。最初は不良文字のみをエンコードし、2番目は?に置き換えます。

def validate_encoding(str)
  str.chars.collect do |c| 
    (c.valid_encoding?) ? c:c.encode!(Encoding.locale_charmap, :invalid => :replace)
  end.join 
end

def validate_encoding2(str)
  str.chars.collect do |c| 
    (c.valid_encoding?) ? c:'?'
  end.join 
end

a = "bad: \xc3\x28 okay"

puts validate_encoding(a)                  #=>bad: ?( okay
puts validate_encoding(a).valid_encoding?  #=>true


puts validate_encoding2(a)                  #=>bad: ?( okay
puts validate_encoding2(a).valid_encoding?  #=>true
于 2012-04-18T11:28:59.490 に答える
0

「実際の」ユースケースでこれを行っている場合-たとえば、ユーザーが入力したさまざまな文字列を解析するためであり、多くのエンコーディングで作成できる完全にランダムなファイルを「デコード」できるようにするためだけではありませんあなたが望むように、少なくとも各文字列のすべての文字が同じエンコーディングを持っていると仮定できると思います。

では、この場合、あなたはこれについてどう思いますか?

strings = [ "UTF-8 string with some utf8 chars \xC3\xB2 \xC3\x93", 
             "ISO-8859-1 string with some iso-8859-1 chars \xE0 \xE8", "..." ]

strings.each { |s| 
    s.force_encoding "utf-8"
    if s.valid_encoding?
        next
    else
        while s.valid_encoding? == false 
                    s.force_encoding "ISO-8859-1"
                    s.force_encoding "..."
                end
        s.encode!("utf-8")
    end
}

私は決してRubyの「プロ」ではないので、私の解決策が間違っていたり、少し素朴でさえある場合はご容赦ください..

私は自分にできることを還元しようとしていますが、これが私がやってきたことです。私は研究プロジェクトのために、任意にエンコードされた文字列用のこの小さなパーサーに取り組んでいました (私はまだ取り組んでいます)。

これを投稿している間、完全にテストさえしていないことを認めなければなりません.. 「肯定的な」結果がいくつか得られただけですが、見つけるのに苦労していたものを見つけた可能性があることにとても興奮しました( SOでこれについて読むのに費やしたすべての時間..)できるだけ早く共有する必要があると感じた. 'これまで... .. 期待どおりに動作する場合:)

于 2013-03-11T01:35:10.500 に答える
0

Ruby 2.1+でよくある 2 つの状況とその対処方法を次に示します。質問はRuby v1.9に関するものですが、これはGoogle経由でこの質問を見つけた他の人にとって役立つかもしれません。

状況 1

UTF-8 文字列に無効なバイトがいくつか含まれている可能性があります。無効なバイトを
削除します。

str = "Partly valid\xE4 UTF-8 encoding: äöüß"

str.scrub('')
 # => "Partly valid UTF-8 encoding: äöüß"

状況 2

UTF-8 または ISO-8859-1 エンコーディングのいずれかの文字列がある場合、そのエンコーディングを
確認し、必要に応じて UTF-8 に変換します。

str = "String in ISO-8859-1 encoding: \xE4\xF6\xFC\xDF"

unless str.valid_encoding?
  str.encode!( 'UTF-8', 'ISO-8859-1', invalid: :replace, undef: :replace, replace: '?' )
end #unless
 # => "String in ISO-8859-1 encoding: äöüß"

ノート

  • 上記のコード スニペットは、RubyUTF-8がデフォルトですべての文字列をエンコードすることを前提としています。これはほとんど常に当てはまりますが、スクリプトを で開始することでこれを確認できます# encoding: UTF-8

  • UTF-8無効な場合、 (Ruby では、次を参照してください)のようなほとんどのマルチバイト エンコーディングをプログラムで検出できます#valid_encoding?。ただし、のようなシングルバイトエンコーディングの無効性をプログラムで検出することは (簡単に) 不可能ISO-8859-1です。したがって、上記のコード スニペットは逆方向には機能しません。つまり、文字列が有効なISO-8859-1エンコーディングかどうかを検出します。

  • UTF-8Web のデフォルト エンコーディングとしてますます人気が高まっていますが、西側諸国、特に北米ではISO-8859-1、他のフレーバーがまだ非常に人気があります。Latin1非常に似ているが、ISO-8859-1 とは少し異なるシングルバイト エンコーディングがいくつかあることに注意してください。例: CP1252(別名Windows-1252)、ISO-8859-15

于 2016-02-18T20:35:07.117 に答える
0

さて、これは私が自分で見つけた本当に不十分な純粋なルビーの方法です。それはおそらくがらくたのために実行します。どうした、ルビー?今のところ自分の答えを選択するのではなく、他の誰かが現れてもっと良いものを提供してくれることを願っています.

 # Pass in a string, will raise an Encoding::InvalidByteSequenceError
 # if it contains an invalid byte for it's encoding; otherwise
 # returns an equivalent string.
 #
 # OR, like String#encode, pass in option `:invalid => :replace`
 # to replace invalid bytes with a replacement string in the
 # returned string.  Pass in the
 # char you'd like with option `:replace`, or will, like String#encode
 # use the unicode replacement char if it thinks it's a unicode encoding,
 # else ascii '?'.
 #
 # in any case, method will raise, or return a new string
 # that is #valid_encoding?
 def validate_encoding(str, options = {})
   str.chars.collect do |c|
     if c.valid_encoding?
       c
     else
       unless options[:invalid] == :replace
         # it ought to be filled out with all the metadata
         # this exception usually has, but what a pain!
         raise  Encoding::InvalidByteSequenceError.new
       else
         options[:replace] || (
          # surely there's a better way to tell if
          # an encoding is a 'Unicode encoding form'
          # than this? What's wrong with you ruby 1.9?
          str.encoding.name.start_with?('UTF') ?
             "\uFFFD" :
             "?" )
       end
     end 
   end.join
 end

http://bibwild.wordpress.com/2012/04/17/checkingfixing-bad-bytes-in-ruby-1-9-char-encoding/でさらに怒鳴る

于 2012-04-18T02:37:28.403 に答える
0

私が考えることができる唯一のことは、往復で文字列を損傷しない何かにトランスコードして戻すことです:

string.force_encoding("UTF-8").encode("UTF-32LE").encode("UTF-8")

かなり無駄に思えますが。

于 2012-04-18T01:17:33.370 に答える
0

例外を引き起こす簡単な方法は次のようです。

untrusted_string.match /./

于 2013-11-09T11:57:42.287 に答える