出于某种原因,我在unicode字符的范围比较中得到了意想不到的结果。
总结一下,在我最小化的测试代码中,("\u1000".."\u1200") === "\u1100"
为false
,我希望它是true
- 而针对"\u1001"
的相同测试是{ {1}}如预期的那样。我觉得这完全不可理解。 true
运算符的结果也很有趣 - 它们与<
相矛盾。
以下代码是一个很好的最小例证:
===
我天真地期望这两个# encoding: utf-8
require 'pp'
a = "\u1000"
b = "\u1200"
r = (a..b)
x = "\u1001"
y = "\u1100"
pp a, b, r, x, y
puts "a < x = #{a < x}"
puts "b > x = #{b > x}"
puts "a < y = #{a < y}"
puts "b > y = #{b > y}"
puts "r === x = #{r === x}"
puts "r === y = #{r === y}"
操作都会产生“真”。但是,运行此程序的实际输出是:
===
有人可以启发我吗?
(注意我在Mac OS X上的1.9.3,我明确地将编码设置为utf-8。)
答案 0 :(得分:3)
ACTION: 我已将此行为提交为bug #6258 to ruby-lang。
这个字符范围内的整理顺序有些奇怪
irb(main):081:0> r.to_a.last.ord.to_s(16)
=> "1036"
irb(main):082:0> r.to_a.last.succ.ord.to_s(16)
=> "1000"
irb(main):083:0> r.min.ord.to_s(16)
=> "1000"
irb(main):084:0> r.max.ord.to_s(16)
=> "1200"
范围的最小值和最大值是输入的预期值,但如果我们将范围转换为数组,则最后一个元素是“\ u1036”,它的后继是“\ u1000”。在封面下,Range#===必须枚举String#succ序列,而不是最小和最大的简单绑定检查。
如果我们查看Range#===的来源(点击切换),我们会看到它发送到Range#include?。范围#包括哪些内容? source显示字符串的特殊处理 - 如果可以仅通过字符串长度来确定答案,或者所有被调用的字符串都是ASCII,我们会得到简单的边界检查,否则我们会调度到super,这意味着#include? Enumerable#include?使用Range#each进行枚举,该String#upto使用String#succ进行枚举,该字符串再次对U+1036进行特殊处理,并使用{{3}}进行枚举。
当字符串包含is_alpha或is_digit数字({{3}}不应该为true)时,字符串#succ有一堆特殊处理,否则它会使用enc_succ_char
递增最终字符。此时我失去了踪迹,但可能会使用与字符串关联的编码和校对信息计算后继者。
r = (a.ord..b.ord)
r === x.ord
r === y.ord
答案 1 :(得分:2)
看起来Range并不意味着我们认为它意味着什么。
我认为正在发生的是你创建的是一个试图包含字母,数字和标点符号的范围。 Ruby无法做到这一点,并没有“理解”你本质上需要一组代码点。
这导致Range#to_a方法崩溃:
("\u1000".."\u1099").to_a.size #=> 55
("\u1100".."\u1199").to_a.size #=> 154
("\u1200".."\u1299").to_a.size #=> 73
zinger是你把所有三个放在一起的时候:
("\u1000".."\u1299").to_a.size #=> 55
Ruby 1.8.7按预期工作 - 正如Matt在评论中指出的那样,“\ u1000”只是文字“u1000”,因为没有Unicode。
字符串#succ C源代码不仅返回下一个代码点:
Returns the successor to <i>str</i>. The successor is calculated by
incrementing characters starting from the rightmost alphanumeric (or
the rightmost character if there are no alphanumerics) in the
string. Incrementing a digit always results in another digit, and
incrementing a letter results in another letter of the same case.
Incrementing nonalphanumerics uses the underlying character set's
collating sequence.
Range正在采取与下一个,下一个,下一个不同的事情。
具有这些字符的范围确实是ACSII序列:
('8'..'A').to_a
=> ["8", "9", ":", ";", "<", "=", ">", "?", "@", "A"]
但使用#succ完全不同:
'8'.succ
=> '9'
'9'.succ
=> '10' # if we were in a Range.to_a, this would be ":"