盒子
盒子
文章目录
  1. Nim编程早茶
    1. 数据存储
    2. 按比特读写文件
    3. 按位写入文件
    4. 按位读取文件

使用 Nim 语言读写比特流

Nim编程早茶

Nim 编程实现读写二进制比特流(01串、bits),也就是一个一个比特读写内容。

数据存储

Nim 语言中,数据最小存储单元是字节(bytes),1 个字节包含 8 个比特。无论我们读取文件或者写入文件,都必须以字节为基本单位。Nim 语言中,提供了 streams 标准库,供我们读写字节流。

按比特读写文件

所以说,如果我们希望按 bit 读取文件,我们必须一次读取一个字节,也就是 8 位,然后再对这一个字节中的每一位进行操作。

Nim 中为我们提供了 bitops 模块,供我们操作 bit

我们可以先创建一个 bit 对象。其中 mark 表示目前处于当前字节的第几位,value 是我们读取的字节值。

1
2
3
4
type
Bit = object
mark: int
value: uint8

按位写入文件

writeBitvalue 参数,是我们写入的 bit 类型。valuetrue,我们向当前位写入 1,反之写入 0。如果 mark 值小于 7,我们就按位填充数值;如果mark 值等于 7,我们就将字节写入文件,并清空 wbit 的数值。要注意,如果写入的比特流不是 8 的倍数,我们在最后一个比特流后面补齐零,然后再将该字节写入文件。

setBitbitops 中的函数,用于将指定位置的比特置为 1。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 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 的时候,我们有可能进行了补零操作,所以说我们需要提供文本的总长度,才能还原出二进制的原貌。

1
2
3
4
5
6
7
8
9
10
11
12
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 模块,直接将字节转变为二进制数的字符串形式。

1
2
3
4
5
6
7
8
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]

最后,来看一个完整的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
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()