Ruby中Æ、Ø、Å字符问题:为何("A".."Å").to_a返回重复字母?
Ruby字符串范围遍历的特殊行为解析
这个问题其实戳中了Ruby处理字符串范围的一个核心设计逻辑,咱们掰开揉碎了说:
1. Ruby中Range#to_a的底层逻辑
Ruby的范围(Range)转换成数组时,是通过不断调用元素的succ方法来生成下一个元素,直到生成的元素超过范围上限为止。对于字符串来说,String#succ的行为并不是简单的ASCII码+1,而是做了特殊优化——纯字母组成的字符串会按照类似Excel列名的"进位"规则生成下一个字符串。
比如:
"Z".succ # 返回 "AA",而非ASCII中Z的下一个字符"[" "AZ".succ # 返回 "BA" "ZZ".succ # 返回 "AAA"
2. 为什么("A".."Z").to_a符合预期?
这个范围的上限是"Z",从"A"开始逐个调用succ,每一步都是单个字母的递增(A→B→C...→Z),当生成到"Z"时已经达到范围上限,所以直接返回26个大写字母的数组,不会触发进位逻辑。
3. 为什么("A".."Å").to_a会生成超长数组?
这里的关键是字典序比较规则和Å的字符编码:
- 在UTF-8编码中,大写字母"A"的编码是
U+0041(十进制65),"Z"是U+005A(十进制90),而Å是U+00C5(十进制197)——Å的编码远大于所有单个大写字母。 - 字典序比较字符串时,是从左到右逐字符比较:比如拿"AA"和
Å比,第一个字符"A"(65)小于Å(197),所以整个字符串"AA" < "Å";同理,"AB"、"AC"...直到"ZZ"、"AAA"这些多字母字符串,它们的第一个字符都是"A"-"Z",编码都小于Å,所以所有这些字符串的字典序都小于Å。
当Ruby遍历("A".."Å")时,会从"A"开始,不断调用succ生成后续字符串:A→B→...→Z→AA→AB→...→AZ→BA→...→ZZ→AAA→...,直到生成的字符串字典序大于Å才会停止——而这需要生成非常多的多字母字符串,所以你会看到数组里有一大串AA、AB开头的元素。
你可以在IRB里验证这个逻辑:
"AA" < "Å" # 返回 true "ZZ" < "Å" # 返回 true "Å" > "ZZZZ" # 返回 true
内容的提问来源于stack exchange,提问作者Peter Højlund Palluth
相关产品推荐
相关产品推荐

