Эквивалент Iconv.conv( "UTF-8//IGNORE",...) в Ruby 1.9.X?
Я читаю данные из удаленного источника и иногда получаю некоторые символы в другой кодировке. Они не важны.
Я хотел бы получить строку "best guess" utf-8 и игнорировать недопустимые данные.
Основная цель - получить строку, которую я могу использовать, и не запускать ошибки, такие как:
- Кодирование:: UndefinedConversionError: "\ xFF" из ASCII-8BIT в UTF-8:
- неверная последовательность байтов в utf-8
Ответы
Ответ 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