我有一个Ruby CGI(不是rails),可以从网络表单中选择照片和标题。我的用户非常热衷于使用智能引号和连字,他们正在从其他来源粘贴。我的网络应用程序不能很好地处理这些非ASCII字符,是否有一个快速的Ruby字符串操作例程可以摆脱非ASCII字符?
答案 0 :(得分:133)
从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字符串中找到的那样,后面跟着一个“组合变音符号”(this one),这意味着“放”关于前一个角色的变音符号“。在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 :(得分:38)
class String
def remove_non_ascii(replacement="")
self.gsub(/[\u0080-\u00ff]/, replacement)
end
end
答案 2 :(得分:20)
这是我使用Iconv的建议。
class String
def remove_non_ascii
require 'iconv'
Iconv.conv('ASCII//IGNORE', 'UTF8', self)
end
end
答案 3 :(得分:2)
在@masakielastic的帮助下,我已经使用#chars方法为个人目的解决了这个问题。
诀窍是将每个角色分解为自己独立的块 ,以便ruby可以失败。
Ruby 需要在面对二进制代码等时失败。如果你不允许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
答案 4 :(得分:2)
class String
def strip_control_characters
self.chars.reject { |char| char.ascii_only? and (char.ord < 32 or char.ord == 127) }.join
end
end
答案 5 :(得分:1)
如果你有积极的支持,你可以使用 I18n.transliterate
I18n.transliterate("áëëçüñżλφθΩ?")
"aee?cunz?????"
或者如果你不想要问号...
I18n.transliterate("áëëçüñżλφθΩ?", replacement: "")
"aeecunz"
请注意,这不会删除无效的字节序列,它只会替换非 ascii 字符。对于我的用例,这正是我想要的,而且很简单。
答案 6 :(得分: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
答案 7 :(得分:0)
不能删除基本字符旁边的所有字符(上面推荐的)。最好的解决方案是正确处理这些名称(因为今天大多数文件系统都没有任何Unicode名称问题)。如果你的用户粘贴了连字,他们肯定会想要让他们回来。如果文件系统是您的问题,请将其抽象出来并将文件名设置为某个md5(这也允许您轻松地将上传文件分片到桶中,这些桶可以非常快速地扫描,因为它们从不会有太多条目)。