如何摆脱ruby中的非ascii字符
我有一个Ruby CGI(不是rails),它从Web表单中select照片和标题。 我的用户非常热衷于使用智能报价和连字,他们正在从其他来源粘贴。 我的networking应用程序不能很好地处理这些非ASCII字符,是否有一个快速的Rubystring操作例程,可以摆脱非ASCII字符?
使用String#编码
从Ruby 1.9开始,在string编码之间进行转换的官方方式是使用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”,就像你在ASCIIstring中find的,然后是一个“组合变音符号”( 这个符号),意思是“在前一个字符上放置一个变音符号”。 在Unicodestring中,它们被解释为单个“字形”或可见字符。 当转换这个时,Ruby保持纯粹的ASCII“e”并且丢弃合并标记。
如果您决定要提供一些特定的replace值,可以这样做:
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
选项的问题。 我间断地看到了这个,但一直没有find原因。
当它发生时,我看到Encoding::ConverterNotFoundError: code converter not found (universal_newline)
。 但是,在一些RVM更新之后,我刚才在下面的Ruby版本下运行了上面的脚本,没有任何问题:
- ruby1.9.2-P290
- ruby1.9.3-P125
- ruby1.9.3-P194
- ruby1.9.3-P362
- ruby2.0.0 preview2
- ruby头(截至2012年12月31日)
鉴于此,它似乎不是一个被弃用的function,甚至在Ruby中的错误。 如果有人知道原因,请评论。
class String def remove_non_ascii(replacement="") self.gsub(/[\u0080-\u00ff]/, replacement) end end
这是我使用Iconv的build议。
class String def remove_non_ascii require 'iconv' Iconv.conv('ASCII//IGNORE', 'UTF8', self) end end
从@masakielastic的一些帮助我已经解决了这个问题,为了我个人的目的使用#chars方法。
诀窍是把每个字符分解成独立的块, 这样ruby可以失败 。
当Ruby遇到二进制代码时,它需要失败。如果你不允许ruby继续前进,并且遇到了这个问题,那么就会遇到困难。 所以我使用String#chars方法将给定的string分解成一个字符数组。 然后,我把这个代码传递给一个消毒方法,允许代码在string中有“microfailures”(我的货币)。
所以,给一个“脏”的string,让我们说你使用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
class String def strip_control_characters self.chars.reject { |char| char.ascii_only? and (char.ord < 32 or char.ord == 127) }.join end end
快速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
不,除了基本的字符之外,并没有删除所有的字符(这是上面推荐的)。 最好的办法是正确处理这些名字(因为今天的大多数文件系统在Unicode名称上没有任何问题)。 如果你的用户粘贴连字,他们肯定也会想把他们弄回来。 如果文件系统是你的问题,把它抽象出来,并设置文件名为一些md5(这也使您可以很容易地分片上传到桶扫描非常快,因为他们从来没有太多的条目)。