Nim编程早茶
Nim 编程实现读写二进制比特流(01串、bits),也就是一个一个比特读写内容。
数据存储
Nim 语言中,数据最小存储单元是字节(bytes),1 个字节包含 8 个比特。无论我们读取文件或者写入文件,都必须以字节为基本单位。Nim 语言中,提供了 streams 标准库,供我们读写字节流。
按比特读写文件
所以说,如果我们希望按 bit 读取文件,我们必须一次读取一个字节,也就是 8 位,然后再对这一个字节中的每一位进行操作。
Nim 中为我们提供了 bitops 模块,供我们操作 bit。
我们可以先创建一个 bit 对象。其中 mark 表示目前处于当前字节的第几位,value 是我们读取的字节值。
type
Bit = object
mark: int
value: uint8
按位写入文件
writeBit 的 value 参数,是我们写入的 bit 类型。value 为 true,我们向当前位写入 1,反之写入 0。如果 mark 值小于 7,我们就按位填充数值;如果mark 值等于 7,我们就将字节写入文件,并清空 wbit 的数值。要注意,如果写入的比特流不是 8 的倍数,我们在最后一个比特流后面补齐零,然后再将该字节写入文件。
setBit 是 bitops 中的函数,用于将指定位置的比特置为 1。
# Nim 编程早茶
# https://tea.nim-cn.com/archives/
import bitops
var wBit = Bit(mark: 0, value: 0)
proc writeBit(s: Stream, value: bool) =
if wbit.mark < 7:
if value:
setBit(wbit.value, 7 - wbit.mark)
wbit.mark += 1
else:
if value:
setBit(wbit.value, 7 - wbit.mark)
s.write(wbit.value)
wbit.mark = 0
wbit.value = 0
# ......
if wBit.mark > 0:
s.write(wbit.value)
wbit.mark = 0
wbit.value = 0
按位读取文件
bitops 中为我们提供了 testBit 函数,用于测试当前位是r否为 1,若是,将返回 true。
由于写入 bit 的时候,我们有可能进行了补零操作,所以说我们需要提供文本的总长度,才能还原出二进制的原貌。
var rBit = Bit(mark: 0, value: 0)
proc readBit(s: Stream, length: int=0): string =
while not s.atEnd:
rBit.value = s.readUint8
for i in 0 ..< 8:
if testBit(rBit.value, 7 - i):
result &= "1"
else:
result &= "0"
if length != 0:
return result[0 ..< length]
我们还可以使用,strutils 模块中提供的 toBin 模块,直接将字节转变为二进制数的字符串形式。
import strutils
proc readBit(s: Stream, length: int=0): string =
while not s.atEnd:
rBit.value = s.readUint8
result &= toBin(int(rBit.value), 8)
if length != 0:
return result[0 ..< length]
最后,来看一个完整的例子
import bitops, streams, strutils
type
Bit = object
mark: int
value: uint8
var wBit = Bit(mark: 0, value: 0)
var rBit = Bit(mark: 0, value: 0)
proc writeBit(s: Stream, value: bool) =
if wbit.mark < 7:
if value:
setBit(wbit.value, 7 - wbit.mark)
wbit.mark += 1
else:
if value:
setBit(wbit.value, 7 - wbit.mark)
s.write(wbit.value)
wbit.mark = 0
wbit.value = 0
proc readBit(s: Stream, length: int=0): string =
while not s.atEnd:
rBit.value = s.readUint8
result &= toBin(int(rBit.value), 8)
if length != 0:
return result[0 ..< length]
# proc readBit(s: Stream, length: int=0): string =
# while not s.atEnd:
# rBit.value = s.readUint8
# for i in 0 ..< 8:
# if testBit(rBit.value, 7 - i):
# result &= "1"
# else:
# result &= "0"
# if length != 0:
# return result[0 ..< length]
when isMainModule:
let s = "010110101000101111"
var input = newFileStream("test_input.txt", fmWrite)
var length = s.len
for c in s:
if c == '1':
input.writeBit(true)
else:
input.writeBit(false)
if wBit.mark > 0:
input.write(wbit.value)
wbit.mark = 0
wbit.value = 0
input.close()
var output = newFileStream("test_input.txt", fmRead)
let text = readBit(output, length)
assert s == text
output.close()