2013-04-24 5 views
0

Я пытаюсь декодировать протокол ID3v2 (MP3 header), используя python. Формат данных, подлежащих расшифровке, выглядит следующим образом.Сплит строки ascii/unicode

s1, s2 ... sn-1 являются Unicode (UTF-16/UTF-8) строки, и последняя строка 'зп' может быть Юникода или двоичная строка.

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

Где, разделитель для UTF-16 является '\x00'+'\x00' и разделителем для UTF-8 является '\x00'

я data вместе с юникод-типа. Теперь я должен извлечь все струны (s1, s2, ... sn) от data. Для этого я использую 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, но не работает для набора-2. Так, «данные» в наборе 2-

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

имеет дополнительный '\x00' предшествует разделитель, из-за буквой «0», то его не в состоянии сделать надлежащую работу.

Может ли кто-нибудь помочь мне правильно декодировать «данные» для обоих случаев?

Update:

Постараюсь просто вопрос. s1 = закодирован (UTF-8/UTF-16) Строка

s2 = двоичная строка (не Unicode)

разделитель для UTF-16 '\x00'+'\x00', и разделитель для UTF-8 является '\x00'

data = (разделитель s1 +) + s2

Может ли кто-нибудь помочь мне извлечь 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

извините забыл упомянуть, последняя строка (зп) не может быть строкой unicode. При декодировании кадра APIC (альбом-арт) sn является двоичной (образной) строкой. – Mohan

ответ

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) может не быть строкой в ​​Юникоде. При декодировании кадра APIC (альбом-арт) sn является двоичной (образной) строкой. – Mohan

4

Где, разделитель для UTF-16 '\ x00' + '\ x00' и разделитель для UTF-8 является '\ x00'

Не совсем. Разделитель для UTF-16 равен \0\0 только на границе кода. Один \0 в конце одного блока кода, за которым следует \0 в начале другого блока кода, не является разделителем. Стандарт ID3, говорящий о байтовой «синхронизации», подразумевает, что это не так, но это неправильно.

[Помимо этого, к сожалению, многие инструменты для чтения тегов воспринимают это буквально таким образом, в результате чего любая последовательность с двойным нулевым байтом (например, U + 0100, U + 0061 Āa в UTF-16BE или , как вы обнаружили, любой ASCII в конце строки в UTF-16LE) сломает фрейм. В результате текстовые форматы UTF-16 (UTF-16 + BOM 0x01 и UTF-16BE 0x02) полностью ненадежны и их следует избегать всеми авторами тегов. А текстовый формат 0x00 ненадежен ни для чего, кроме чистого ASCII. UTF-8 является победителем!]

Если у вас есть список-оф-закодированных-прерванных-строк структуры, как те, которые указаны для 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) может не быть строкой в ​​Юникоде. При декодировании кадра APIC (альбом-арт) sn является двоичной (образной) строкой. – Mohan

+0

Двоичные строки, такие как данные изображения «APIC», не должны отправляться через какой-либо из этих текстовых текстов - они не являются ни потенциально-Unicode, ни нулевыми. – bobince

+0

Я просто хочу извлечь (разделить) «данные» на сегменты s1, s2 .. sn. Затем я обрабатываю только текст и обрабатываю двоичные данные отдельно. – Mohan

Смежные вопросы