前言
本文提供计算机科学导论课程“信息隐藏”实验的个人思路,仅供参考。
原题链接请戳这里
为避免抄袭,本文仅提供部分核心代码片段。
预备知识
strconv 是 Go 语言标准库中的一个包,用于字符串与基本数据类型之间的相互转换。本文给出的代码示例将使用 strconv 包中的如下函数:
func ParseInt(s string, base int, bitSize int) (i int64, err error)
func FormatInt(i int64, base int) string
其中, P a r s e I n t ParseInt ParseInt 用于将字符串 s s s 以 b a s e base base 进制转为整数,并且该整数的位数不超过 b i t S i z e bitSize bitSize ( b i t S i z e = 0 , 8 , 16 , 32 , 64 bitSize=0,8,16,32,64 bitSize=0,8,16,32,64 分别代表 i n t , i n t 8 , i n t 16 , i n t 32 , i n t 64 int,int8,int16,int32,int64 int,int8,int16,int32,int64),若超出则会返回溢出错误,同时返回 b i t S i z e bitSize bitSize 能够表示的最大或最小值。
F o r m a t I n t FormatInt FormatInt 用于将整数 i i i 转为 b a s e base base 进制的字符串,返回类型为 s t r i n g string string。
(关于 strconv 包的更详细知识,请参见这里)
题解
本题思维难度几乎为零,我们需要处理的仅仅是 Golang 中令人生理不适的各种麻烦的类型转换。
隐藏程序
注意进行位运算前的强制转换即可。
func modify(value int,pix []byte,size int){
for i:=0;i<size;i++{
pix[i]=(pix[i] & 0xFC) | (byte(value) & 0x3) //value类型为int,需要强制转换
value>>=2
}
}
恢复程序
重点在于 d e c o d e decode decode 函数的编写以及主程序中对图像文件的逐字节提取操作。
关于 d e c o d e decode decode 函数,我们需要对传入的 p i x pix pix 切片依次提取出每个元素字节的末两位,将其拼接后返回。为了方便,这里使用了 s t r c o n v . F o r m a t I n t strconv.FormatInt strconv.FormatInt 函数直接将提取后的字节元素以二进制的形式转换为字符串,最后拼接。
值得注意的是传入参数的类型必须为 i n t 64 int64 int64,所以要求强制转换。
func decode(pix[]byte) string{ //解码函数,对传入的pix切片依次提取出每个字节的末两位,返回类型为string
s:=""
for i:=0; i<len(pix); i++{
temp:=strconv.FormatInt(int64(pix[i] & 0x3 | 0xF0),2) //感谢知乎作者“资深韭菜”对strconv包函数的详解(原网址https://zhuanlan.zhihu.com/p/652026180)
s=temp[len(temp)-2:]+s
}
return s
}
在主程序中,我们先对原文件的长度进行提取,然后依次逐字节提取原文件的每个字符。
p,_:=os.ReadFile(image)
size,_:=strconv.ParseInt(decode(p[S:S+T]),2,0) //二进制字符串转整数,返回值为int64类型
s:=[]byte{}
for i:=0; i<int(size); i++{
temp,_:=strconv.ParseInt(decode(p[S+T+i*C:S+T+(i+1)*C]),2,0) //提取每个字节的字符,temp为int64类型
s=append(s,byte(temp)) //强制转换,将temp加入最终输出的byte切片数组
}
os.WriteFile(txt,s,0644)
一些碎碎念
Go 语言对于不同类型参数之间的运算施加了诸多限制,而本题的代码实现由于牵扯到较多参数类型迥异的函数,故使用了极多的强制转换,这点对于初学 Go 语言的新手并不友好。
更多的雷点在于文本文件字符的处理上。由于可能涉及中文字符等 ASCII码大于 127 的非传统 ASCII 字符,输出结果时不能逐字节将
t
e
m
p
temp
temp 转为字符(
r
u
n
e
(
t
e
m
p
)
rune(temp)
rune(temp))并拼接成字符串,会导致乱码出现。正确的做法在于直接将
b
y
t
e
(
t
e
m
p
)
byte(temp)
byte(temp) 添加至
b
y
t
e
byte
byte 切片类型数组的末尾。我不会告诉你这个bug我调了两小时TAT
就写到这吧,祝我 C a l c u l u s Calculus Calculus 和 D M c o u r s e DMcourse DMcourse 期中考试顺利~