У меня есть Ruby CGI (не рельсы), который выбирает фотографии и титры из веб-формы. Мои пользователи очень заинтересованы в использовании смарт-кавычек и лигатур, они вставляются из других источников. Мое веб-приложение не очень хорошо справляется с этими символами, отличными от ASCII, существует ли процедура рутинной манипуляции с Ruby, которая может избавиться от символов, отличных от ASCII?
Как избавиться от символов не-ascii в рубине
Ответ 1
Использовать кодировку String #
Официальным способом преобразования между строковыми кодировками с 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. Первый "ë" тоже. Когда Ruby видит это во время этого преобразования, он отбрасывает их.
Но второй "ë" - это два кодовых пункта: простое "e" , как и в строке ASCII, а затем "сочетание диакритической метки" (этот), что означает "положить умлаут на предыдущий символ". В строке 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"
Update
Некоторые из них сообщили о проблемах с опцией :universal_newline
. Я видел это с перерывами, но не смог отследить причину.
Когда это произойдет, я вижу Encoding::ConverterNotFoundError: code converter not found (universal_newline)
. Однако после некоторых обновлений RVM я просто запускал script выше в следующих версиях Ruby без проблем:
- рубин-1.9.2-P290
- рубин-1.9.3-P125
- рубин-1.9.3-Р194
- рубин-1.9.3-P362
- рубиново-2.0.0-preview2
- ruby-head (по состоянию на 12-31-2012)
Учитывая это, он не является устаревшей функцией или даже ошибкой в Ruby. Если кто-нибудь знает причину, прокомментируйте.
Ответ 2
class String
def remove_non_ascii(replacement="")
self.gsub(/[\u0080-\u00ff]/, replacement)
end
end
Ответ 3
Здесь мое предложение с использованием Iconv.
class String
def remove_non_ascii
require 'iconv'
Iconv.conv('ASCII//IGNORE', 'UTF8', self)
end
end
Ответ 4
С некоторой помощью от @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
Ответ 5
class String
def strip_control_characters
self.chars.reject { |char| char.ascii_only? and (char.ord < 32 or char.ord == 127) }.join
end
end
Ответ 6
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
Ответ 7
Нет никаких ограничений на удаление всех символов рядом с базовыми (что рекомендуется выше). Лучшее slution будет правильно обрабатывать эти имена (так как большинство файловых систем сегодня не имеют проблем с именами Unicode). Если ваши пользователи вставляют лигатуры, они, конечно, захотят вернуть их обратно. Если ваша файловая система является вашей проблемой, отвлеките ее и установите имя файла на какой-то md5 (это также позволяет легко загружать загружаемые файлы в ведра, которые сканируются очень быстро, так как они никогда не имеют слишком большого количества записей).