原文来自https://www.shiyanlou.com/courses/651/labs/2121/documentclass
bytearray
([source[, encoding[, errors]]])
返回一个新的字节数组。bytearray
类是一个关于整数的 mutable(可变)序列,范围为0 < = x < 256。它包含了可变序列大部分的常用方法,参见 Mutable Sequence Types,同时也包含了bytes
类型的大部分方法,参见Bytes and Bytearray Operations
可选参数source可用于初始化数组,有多种实现方式:
- 如果是个字符串 string,应该直接在参数中指定编码 encoding (and optionally, errors) ;之后
bytearray()
将使用str.encode()
按照编码转化字符串为字节序列。 - 如果是个integer,数组将会获取大小并将其初始化为空字节。
- 如果是个符合buffer接口的对象,该对象的一个只读缓冲区将用于初始化字节数组。
- 如果它是可迭代类型iterable,其整数元素的取值范围是
0 <= x < 256
,一般用作数组的初始内容。
如果没有设置任何参数,数组大小为0.
bin
(
x
)
Convert an integer number to a binary string. The result is a valid Python expression. If x is not a Python int
object, it has to define an __index__()
method that returns an intege
对于作用域:
- python能够改变变量作用域的代码段是def、class、lamda.
- if/elif/else、try/except/finally、for/while 并不能涉及变量作用域的更改,也就是说他们的代码块中的变量,在外部也是可以访问的
- 变量搜索路径是:本地变量->全局变量
UTF-8是Unicode的一种实现方式,也就是它的字节结构有特殊要求,所以我们说一个汉字的范围是0X4E00到0x9FA5,是指unicode值,至于放在utf-8的编码里去就是由三个字节来组织,所以可以看出unicode是给出一个字符的范围,定义了这个字是码值是多少,至于具体的实现方式可以有多种多样来实现。
UTF-8是一种变长字节编码方式。对于某一个字符的UTF-8编码,如果只有一个字节则其最高二进制位为0;如果是多字节,其第一个字节从最高位开始,连续的二进制位值为1的个数决定了其编码的位数,其余各字节均以10开头。UTF-8最多可用到6个字节。
如表:
1字节 0xxxxxxx
2字节 110xxxxx 10xxxxxx
3字节 1110xxxx 10xxxxxx 10xxxxxx
4字节 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
5字节 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
6字节 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
因此UTF-8中可以用来表示字符编码的实际位数最多有31位,即上表中x所表示的位。除去那些控制位(每字节开头的10等),这些x表示的位与UNICODE编码是一一对应的,位高低顺序也相同。
实际将UNICODE转换为UTF-8编码时应先去除高位0,然后根据所剩编码的位数决定所需最小的UTF-8编码位数。
因此那些基本ASCII字符集中的字符(UNICODE兼容ASCII)只需要一个字节的UTF-8编码(7个二进制位)便可以表示。
对于上面的问题,代码中给出的两个字节是
十六进制:C0 B1
二进制:11000000 10110001
对比两个字节编码的表示方式:
110xxxxx 10xxxxxx
提取出对应的UNICODE编码:
00000 110001
可以看出此编码并非“标准”的UTF-8编码,因为其第一个字节的“有效编码”全为0,去除高位0后的编码仅有6位。由前面所述,此字符仅用一个字节的UTF-8编码表示就够了。
自己的一点体会:
1、
关于这个代码,有一个地方需要解释一下:为什么原文中index*4<len(binary)能够保证既不越界又能表示完所有的binary呢?这是因为binary一定是4的倍数,我们的constLen函数使得binary其实一定是8的倍数,因为一个中文字符需要3个整数来编码,一个整数需要8个binary bit来表示,所以binary数组一定是8的倍数,所以这样做是没有问题的
2、
还有一个是decode的时候为什么直接使用join就可以连成一串了呢?这是因为如果list的内部全部都是字符串,那么使用join方法就能连成一个完整的字符串,并且前面不用加上str方法来转换,否则需要加上str方法转变,并且转换出来的字符串会带上'[',']',','等list的符号
3、
这个lamda函数其实很简单。。不需要看刘大牛的博客也可以理解。自己用c写一个递归马上就能发现它是怎么写的了
4、关于全文编码的一个过程:中文/英文 -》utf-8(bytearray其实等价于str.encode('utf-8'))-》utf-8转化成的int(并且具有前导0,其实这个前导0只有在符号是ascill码的时候需要添加,因为查看utf-8编码表发现凡是需要字节数大于1的字符它的utf-8编码的第一位一定是1) -》加密到图片 -》从图片中获得尾数 ->用两个函数(func和rec)解码为unicode的int(因为unicode删除utf-8的前导数字以后就是unicode编码) -》用chr从unicode的int解码为unicode
</pre><pre name="code" class="python">#coding=utf-8
from PIL import Image
def makeImageEven(image):
pixels = list(image.getdata())
evenPixels = [(r>>1<<1,g>>1<<1,b>>1<<1,t>>1<<1) for [r,g,b,t] in pixels]
evenIMG = Image.new(image.mode,image.size)
evenIMG.putdata(evenPixels)
return evenIMG
def encodeDataInImage(image,data):
evenIMG = makeImageEven(image)
binary = ''.join(map(constLen,bytearray(data,'utf-8')))
evenPixels = list(evenIMG.getdata())
if len(binary) > len(evenPixels)*4:
raise Exception("Error:cannot conver data coded larger than"+len(evenPixels)*4+4)
encodedPixels = [(r+int(binary[index*4+0]),g+int(binary[index*4+1]),b+int(binary[index*4+2]),t+int(binary[index*4+3])) if index*4 < len(binary) else (r,g,b,t) for index,(r,g,b,t) in enumerate(evenPixels)]
encodedIMG = Image.new(image.mode,image.size)
encodedIMG.putdata(encodedPixels)
return encodedIMG
def constLen(int):
binary = '0'*(8-(len(bin(int))-2))+bin(int).replace('0b','') # 8 bit lenth with front 0s
return binary # the zero can be deleted later, which won't effect the result
def BinaryToString(binary):
index = 0
string = []
rec = lambda x, i: x[2:8] + (rec(x[8:], i-1) if i > 1 else '') if x else ''
# rec = lambda x, i: x and (x[2:8] + (i > 1 and rec(x[8:], i-1) or '')) or ''
fun = lambda x, i: x[i+1:8] + rec(x[8:], i-1)
while index + 1 < len(binary):
chartype = binary[index:].index('0')
length = chartype*8 if chartype else 8
string.append(chr(int(fun(binary[index:index+length],chartype),2)))
index += length
return ''.join(string)
def decodeImage(image):
pixels = list(image.getdata())
binary = ''.join([str(r%2)+str(g%2)+str(int(b%2))+str(int(t%2)) for (r,g,b,t) in pixels])
location_END = binary.find('0000000000000000')
if location_END % 8 == 0 :
End_Index = location_END
else:
End_Index = location_END + (8 - location_END % 8)
data = BinaryToString(binary[:End_Index])
return data
if __name__ == '__main__':
# IMGname = raw_input("IMG name:")
IMGname='4.png'
IMG = Image.open(IMGname)
# data = raw_input("the data:")
data='asdasdasd'
encodedIMG = encodeDataInImage(IMG,data)
encodedIMG.save('encoded'+IMGname)
print decodeImage(encodedIMG)