2016-02-20 45 views
1
import wave,struct 
f = wave.open('bird.wav', 'r') 

for i in range(5,10): 
    frame = f.readframes(i) 
    print frame 
struct.unpack('<H',frame) 

我使用上面的代码从python的立体声wav文件中提取字节。但是,不是字节,我得到一些乱码字符。使用struct.unpack()功能我收到以下错误在python中读取WAV文件

“解压需要长度为2的字符串参数”

我在代码中进行打印这些字节中的1和0的什么样的变化?我想稍后修改用于隐写的音频帧的LSB。

+1

乱码字符可能是字节,它们只是以这种方式打印。什么让你觉得他们不是?你有没有尝试'打印(类型(框架))'? – rofls

+0

子过程ffmpeg? – alphanumeric

+1

@rofls - 类型是str。如何以1和0打印这些字节? – aditya

回答

1

我不确定你为什么要用二进制打印这些字节,但这样做很容易。

您需要将字节转换为整数,然后使用str.format方法对其进行格式化,旧的%风格的格式不会执行位。

执行该转换的简单方法是使用ord函数,但对于大量字节,最好通过创建bytearray来将它们转换为一次命中。

#Some bytes, using hexadecimal escape codes 
s = '\x01\x07\x0f\x35\xad\xff' 
print ' '.join(['{0:08b}'.format(ord(c)) for c in s]) 

b = bytearray(s) 
print ' '.join(['{0:08b}'.format(u) for u in b]) 

输出

00000001 00000111 00001111 00110101 10101101 11111111 
00000001 00000111 00001111 00110101 10101101 11111111 

通常,十六进制符号是不是二进制读取更方便。

from binascii import hexlify 

print hexlify(s) 
print ' '.join(['%02X' % u for u in b]) 
print ' '.join(['%02X' % ord(c) for c in s]) 
print ' '.join(['{0:02X}'.format(ord(c)) for c in s])  

输出

01070f35adff 
01 07 0F 35 AD FF 
01 07 0F 35 AD FF 
01 07 0F 35 AD FF 

我刚才看到重新隐写您的评论。旋转字节位的最方便的方法是使用bytearray。您可以使用str函数轻松地将bytearray转换回字符串。

​​

输出

01070f35adff 

字符串格式化选项在官方Python文档描述。对于旧的%风格的格式,请参阅5.6.2. String Formatting Operations。对于现代str.format选项,请参阅7.1.3. Format String Syntax7.1.3.1. Format Specification Mini-Language

{0:08b}0之前的冒号是字段位置(可以在最近版本的Python中省略)。它表示我们要将这个格式代码应用到.format的第一个参数,即索引为零的参数。例如,

'{0} {2} {1}'.format('one', 'two', 'three') 

打印

one three two 

b意味着我们要打印一个数字作为二进制文件。 08表示我们希望输出为8个字符宽,对于小于8位的二进制数填充零填充。

%02X大写X意味着我们要打印一个数为十六进制,使用大写字母A-F的数字大于9,我们可以用小写x获得小写字母。 02表示我们希望输出为2个字符宽,对于小于2个十六进制数字的十六进制数字填充零。

+0

你能解释一些格式化选项,比如'{0:08b}'和'%02X'吗?那些做特别的事情吗? – rofls

0

如果要修改字节的LSB,将值表达为二进制字符串没有意义。实际上,你会做沿东西线(在伪代码):

byte = '\x6h' 
binary = convert_to_bits(byte) # some way of getting 1s and 0s in a string 
binary = binary[:7] + my_bit_string 
byte = convert_to_byte(binary) 

有更直接,更有效的方式来修改一个位值,这就是与bitwise operators。例如,假设我们要将01001001(十进制73)更改为01001000.我们想要创建一个位掩码11111110,它的十进制值是254,而AND它与我们的值。

>>> value = 73 & 254 
>>> value 
72 
>>> '{0:08b}'.format(value) 
'01001000' 

当你嵌入位字节中,LSB可能改变也可能不会。有很多方法可以解决这个问题,但最直接的做法是将lsb清零,然后用OR(如果您还想嵌入多个位,则功能非常全面)将其覆盖。

byte = (byte & 254) | my_bit 

你也可以零出LSB用right shift,随后left shift,但这需要2个操作,而不是一个。

byte = ((byte >> 1) << 1) | my_bit 

或者你可以检查LSB和你的位是否是不同的,并用XOR翻转。但是,这种方法使用分支,效率最低。

if (byte & 1) != my_bit: 
    byte = byte^1 
# no need to do anything if they are the same 

所以,你需要做的就是将您的字节整数数组。你可以使用[ord(byte) for byte in frame],但有更高效的内置方式。随着bytearray()bytes()

>>> frame = '\x0f\x02\x0e\x02\xf7\x00\xf7\x00T\xffT\xff' 
>>> frame_bytes = bytearray(frame) 
>>> frame_bytes[0] 
15 
>>> frame_bytes[0] = 14  # modify 
>>> bytes(frame_bytes)  # convert back to bytes 
'\x0e\x02\x0e\x02\xf7\x00\xf7\x00T\xffT\xff' 

array.array()(这似乎是几十万字节的一小一丁点儿慢):

>>> import array 
>>> frame = '\x0f\x02\x0e\x02\xf7\x00\xf7\x00T\xffT\xff' 
>>> frame_bytes = array.array('B', frame) 
>>> frame_bytes[0] 
15 
>>> frame_bytes[0] = 14  # modify 
>>> frame_bytes.tostring() # convert back to bytes; in Python 3 use `tobytes()` 
'\x0e\x02\x0e\x02\xf7\x00\xf7\x00T\xffT\xff' 

嵌入和提取的例子。

frame = '\x0f\x02\x0e\x02\xf7\xf7T\xffT\xff' 
bits = [0, 0, 1, 1, 0] 

# Embedding 
frame_bytes = bytearray(frame) 
for i, bit in enumerate(bits): 
    frame_bytes[i] = (frame_bytes[i] & 254) | bit 
frame_modified = bytes(frame_bytes) 

# Extraction 
frame_bytes = bytearray(frame_modified) 
extracted = [frame_bytes[i] & 1 for i in range(5)] 
assert bits == extracted 

如果你的秘密是一个字符串或字节序列,它很容易convert them to a list of 1s and 0s

最后,请确保您不要修改任何标题数据,因为这可能会导致文件无法读取。