JS Int8Array编码

JavaScript09

JS Int8Array编码,第1张

TCP/IP开发中,出现需要自己编码的情况,这种情况需要将变量拓展为自己需要的byte[]表现形式。

假设我们需要编码格式如下:

1111 1111 ... 1111 1111 1111 1111 ...

第一个8位,表示文件名的长度,紧接着是文件名,然后用一个32位表示文件的长度,最后是文件数据。

这里用js中的Int8Array完成编码。Int8Array是一种TypedArray,本质上是一个数组,特点是每个元素的长度为8位(一个byte)。

假设filenameArr为文件名数组,fileArr为文件数组:

那么可以想到,4个字节的数组在java中相当于一个int,它可以表示的最大值就是2,147,483,647。我们这里就需要手动计算每个字节的值,不足32位的,需要将高位全部置成0。

采取的方法是从高到低,每次计算8位,然后放入数组

fileLengthArr[0] = fileArr.length >>24 &0xFF

这里的计算方式是先把要放入的值右移24位,再截取后8位,得到其高8位的值,比如文件长度是1,895,825,407个字节,那么其二进制表示就是

01110000111111111111111111111111

右移24位得到

0000 0000 0000 0000 0000 0000 1000 1111

跟0xFF,也就是1111 1111 做与运算,就得到

1000 1111

再比如文件长度是16,777,215个字节,那么其二进制表示就是

1111 1111 1111 1111 1111 1111

只用了24位就表示完了,那么右移24位,我们会得到

0000 0000 0000 0000 0000 0000 0000 0000

做与运算,得到

0000 0000

可以看到符合我们的需求,也就是不足32位的,高位用0补齐。

依次类推,依次计算每个8位的值:

再把这个数组拼接到结果中:

result = result.concat(fileLengthArr).concat(fileArr)

如果要用WebSocket传输,那么要将这个array转换成ArrayBuffer:

var resultBuffer =new Int8Array(result).buffer

用上面的例子,如果文件名长度是255,文件长度是65535,那么result应该是这样的:

[255, ...(255个元素)...0,0,255,255,...(65535个元素)]

解码时,先取一个字节(8位),作为文件名的长度,然后向后取255个元素,转换成文件名,再取4个字节,作为文件的长度,再向后取65535个字节,存储成文件。

总结:

这里学到了几个基本概念,首先一个byte[],字节数组,里面的每个元素都是8位,刚好对应一个字节,方便存取。其次是位移的概念,右移24位相当于取一个32位的值的高8位,&0xFF运算相当于只保留这个值的低8位(一个F(hex)=1111(bin))。这里用到的编码格式是一种较为粗糙的格式,长度位能表现的最大值固定,且位数固定,效率不是最高的。但基本理念要厘清,就是在编码时,我们是按照每8位依次计算并存入数组的。

ava的数据类型分为三大类,即布尔型、字符型和数值型,而其中数值型又分为整型和浮点型;相对于数据类型,Java的变量类型为布尔型boolean;字符型char;整型byte、short、int、long;浮点型float、double。其中四种整型变量和两种浮点型变量分别对应于不同的精度和范围。此外,我们还经常用到两种类变量,即String和Date。对于这些变量类型之间的相互转换在我们编程中经常要用到,下面是几种相互转化的关系:

一、 整型、实型、字符型变量中的相互转换

在Java中整型、实型、字符型被视为同一类数据,这些类型由低级到高级分别为(byte,short,char)——int——long——float——double,低级变量可以直接转换为高级变量,例如,下面的语句可以在Java中直接通过:

byte b

int i=b

而将高级变量转换为低级变量时,情况会复杂一些,你可以使用强制类型转换。即你必须采用下面这种语句格式:

int i

byte b=(byte)i

可以想象,这种转换肯定可能会导致溢出或精度的下降,因此我们并不推荐使用这种转换。

二、Java的包装类

在我们讨论其它变量类型之间的相互转换时,我们需要了解一下Java的包装类,所谓包装类,就是可以直接将简单类型的变量表示为一个类,在执行变量类型的相互转换时,我们会大量使用这些包装类。Java共有六个包装类,分别是Boolean、Character、Integer、Long、Float和Double,从字面上我们就可以看出它们分别对应于 boolean、char、int、long、float和double。而String和Date本身就是类。所以也就不存在什么包装类的概念了。

三、简单类型变量和包装类之间的相互转换

简单类型的变量转换为相应的包装类,可以利用包装类的构造函数。即:

Boolean(boolean value)、Character(char value)、Integer(int value)、Long(long value)、Float(float value)、Double(double value)

而在各个包装类中,总有形为××Value()的方法,来得到其对应的简单类型数据。利用这种方法,也可以实现不同数值型变量间的转换,例如,对于一个双精度实型类,intValue()可以得到其对应的整型变量,而doubleValue()可以得到其对应的双精度实型变量。

四、String类和其它数据类型的相互转换

对于上面的这些包装类,除了Character以外,都有可以直接使用字符串参数的构造函数,这也就使得我们将String类转换为这些数据类型变得相当之简单,即:

Boolean(String s)、Integer(String s)、Long(String s)、Float(String s)、Double(String s)

而将String类转换为Date类也可以使用这样的构造函数:Date(String s)

现在我们还剩下一个字符型变量,事实上String类可以理解为一个char型数组,因此我们可以在String类中找到这样的方法来实现这种转换:charAt(int index)可以得到String类中某一位置上的字符,toCharArray()更可以将整个String类转换成一个char的数组。

对于所有的包装类都存在一个名为toString()的方法可以将其转换成对应的String类,而对于整型类和长整型类,还可以使用toBinaryString(int i)、toHexString(int i)、toOctalString(int i)分别以二进制、十六进制和八进制的形式进行到String类的转换。

五、将字符型直接做为数值转换为其它数据类型

将字符型变量转换为数值型变量实际上有两种对应关系,在我们在第一部分所说的那种转换中,实际上是将其转换成对应的ASCII码,但是我们有时还需要另一种转换关系,例如,‘1’就是指的数值1,而不是其ASCII码,对于这种转换,我们可以使用Character的getNumericValue(char ch)方法。

六、Date类与其它数据类型的相互转换

整型和Date类之间并不存在直接的对应关系,只是你可以使用int型为分别表示年、月、日、时、分、秒,这样就在两者之间建立了一个对应关系,在作这种转换时,你可以使用Date类构造函数的三种形式:

Date(int year, int month, int date):以int型表示年、月、日

Date(int year, int month, int date, int hrs, int min):以int型表示年、月、日、时、分

Date(int year, int month, int date, int hrs, int min, int sec):以int型表示年、月、日、时、分、秒

在长整型和Date类之间有一个很有趣的对应关系,就是将一个时间表示为距离格林尼治标准时间1970年1月1日0时0分0秒的毫秒数。对于这种对应关系,Date类也有其相应的构造函数:Date(long date)

获取Date类中的年、月、日、时、分、秒以及星期你可以使用Date类的getYear()、getMonth()、getDate()、getHours()、getMinutes()、getSeconds()、getDay()方法,你也可以将其理解为将Date类转换成int。

而Date类的getTime()方法可以得到我们前面所说的一个时间对应的长整型数,与包装类一样,Date类也有一个toString()方法可以将其转换为String类。

解决方法是在输入后放一层过滤器转码,输出时在放一个另一个过滤器,这样,每当有其他移植时,直接改变过滤器的代码就可以了。

过滤器代码如下

public static String SyConvertCode(String tempSql){

if (tempSql==null)

tempSql=""

else

tempSql = tempSql.trim()

String returnString = tempSql

try{

// byte[] ascii=returnString.getBytes("GBK")

// returnString =new String(ascii,"ISO-8859-1")

byte[] ascii=returnString.getBytes("ISO-8859-1")

returnString =new String(ascii,"GBK")

}catch(Exception e){

e.printStackTrace()

}

return returnString

}