Эквивалент Iconv.conv ("UTF-8 // IGNORE", ...) в Ruby 1.9.X? - PullRequest
12 голосов
/ 24 октября 2011

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

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

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

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

Ответы [ 6 ]

15 голосов
/ 24 октября 2011

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

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

Сценарий, кажется, работает, теперь хорошо. Но я почти уверен, что раньше получал ошибки с этим.

Редактировать 2:

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

3 голосов
/ 08 июня 2013

String # chars или String # 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 может использоваться с Ruby2.1.

p [
  'abcd' == str.scrub(''),
  'abcd' == str.scrub{ |c| '' }
]
2 голосов
/ 23 января 2014

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

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

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

string.encode('UTF-16', :invalid => :replace, :replace => '').encode('UTF-8')
2 голосов
/ 16 марта 2012

Это прекрасно работает для меня:

"String".encode("UTF-8", :invalid => :replace, :undef => :replace, :replace => "").force_encoding('UTF-8')
0 голосов
/ 29 октября 2014

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

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

https://github.com/jrochkind/scrub_rb

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

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

0 голосов
/ 28 октября 2013

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

Хитрость заключается в том, чтобы разбить каждый символ на свой отдельный отдельный блок , чтобы 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

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

...