分享 Ruby 的字符串比较,一个天天用,但是没用对的知识点

hiveer · 2017年02月16日 · 最后由 nowherekai 回复于 2017年02月21日 · 2222 次阅读

在写 Ruby 代码的时候,经常会遇到需要比较字符串的场景。我相信遇到的最多的就是看两个字符串是否相等了

str1 == str2

这种情况只要长度和内容相等就会返回true

而有些时候遇到的情况比较特殊,像是这样的字符串“20170101”, “20170113”,或者是“2017-01-01”, “2017-01-13” 这些字符串一看就是代表的时间,而我们业务上也是需要比较出两个时间的大小。这个时候你可能会将字符串 parse 为Time或者是DateTime类型,然后再比较。 但是这比较麻烦,所以会考虑直接将字符串进行比较如何:

[42] pry(main)> "20170101" < "20170113"
=> true

[43] pry(main)> "2017-01-01" < "2017-01-13"
=> true

从结果来看这两个 case 都是正确的结果。但是背后的原理究竟是什么呢,其他的 case 是不是一样的 work 呢?

大家觉得下面这个 case 的结果如何?

"20121110" > "201210100"

也许会出乎你的预料,结果是true

[27] pry(main)> "20121110" > "20120110000000"
=> true

这个也是true。这是不是让你侥幸的认为他们就是在比较str.to_i之后的大小的心理受到了一万点伤害。

其实字符串比较,并没有对字符串做任何的转换,比较的就是字符串。但是除了比较长得一不一样,还有什么可比的呢? 长得像不像是你的眼睛做的比较,计算机比较的是字符编码

对于字符串的比较:

A: 如果长度一样,那么从第一个开始比较,当遇到不一样的字符时,哪个字符串对应的字符的编码值大,就谁大。

B: 如果长度不一样,并且较长字符串的前面部分跟短的字符串相同,那么算是长的字符串大。

C: 如果长度不一样,并且较长字符串的前面部分跟短的字符串不同,那么按照规则 A 比较。

所以回到比较两个时间的 case,你可以直接通过字符串比较,但是切记保证时间的格式一致,长度一致,否则得到的可能不是你想要的结果。

如有错误,欢迎指正!

这不就是按照字典顺序来比较大小吗?

一般比较字符串最好还是统一长度再比,不同长度比较没有啥意义。

#1 楼 @rudy 字典顺序指的是 ASCII 的编码顺序吗?

感觉各种语言的类似行为都是从 C 的 strcmp「继承」来的…

#2 楼 @hiveer 严格意义上不是,只是 ASCII 编码表里,字母的 ASCII 编码值相对大小刚好对应字母的字典序,这么一讲,好像也是,哈,我只是抠字眼而已。

#4 楼 @martin91 其实我并没有挖掘到源码的实现,只是从别人博客里看到了这个解释。不敢确信是对的,所以发出来希望被指正,然后就能写成 Note 了。

"计算机比较的是字符编码"是正确的,其实就是比较字符串在内存里面的 byte 而已,用的是 memcmp

int rb_str_cmp(VALUE str1, VALUE str2)
{
    long len1, len2;
    const char *ptr1, *ptr2;
    int retval;

    if (str1 == str2) return 0;
    RSTRING_GETMEM(str1, ptr1, len1);
    RSTRING_GETMEM(str2, ptr2, len2);
    if (ptr1 == ptr2 || (retval = memcmp(ptr1, ptr2, lesser(len1, len2))) == 0) {
    if (len1 == len2) {
        if (!rb_str_comparable(str1, str2)) {
        if (ENCODING_GET(str1) > ENCODING_GET(str2))
            return 1;
        return -1;
        }
        return 0;
    }
    if (len1 > len2) return 1;
    return -1;
    }
    if (retval > 0) return 1;
    return -1;
}

#6 楼 @nowherekai 喜欢这个回复,我自己没找到这个 C 的源码。你是哪儿找到的

#7 楼 @hiveer 我用 mac 上的 Dash 看<=>能看对应的 C 代码,还不够就是看 ruby 的source code 了

需要 登录 后方可回复, 如果你还没有账号请 注册新账号