如何摆脱 ruby 中的非 ascii 字符

问题描述 投票:0回答:9

我有一个 Ruby CGI(不是 rails),可以从 Web 表单中挑选照片和标题。我的用户非常热衷于使用智能引号和连字,他们从其他来源粘贴。我的网络应用程序不能很好地处理这些非 ASCII 字符,是否有一个快速的 Ruby 字符串操作例程可以摆脱非 ASCII 字符?

ruby unicode cgi ascii
9个回答
157
投票

使用字符串#encode

从 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 中有多种表达同一个书面字符的方法。 “á”是一个单一的 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"

更新

有些人报告了

:universal_newline
选项的问题。我断断续续地看到过这种情况,但一直无法追查原因。

当它发生时,我看到

Encoding::ConverterNotFoundError: code converter not found (universal_newline)
。然而,在一些 RVM 更新之后,我刚刚在以下 Ruby 版本下运行上面的脚本没有问题:

  • ruby-1.9.2-p290
  • ruby-1.9.3-p125
  • ruby-1.9.3-p194
  • ruby-1.9.3-p362
  • ruby-2.0.0-preview2
  • 红宝石头(截至 12-31-2012)

鉴于此,它似乎不是弃用的功能,甚至不是 Ruby 中的错误。如果有人知道原因,请评论。


41
投票

1.9


class String
 def remove_non_ascii(replacement="") 
   self.gsub(/[\u0080-\uffff]/, replacement)
 end
end

1.8


class String
 def remove_non_ascii(replacement="") 
   self.gsub(/[\x80-\xff]/, replacement)
 end
end

21
投票

这是我使用 Iconv 的建议。

class String
  def remove_non_ascii
    require 'iconv'
    Iconv.conv('ASCII//IGNORE', 'UTF8', self)
  end
end

6
投票

如果你有积极的支持,你可以使用

I18n.transliterate

I18n.transliterate("áëëçüñżλφθΩ𠜎")
"aee?cunz?????"

或者如果你不想要问号...

I18n.transliterate("áëëçüñżλφθΩ𠜎", replacement: "")
"aeecunz"

请注意,这不会删除无效的字节序列,它只会替换非 ascii 字符。对于我的用例,这是我想要的,而且很简单。


2
投票

在@masakielastic 的帮助下,我使用#chars 方法出于个人目的解决了这个问题。

诀窍是将每个角色分解成自己单独的块这样ruby就可以失败

Ruby needs 在遇到二进制代码等时失败。如果你不允许 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

2
投票

这应该可以解决问题:

ascii_only_str = str.gsub(/[^[:ascii:]]/, '')

1
投票
class String
  def strip_control_characters
    self.chars.reject { |char| char.ascii_only? and (char.ord < 32 or char.ord == 127) }.join
  end
end

0
投票

Quick GS 揭示了 this discussion 建议以下方法:

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

0
投票

不,除了基本字符(上面推荐的)之外,还需要删除所有字符。最好的解决方案是正确处理这些名称(因为当今大多数文件系统对 Unicode 名称没有任何问题)。如果您的用户粘贴了连字,他们肯定也会想把它们取回来。如果文件系统是您的问题,请将其抽象化并将文件名设置为某个 md5(这也使您可以轻松地将上传内容分片到存储桶中,存储桶扫描速度非常快,因为它们永远不会有太多条目)。

© www.soinside.com 2019 - 2024. All rights reserved.