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

Как избавиться от символов не-ascii в рубине

У меня есть Ruby CGI (не рельсы), который выбирает фотографии и титры из веб-формы. Мои пользователи очень заинтересованы в использовании смарт-кавычек и лигатур, они вставляются из других источников. Мое веб-приложение не очень хорошо справляется с этими символами, отличными от ASCII, существует ли процедура рутинной манипуляции с Ruby, которая может избавиться от символов, отличных от ASCII?

4b9b3361

Ответ 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 (это также позволяет легко загружать загружаемые файлы в ведра, которые сканируются очень быстро, так как они никогда не имеют слишком большого количества записей).