Подтвердить что ты не робот

Эквивалент Iconv.conv( "UTF-8//IGNORE",...) в Ruby 1.9.X?

Я читаю данные из удаленного источника и иногда получаю некоторые символы в другой кодировке. Они не важны.

Я хотел бы получить строку "best guess" utf-8 и игнорировать недопустимые данные.

Основная цель - получить строку, которую я могу использовать, и не запускать ошибки, такие как:

  • Кодирование:: UndefinedConversionError: "\ xFF" из ASCII-8BIT в UTF-8:
  • неверная последовательность байтов в utf-8
4b9b3361

Ответ 1

Я думал, что это было:

string.encode("UTF-8", :invalid => :replace, :undef => :replace, :replace => "?")

заменит всех известных на "?".

Чтобы игнорировать все неизвестные, :replace => '':

string.encode("UTF-8", :invalid => :replace, :undef => :replace, :replace => "")

Edit:

Я не уверен, что это надёжно. Я перешел в параноидальный режим и использовал:

string.encode("UTF-8", ...).force_encoding('UTF-8')

Script, похоже, работает, сейчас. Но я уверен, что раньше я ошибся.

Изменить 2:

Даже при этом я продолжаю получать прерывистые ошибки. Не каждый раз, заметьте. Просто иногда.

Ответ 2

Строка # chars или Строка # each_char.

# Table 3-8. Use of U+FFFD in UTF-8 Conversion
# http://www.unicode.org/versions/Unicode6.2.0/ch03.pdf)
str = "\x61"+"\xF1\x80\x80"+"\xE1\x80"+"\xC2"
     +"\x62"+"\x80"+"\x63"+"\x80"+"\xBF"+"\x64"

p [
  'abcd' == str.chars.collect { |c| (c.valid_encoding?) ? c : '' }.join,
  'abcd' == str.each_char.map { |c| (c.valid_encoding?) ? c : '' }.join
]

String # scrub можно использовать с Ruby 2.1.

p [
  'abcd' == str.scrub(''),
  'abcd' == str.scrub{ |c| '' }
]

Ответ 3

Это отлично работает для меня:

"String".encode("UTF-8", :invalid => :replace, :undef => :replace, :replace => "").force_encoding('UTF-8')

Ответ 4

Чтобы игнорировать все неизвестные части строки, которые не корректно кодируются в кодировке UTF-8, следующее (как вы уже писали) почти делает то, что вы хотите.

string.encode("UTF-8", :invalid => :replace, :undef => :replace, :replace => "")

Предостережение заключается в том, что кодирование ничего не делает, если он считает, что строка уже является UTF-8. Поэтому вам нужно изменить кодировку, пройдя через кодировку, которая все еще может кодировать полный набор символов Unicode, которые UTF-8 может кодировать. (Если вы этого не сделаете, вы повредите любые символы, которые не входят в эту кодировку, - 7-битный ASCII был бы очень плохим выбором!) Итак, перейдите через UTF-16:

string.encode('UTF-16', :invalid => :replace, :replace => '').encode('UTF-8')

Ответ 5

С некоторой помощью от @masakielastic я решил эту проблему для своих личных целей, используя метод #chars.

Трюк состоит в том, чтобы разбивать каждый символ на свой отдельный блок, чтобы рубин мог сбой.

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

позволяющий коду провалиться где-то в процессе, кажется, лучший способ переместиться через него. Пока вы включаете эти сбои в блоках, вы можете захватить то, что читается с помощью только частей, содержащих только UTF-8 ruby ​​

Ответ 6

Мне не повезло с однострочным использованием String # encode ala string.encode("UTF-8", :invalid => :replace, :undef => :replace, :replace => "?") . Не работайте надежно для меня.

Но я написал чистую рубиновую "засыпку" скраба String # в MRI 1.9 или 2.0 или любой другой рубин, который не предлагает скраб String #.

https://github.com/jrochkind/scrub_rb

Это делает скраб String # доступным в рубинах, у которого его нет; если он загружен в MRI 2.1, он ничего не сделает, и вы все равно будете использовать встроенный скраб String #, чтобы он мог легко писать код, который будет работать на любой из этих платформ.

Эта реализация несколько похожа на некоторые из других решений char -by- char, предложенных в других ответах, но не использует исключения для управления потоком (не делают этого), проверена и обеспечивает API, совместимый с MRI 2.1 String # scrub