2013-04-24 139 views
0

我想解码使用python的ID3v2(MP3标头)协议。要解码的数据格式如下。分割ascii/unicode字符串

s1s2,... sn-1是个Unicode(UTF-16/UTF-8)的字符串,和最后一个字符串 'SN' 可以是Unicode或二进制字符串。

data = s1+delimiters+s2+delimiters+...+sn 

其中,分隔符为UTF-16是'\x00'+'\x00' 和分隔符为UTF-8是'\x00'

我得到data使用Unicode型一起。现在我必须从data中提取所有字符串(s1,s2,... sn)。对于这个我使用split()如下,

#!/usr/bin/python 

def extractStrings(encoding_type, data): 
    if(encoding_type == "utf-8"): delimitors = '\x00' 
    else: delimitors = '\x00'+'\x00' 
    return data.split(delimitors) 

def main():   
    # Set-1 
    encoding_type = "utf-8" 
    delimitors = '\x00' 
    s1="Hello".encode(encoding_type) 
    s2="world".encode(encoding_type) 
    data = s1+delimitors+s2 
    print extractStrings(encoding_type, data) 

    # Set-2 
    encoding_type = "utf-16" 
    delimitors = '\x00'+'\x00' 
    s1="Hello".encode(encoding_type) 
    s2="world".encode(encoding_type) 
    data = s1+delimitors+s2 
    print extractStrings(encoding_type, data) 

if __name__ == "__main__": 
    main() 

输出:

['Hello', 'world'] 

['\xff\xfeH\x00e\x00l\x00l\x00o', '\x00\xff\xfew\x00o\x00r\x00l\x00d\x00'] 

它适用于集-1数据但SET-2不工作。 因为在SET-2

'\xff\xfeH\x00e\x00l\x00l\x00o\x00\x00\x00\xff\xfew\x00o\x00r\x00l\x00d\x00' 
          ^   ^

“数据”有一个额外的'\x00'之前分隔符,由于字母“0”,它不能做一个适当的工作。

任何人都可以帮助我正确解码'数据'两种情况?

更新:

我会尽量简单的问题。 S1 =编码(UTF-8/UTF-16)串

S2 =二进制串(不是Unicode)

定界符对于UTF-16是'\x00'+'\x00',和定界符对于UTF-8是'\x00'

data =(s1 + delimiter)+ s2

任何人都可以帮助我从'data'中提取s1和s2吗?

UPDATE2:解决方案

下面的代码工作对我的要求,

def splitNullTerminatedEncStrings(self, data, encoding_type, no_of_splits): 
data_dec = data.decode(encoding_type, 'ignore') 
chunks = data_dec.split('\x00', no_of_splits) 
enc_str_lst = [] 
for data_dec_seg in chunks[:-1]: 
    enc_str_lst.append(data_dec_seg.encode(encoding_type)) 
data_dec_chunks = '\x00'.join(chunks[:-1]) 
if(data_dec_chunks): data_dec_chunks += '\x00' 
data_chunks = data_dec_chunks.encode(encoding_type) 
data_chunks_len = len(data_chunks) 
enc_str_lst.append(data[data_chunks_len:]) # last segment 
return enc_str_lst 
+0

对不起忘了提,最后一个字符串(SN )可能不是一个unicode字符串。在解码APIC(专辑艺术)帧时,sn是二进制(图像)字符串。 – Mohan 2013-04-24 18:10:09

回答

0

下面的代码工作对我的要求,

def splitNullTerminatedEncStrings(self, data, encoding_type, no_of_splits): 
data_dec = data.decode(encoding_type, 'ignore') 
chunks = data_dec.split('\x00', no_of_splits) 
enc_str_lst = [] 
for data_dec_seg in chunks[:-1]: 
    enc_str_lst.append(data_dec_seg.encode(encoding_type)) 
data_dec_chunks = '\x00'.join(chunks[:-1]) 
if(data_dec_chunks): data_dec_chunks += '\x00' 
data_chunks = data_dec_chunks.encode(encoding_type) 
data_chunks_len = len(data_chunks) 
enc_str_lst.append(data[data_chunks_len:]) # last segment 
return enc_str_lst 
3

你为什么不首先解码字符串?

的Python 2:

decoded = unicode(data, 'utf-8') 
# or 
decoded = unicode(data, 'utf-16') 

的Python 3:

decoded = str(data, 'utf-8') 
# or 
decoded = str(data, 'utf-16') 

然后你用编码无关的数据直接工作,分隔符总是一个空。

+0

对不起忘了提,最后一个字符串(sn)可能不是一个unicode字符串。在解码APIC(专辑艺术)帧时,sn是二进制(图像)字符串。 – Mohan 2013-04-24 18:12:14

4

其中,分隔符对于UTF-16是 '\ X00' + '\ X00' 和分隔符对于UTF-8是 '\ X00'

不完全。 UTF-16的分隔符仅在代码单元边界为\0\0。一个\0在一个代码单元的末尾跟着\0在另一个代码单元的开始处不构成分隔符。关于字节“同步”的ID3标准意味着情况并非如此,但这是错误的。另外:很遗憾,很多标签阅读工具确实是这样认为的,结果是任何有一个双零字节的序列(例如UTF-16BE中的U + 0100,U + 0061 Āa,或者UTF-16BE中的U + 0100,U + 0061 Āa或,正如您发现的那样,UTF-16LE中的任何字符串末尾的ASCII都会破坏帧。因此,UTF-16文本格式(UTF-16 + BOM 0x01和UTF-16BE 0x02)完全不可靠,应该由所有标签编写者避免。对于除纯ASCII之外的任何内容,文本格式0x00都不可靠。 ]

如果你有一个类似于T帧(TXXX除外)的编码结束字符串结构列表,那么简单的方法是在分割之前解码它们在U + 0000终结者:

def extractStrings(encoding_type, data): 
    chars = data.decode(encoding_type) 
    # chars is now a Unicode string, delimiter is always character U+0000 
    return chars.split(u'\0') 

如果data是一个整体的ID3框架恐怕你不能用一个单一的split()处理它。 T系列以外的帧包含编码终止字符串,仅ASCII字符终止字符串,二进制对象(无终止符)和整数字节/字值的混合。 APIC就是这样的一个例子,但是对于一般情况,您必须先了解每个要预先解析的帧的结构,并逐个使用每个字段,然后手动查找每个终止符。

要查找UTF-16编码数据的代码单元排列的终止不曲解Āa等,你可以使用正则表达式,如:

ix= re.match('((?!\0\0)..)*', data, re.DOTALL).end() 
s, remainder= data[:ix], data[ix+2:] 

这不是一个很大的乐趣真的 - ID3v2不是一个非常干净的格式。我的头和未经考验的顶部,这样的事情是如何,我可能接近它:

p= FrameParser(data) 
if frametype=='APIC': 
    encoding= p.encoding() 
    mimetype= p.string() 
    pictype= p.number(1) 
    desc= p.encodedstring() 
    img= p.binary() 

class FrameParser(object): 
    def __init__(self, data): 
     self._data= data 
     self._ix= 0 
     self._encoding= 0 

    def encoding(self): # encoding byte - remember for later call to unicode() 
     self._encoding= self.number(1) 
     if not 0<=self._encoding<4: 
      raise ValueError('Unknown ID3 text encoding %r' % self._encoding) 
     return self._encoding 

    def number(self, nbytes= 1): 
     n= 0 
     for i in nbytes: 
      n*= 256 
      n+= ord(self._data[self._ix]) 
      self._ix+= 1 
     return n 

    def binary(self): # the whole of the rest of the data, uninterpreted 
     s= self._data[self._ix:] 
     self._ix= len(self._data) 
     return s 

    def string(self): # non-encoded, maybe-terminated string 
     return self._string(0) 

    def encodedstring(self): # encoded, maybe-terminated string 
     return self._string(self._encoding) 

    def _string(self, encoding): 
     if encoding in (1, 2): # UTF-16 - look for double zero byte on code unit boundary 
      ix= re.match('((?!\0\0)..)*', self._data[self._ix:], re.DOTALL).end() 
      s= self._data[self._ix:self._ix+ix] 
      self._ix+= ix+2 
     else: # single-byte encoding - look for first zero byte 
      ix= self._data.find('\0', self._ix) 
      s= self._data[self._ix:self._ix+ix] if ix!=-1 else self._data[self._ix:] 
      self._ix= ix if ix!=-1 else len(self._data) 
     return s.decode(['windows-1252', 'utf-16', 'utf-16be', 'utf-8][encoding]) 
+0

对不起忘了提,最后一个字符串(sn)可能不是一个unicode字符串。在解码APIC(专辑艺术)帧时,sn是二进制(图像)字符串。 – Mohan 2013-04-24 18:11:40

+0

像APIC'图片数据'这样的二进制字符串不应该通过任何这种文本处理代码发送 - 它们既不可能是Unicode也不是零终止的。 – bobince 2013-04-24 18:22:00

+0

我只想提取(拆分)'数据'到段s1,s2 ... sn中。然后,我将仅处理文本并分别处理二进制数据。 – Mohan 2013-04-24 18:37:08