Node.js中的缓冲区比较

时间:2012-12-09 18:18:21

标签: node.js

我是Node.js的新手。没有Buffer比较,我应该使用像buffertools这样的模块来实现这些功能。

但是当我比较纯节点中的Buffer对象时,我发现了一个非常奇怪的行为。

> var b1 = new Buffer([170]);
> var b2 = new Buffer([171]);
> b1
<Buffer aa>
> b2
<Buffer ab>
> b1 < b2
false
> b1 > b2
false
> b1 == b2
false

> var b1 = new Buffer([10]);
> var b2 = new Buffer([14]);
> b1
<Buffer 0a>
> b2
<Buffer 0e>
> b1 > b2
false
> b1 < b2
true
> b1 == b2
false

引擎盖下究竟发生了什么?

2 个答案:

答案 0 :(得分:39)

这就是比较运算符在对象上的工作方式:

var a = {}, b = {};
a === b; //false
a == b; //false
a > b; //false
a < b; //false

var c = { valueOf : function () { return 0; } };
var d = { valueOf : function () { return 1; } };
c === d; //false
c == d; //false
c > d; //false
c < d; //true

引擎盖下

(有点)

第1部分:平等

这是最简单的部分。抽象相等(==spec)和严格相等(===spec)都会检查您是否引用相同的对象(比较引用)。在这种情况下,它们显然不是,所以他们的答案是false==规范第10步,===规范第7步。

因此,在这两种情况下:

b1 == b2 //false
b1 === b2 //false

第2部分:比较反击

这是有趣的部分。让我们看一下关系运算符(<>are defined的关系。让我们在两种情况下跟随调用链。

x = b1 //<Buffer aa>
y = b2 //<Buffer ab>

//11.8.5 The Abstract Relational Comparison Algorithm (http://es5.github.com/#x11.8.5)
Let px be the result of calling ToPrimitive(x, hint Number).
Let py be the result of calling ToPrimitive(y, hint Number).

//9.1 ToPrimitive (http://es5.github.com/#x9.1)
InputType is Object, therefore we call the internal [[DefaultValue]] method with hint Number.

//8.12.8 [[DefaultValue]] (hint) http://es5.github.com/#x8.12.8
We try and fetch the object's toString method. If it's defined, call it.

在这里我们达到了高潮:什么是缓冲区的toString方法?答案位于node.js内部深处。如果你愿意,have at it。我们可以通过实验轻松找到:

> b1.toString()
'�'
> b2.toString()
'�'
好吧,那没用。你会注意到在抽象关系比较算法(<的一个很大的名字)中,有一个处理字符串的步骤。它只是将它们转换为它们的数值 - 字符代码。我们这样做:

> b1.toString().charCodeAt(0)
65533
> b2.toString().charCodeAt(0)
65533

65533是一个重要的数字。它是两个方格的总和:142^2 + 213^2。它也恰好是Unicode替换字符,这个字符表示“我不知道发生了什么”。这就是为什么它的十六进制等价物是FFFD。

显然,65533 === 65533,所以:

b1 < b2 //is
b1.toString().charCodeAt(0) < b2.toString().charCodeAt(0) //is
65533 < 65533 //false
b1 > b2 //following same logic as above, false

就是这样。

老兄,到底是怎么回事?

好吧,这一定令人困惑,因为我的解释工作还没有经过深思熟虑。回顾一下,发生了什么:

  1. 您创建了一个缓冲区。 Benjamin Gruenbaum通过执行以下操作帮助我重新创建了测试用例:

    var b1 = new Buffer([170]), b2 = new Buffer([171]);

  2. 输出到控制台时,值会变为十六进制等效值(请参阅Buffer#inspect):

    170..toString(16) === 'aa'

    171..toString(16) === 'ab'

  3. 然而,在内部,它们代表无效字符(因为它不是十六进制编码;再次,你可以自由钻研实现细节,我不会(讽刺))。因此,当转换为字符串时,它们用Unicode替换字符表示。

  4. 由于它们是不同的对象,因此任何相等运算符都将返回false

  5. 然而,由于小于和大于工作的方式,它们被转换为字符串(然后转换为数字)以进行比较。根据第3点,这是相同的值;因此,它们不能小于或大于彼此,导致false

  6. 最后,只是为了笑容:

    b1 <= b2 //true
    b1 >= b2 //true
    

答案 1 :(得分:8)

已经有一个已经接受的答案,但我认为我可能仍然会发表评论,因为我没有发现接受的答案特别明确或有帮助。如果只是因为它回答OP没有提出的问题,那甚至是不正确的。所以,让我们把它煮沸:

> var b1 = new Buffer([170]);
> var b2 = new Buffer([171]);
> b1 < b2
> b1 > b2
> b1 == b2

所要求的只是:“我如何在缓冲区上执行等效性并且小于/大于比较(a.k.a.(总)排序)”。

答案是:

  • 通过逐步执行两个缓冲区的所有字节来手动执行,并在相应的字节之间执行比较,例如: b1[ idx ] === b2[ idx ]

  • 或使用Buffer.compare( b1, b2 ),它会为您提供-10+1之一,具体取决于第一个缓冲区之前是否排序,完全相同,或者在第二个之后(排序包含缓冲区的列表d就像d.sort( Buffer.compare ))一样简单。

观察我在第一个例子中使用===;我经常对本网站上有关JavaScript中==滥用的评论应该非常清楚为什么会这样。