web-dev-qa-db-ja.com

rubyで非ASCII文字を削除する方法

Webフォームから写真やキャプションを選択するRuby CGI(Railsではありません)。これらの非ASCII文字をうまく処理しません、非ASCII文字を取り除くことができる迅速なRuby文字列操作ルーチンはありますか?

60
Andre Garzia

String#encodeを使用

Ruby 1.9の時点で文字列エンコーディング間で変換する公式の方法は、 String#encode を使用することです。

非ASCII文字を単純に削除するには、次のようにします。

_some_ascii   = "abc"
some_unicode = "áëëçüñżλφθΩ????????"
more_ascii   = "123ABC"
invalid_byte = "\255"

non_ascii_string = [some_ascii, some_unicode, more_ascii, invalid_byte].join

# See String#encode documentation
encoding_options = {
  :invalid           => :replace,  # Replace invalid byte sequences
  :undef             => :replace,  # Replace anything not defined in ASCII
  :replace           => '',        # Use a blank for those replacements
  :universal_newline => true       # Always break lines with \n
}

ascii = non_ascii_string.encode(Encoding.find('ASCII'), encoding_options)
puts ascii.inspect
  # => "abce123ABC"
_

結果の最初の5文字は「abce1」であることに注意してください。「á」は破棄され、「ë」は破棄されますが、別の「ë」は「e」に変換されたようです。

これは、同じ文字をUnicodeで表現する方法が複数ある場合があるためです。 「á」は単一のUnicodeコードポイントです。最初の「ë」も同様です。この変換中にRubyがこれらを検出すると、それらを破棄します。

しかし、2番目の "ë"は2つのコードポイントです。ASCII文字列に続くように、普通の "e"に続いて "結合発音区別記号"( this one )、これは「前の文字にウムラウトを付ける」ことを意味します。Unicode文字列では、これらは単一の「書記素」または可視文字として解釈されます。これを変換するとき、Ruby =はプレーンASCII "e"を保持し、結合マークを破棄します。

特定の置換値を提供する場合は、次の操作を実行できます。

_REPLACEMENTS = { 
  'á' => "a",
  'ë' => 'e',
}

encoding_options = {
  :invalid   => :replace,     # Replace invalid byte sequences
  :replace => "",             # Use a blank for those replacements
  :universal_newline => true, # Always break lines with \n
  # For any character that isn't defined in ASCII, run this
  # code to find out how to replace it
  :fallback => lambda { |char|
    # If no replacement is specified, use an empty string
    REPLACEMENTS.fetch(char, "")
  },
}

ascii = non_ascii_string.encode(Encoding.find('ASCII'), encoding_options)
puts ascii.inspect
  #=> "abcaee123ABC"
_

更新

_:universal_newline_オプションの問題を報告している人もいます。私はこれを断続的に見ましたが、原因を突き止めることができませんでした。

それが起こると、Encoding::ConverterNotFoundError: code converter not found (universal_newline)が表示されます。ただし、RVMを更新した後、次のRubyバージョンで問題なく上記のスクリプトを実行しました。

  • Ruby-1.9.2-p290
  • Ruby-1.9.3-p125
  • Ruby-1.9.3-p194
  • Ruby-1.9.3-p362
  • Ruby-2.0.0-preview2
  • ルビーヘッド(2012年12月31日現在)

これを考えると、それは非推奨の機能やRubyのバグでさえないようです。誰かが原因を知っている場合は、コメントしてください。

136
Nathan Long

class String
 def remove_non_ascii(replacement="") 
   self.gsub(/[\u0080-\u00ff]/, replacement)
 end
end
39
klochner

Iconvを使用した私の提案は次のとおりです。

class String
  def remove_non_ascii
    require 'iconv'
    Iconv.conv('ASCII//IGNORE', 'UTF8', self)
  end
end
20
Scott
class String
  def strip_control_characters
    self.chars.reject { |char| char.ascii_only? and (char.ord < 32 or char.ord == 127) }.join
  end
end
2
Diego Carrion

@masakielasticの助けを借りて、個人的な目的で#charsメソッドを使用してこの問題を解決しました。

トリックは、各文字独自の個別ブロックに分解することです= Rubyは失敗する可能性があります

Rubyは、バイナリコードなどに直面したときに失敗する必要があります。Rubyが先に進み失敗することを許可しない場合このことに関しては難しい道です。String#charsメソッドを使用して、指定された文字列を文字の配列に分割します。次に、そのコードをサニタイズメソッドに渡します。コイン))文字列内。

したがって、「ダーティ」文字列が与えられた場合、File#read写真。 (私の場合)

dirty = File.open(filepath).read    
clean_chars = dirty.chars.select do |c|
  begin
    num_or_letter?(c)
  rescue ArgumentError
    next
  end
end
clean = clean_chars.join("")

def num_or_letter?(char)
  if char =~ /[a-zA-Z0-9]/
    true
  elsif char =~ Regexp.union(" ", ".", "?", "-", "+", "/", ",", "(", ")")
    true
  end
end
2
boulder_ruby

いいえ、基本的な文字以外のすべての文字を削除することもできます(上記をお勧めします)。最良の解決策は、これらの名前を適切に処理することです(現在のほとんどのファイルシステムにはUnicode名に関する問題がないため)。ユーザーが合字を貼り付けると、地獄でそれらを取り戻したいと思うでしょう。ファイルシステムが問題の場合は、ファイルシステムを抽象化し、ファイル名をmd5に設定します(これにより、エントリが多すぎないため、非常に迅速にスキャンするバケットに簡単に断片をアップロードできます)。

0
Julik

Quick GSは この議論 を明らかにしました。これは次の方法を示唆しています:

class String
  def remove_nonascii(replacement)
    n=self.split("")
    self.slice!(0..self.size)
    n.each { |b|
     if b[0].to_i< 33 || b[0].to_i>127 then
       self.concat(replacement)
     else
       self.concat(b)
     end
    }
    self.to_s
  end
end
0
Joseph Weissman