《Lua程序设计》--学习6

日期和时间

第1种表示方式是一个数字,这个数字通常是一个整型数。尽管并非是ISO C所必需的,但在大多数系统中这个数字是自一个被称为纪元(epoch)的固定日期后至今的秒数。

Lua语言针对日期和时间提供的第2种表示方式是一个表。日期表(date table)具有以下几个重要的字段:year、month、day、hour、min、sec、wday、yday和isdst,除isdst以外的所有字段均为整型数。前6个字段的含义非常明显,而wday字段表示本周中的第几天(第1天为星期天);yday字段表示当年中的第几天(第1天是1月1日);isdst字段表示布尔类型,如果使用夏时令则为真

函数os.time

不带任何参数调用函数os.time,会以数字形式返回当前的日期和时间:

 返回的数字是上文中提到的纪元时间开始的秒数,具体日期需要根据这个计算

如果以一个日期表作为参数调用函数os.time,那么该函数会返回该表中所描述日期和时间对应的数字。year、month和day字段是必需的,hour、min和sec字段如果没有提供的话则默认为12:00:00,其余字段(包括wday和yday)则会被忽略

 函数os.date

函数os.date在一定程度上是函数os.time的反函数(尽管这个函数的名字写的是date),它可以将一个表示日期和时间的数字转换为某些高级的表示形式,要么是日期表要么是字符串。该函数的第1个参数是描述期望表示形式的格式化字符串(format string),第2个参数是数字形式的日期和时间(如果不提供,则默认为当前日期和时间)。

 对于其他格式化字符串,函数os.date会将日期格式化为一个字符串,该字符串是根据指定的时间和日期信息对特定的指示符进行了替换的结果。所有的指示符都以百分号开头紧跟一个字母

 日期和时间处理

当函数os.date创建日期表时,该表的所有字段均在有效的范围内。当我们给函数os.time传入一个日期表时,其中的字段并不需要归一化。

 函数os.difftime用来计算两个时间之间的差值,该函数以秒为单位返回两个指定数字形式表示的时间的差值。

通过归一化,可以很容易地将用秒表示的时间转换为合法的数字形式表示的时间,即我们可以创建一个带有开始时刻的日期表并将日期表中的秒数设置为想要转换的数字。

 位和字节

位运算

位运算符只能用于整型数。位运算符包括&(按位与)、|(按位或)、~(按位异或)、>>(逻辑右移)、<<(逻辑左移)和一元运算符~(按位取反)

 所有的位运算都针对构成一个整型数的所有位,也就是64位

要操作32位整型数也不难。除了右移操作外,只要忽略高32位,那么所有针对64位整型数的操作与针对32位整型数的操作都一样。这对于加法、减法和乘法都有效。因此,在操作32位整型数时,只需要在进行右移前抹去高32位即可

Lua提供逻辑移位(即0填充的移位),不提供使用符号位填充的  算数右移

我们可以通过向下取整除法(floor除法),除以合适的2的整数次幂来实现算术右移(例如,x//16与算术右移4位等价)

移位数是负数表示向相反的方向移位,即a>>n与a<<-n等价

 如果移位数等于或大于整型表示的位数(标准Lua为64位,精简Lua为32位),由于所有的位都被从结果中移出了,所以结果是0

无符号整型数

Lua语言不显式支持无符号整型数。不过尽管如此,只要稍加注意,在Lua语言中处理无符号整型数并不难

默认情况下,打印数值时是将其作为有符号整型数进行处理的。我们可以使用选项%u或%x在函数string.format中指定以无符号整型数进行输出:

 上面的数字是 3 >> 62

根据有符号整型数的表示方式(2的补码),加法、减法和乘法操作对于有符号整型数和无符号整型数是一样

关系运算符对于无符号和有符号数是不等价的,因为会被当作有符号数来看待

无符号除法

 第一个比较(d<0)等价于比较d是否大于2的63次方。如果大于,那么商只能是1(如果n等于或大于d)或0。否则,我们使被除数除以2,然后除以除数,再把结果乘以2。右移1位等价于除以2的无符号除法,其结果是一个非负有符号整型数。后续的左移则纠正了商,还原了之前的除法。

总体上说,floor(floor(n/2)/d)*2(算法进行的计算)与floor(((n/2)/d)*2)(正确的结果)并不等价。不过,要证明它们之间最多相差1并不困难。因此,算法计算了余数(变量r),然后判断余数是否比除数大,如果余数比除数大则纠正商(加1)即可。

无符号整型数和浮点型数之间的转换需要进行一些调整。要把一个无符号整型数转换为浮点型数,可以先将其转换成有符号整型数,然后通过取模运算纠正结果

 由于标准转换把u当作有符号整型数,因此表达式u+0.0的值是-6917529027641081856,而之后的取模操作会把这个值限制在有符号整型数的表示范围内(在实际的代码中,由于涉及浮点型数的取模运算肯定会进行类型转换,所以并不需要进行这次加法运算)

要把一个浮点型数转换为无符号整型数,可以使用如下的代码

加法把一个大于2^63的数转换为一个大于2^64的数,取模运算把这个数限制到[0,2^63)范围内,然后通过减法把结果变成一个“负”值(即最高位置位的值)。对于小于2^63的值,加法结果小于2^64,所以取模运算没有任何效果,之后的减法则把它恢复到了之前的值

打包和解包二进制数据

在二进制数和基本类型值(数值和字符串类型)之间进行转换的函数。函数string.pack会把值“打包(pack)”为二进制字符串,而函数string.unpack则从字符串中提取这些值。

函数string.pack和函数string.unpack的第1个参数是格式化字符串,用于描述如何打包数据。格式化字符串中的每个字母都描述了如何打包/解包一个值,例如:

 调用函数string.pack将创建一个字符串,其中为3个整型数的二进制代码(根据"iii"),每一个"i"编码对与之对应的参数进行了编码,而字符串的长度则是一个整型数本身大小的3倍(在笔者的机器上是3×4字节)。调用函数string.unpack对给定字符串中的3个整型数进行了解码(还是根据"iii")并返回解码后的结果。

为了便于迭代,函数string.unpack还会返回最后一个读取的元素在字符串中的位置(这解释了上例中的13)。相应地,该函数还有一个可选的第3个参数,这个参数用于指定开始读取的位置。例如,下例输出了一个指定字符串中所有被打包的字符串:

 对于编码一个整型数而言有几种选项,每一种对应了一种整型大小:b(char)、h(short)、i(int)、l(long)和j(代表Lua语言中整型数的大小)。要是使用固定的、与机器无关的大小,可以在选项i后加上一个1~16的数。例如,i7会产生7字节的整型数。所有的大小都会被检查是否存在溢出的情况

 二进制文件