纵看 Ruby 1.8 -> 2.5 中字符串的演变

yufei       6 年, 1 月 前       948

Ruby 是一个完全面向对象的语言。对于字符串来说,它们背后的类是 String。我们今天要介绍的,就是这个 String 类。Ruby 从 1.8 发展到今天的 Ruby 2.5,这个类发生了很大的变化。

因此,本文的目的是详细说明每个主要版本发生的主要更改。

从 Ruby 1.8 到 Ruby 1.9

Ruby 1.8 来说,那个时候还不存在 ancestors 祖先链属性,只有 include_modules 属性

这个属性的内容如下

String.included_modules # => [String, Enumerable, Comparable, Object, Kernel]

到了 Ruby 1.9 中,有了祖先链属性,属性内容如下

String.ancestors # => [String, Comparable, Object, Kernel, BasicObject]

从继承链来说,就是抛弃了 Enumerable 添加了 BasicObject

我们再来看看它们之间方法的区别

首先是 Ruby 1.8 ,要获取字符串所有的实例方法,可以使用下面的语句

ims_18 = String.instance_methods(false).map(&:to_sym)

其次是 Ruby 1.9,可以使用下面的语句获取字符串所有的实例方法

ims_19 = String.instance_methods(false)

然后,我们就可以通过 ims_19 - ims_18 来获得差异方法

diff = ims19 - ims18

diff # => [:===, :clear, :chr, :getbyte, :setbyte, :byteslice, :codepoints, :prepend, :ord, :each_codepoint, :encoding, :force_encoding, :valid_encoding?, :ascii_only?, :encode, :encode!, :to_r, :to_c]

可以看到,Ruby 1.9 中,为字符串 String 类添加了很多方法,大多数方法都用于处理字节。

发生这样的改变,原因很重要。那就是: Ruby 1.8 中,字符串是一个字节序列,ascii 字符序列,而在 Ruby 1.9 中字符串代码点( codepoint ) 序列

什么叫做 「 代码点序列 」 呢?

Ruby 1.9 中,每一个字符串对象都有自己的字符编码,字符串字面量总是以定义它的源代码文件的字符编码来编码的。

因此,对于每一个字符串,源文件的编码决定了它的编码,也决定了它是什么编码序列。对于 ASCII 字符,那么还是原来的字节序列,但对于 GBK 和 UTF8 ,那么就是 GBK 序列和 UTF8 序列。

比如下面这段代码

#!/usr/bin/env ruby
# coding: utf-8
str = "中文"
sym = :name
regex = Regexp.new(str.encode("GBK"))
puts str.encoding
puts sym.encoding
puts regex.encoding

运行的结果如下

[root@www.twle.cn ruby] ruby demo.rb
UTF-8
US-ASCII
GBK

虽然每个字符串对象看起来都有自己的编码,但在硬盘上,它们都是一字节序列来存储的。编码只是指定如何获取这些字节并将它们转换为代码点。

因为这种变更,从 Ruby 1.9 开始,Ruby 本身可以处理字符串编码,而在 1.8 及之前的版本中需要 iconv 库来完成这项工作。

也因为这个原因,在 Ruby 1.9 中不推荐使用 iconv 库,因为它已经被废弃了。

注意: 每个字符串的默认编码是二进制,也成为字节序列。

Ruby 1.9 到 Ruby 2.0

Ruby 2.0 中,最重要的变更,就是确定使用 UTF-8 作为文件的默认编码,也是唯一的编码。这也间接造成了字符串对象的默认编码是 UTF-8。

而在 Ruby 1.9 及之前的版本中,默认的编码是二进制 ( Binary ) ,也就是字节序列。

这种行为与使用 UTF16 作为默认编码的 Java 有点类似。

同时需要注意的是,Ruby 2.0 中,iconv 库已经完全删除,再也不是自带的一个库了。

Ruby 2.0 到 Ruby 2.1

虽然 Ruby 2.0 中使用 UTF-8 作为默认编码,但是,将已经编码的字符串编码为相同的字符串(例如 UTF8UTF8 )会导致无操作 ( no-op )

比如在 Ruby 1.9 及之前的版本中,字符串编码的行为如下

# in ruby <= 2.0.x
content =  "Is your pl\xFFace available?".force_encoding("UTF-8")
content.encode("UTF-8", invalid: :replace) # => "Is your pl\xFFace available?"

但在 Ruby 2.1.x 中,字符串的编码行为则变得诡异了

# in ruby 2.1.x
content =  "Is your pl\xFFace available?".force_encoding("UTF-8")
content.encode("UTF-8", invalid: :replace) # => "Is your pl�ace available?"

在这里我们可以看到,在 Ruby 2.0 中,我们在 UTF8 中显式编码的 UTF8 字符串返回字符串而不替换未知的代码点。所以 invalid: :replace 操作是被忽略了的。

而在 Ruby 2.1 中, invalid: :replace 操作是被处理的,默认字符 替换了序列中的每个无效代码点。

Ruby 2.1 到 2.5

从 Ruby 2.1 到 Ruby 2.5,String 类的大多数变更都体现在提高性能上。但,仍然有两个主要的功能值得讲解下。

  1. 一个是 Ruby 2.3 中新增的 frozen_string_literal: true 魔法注释 frozen_string_literal: true 注释用于告诉 Ruby 解释器,所有的字符串都是不可变的。

    因为不可变,对于相同的字符串,也就没必要重新创建一个新的对象

    # frozen_string_literal: true
    
    hash = { 'key' => 'value' }
    hash['key'] # 这里并不会为 'key' 字符串创建一个新的实例
    
  2. Ruby 2.4 中的非 ASCII 字符串的大小写转换 这个,不多说了,直接看代码就好了

    'Türkiye'.upcase          # => "TÜRKIYE" - Full Unicode case mapping
    'Türkiye'.upcase :ascii   # => "TüRKIYE" - ASCII only 
    'Türkiye'.upcase :turkic  # => "TÜRKİYE" - Full Unicode case mapping, adapted for Turkic languages
    "Ačiū".upcase :lithuanian # => "AČIŪ"    - Full Unicode case mapping, adapted for Lithuanian
    
目前尚无回复
简单教程 = 简单教程,简单编程
简单教程 是一个关于技术和学习的地方
现在注册
已注册用户请 登入
关于   |   FAQ   |   我们的愿景   |   广告投放   |  博客

  简单教程,简单编程 - IT 入门首选站

Copyright © 2013-2022 简单教程 twle.cn All Rights Reserved.