2009-11-01 101 views
3

我正在編寫解析器,並且有大量文本需要解碼,但大多數用戶只關心所有數據的幾個字段。所以我只想在用戶實際使用某些數據時進行解碼。這是做這件事的好方法嗎?Python:懶惰字符串解碼

class LazyString(str): 
    def __init__(self, v) : 
     self.value = v 
    def __str__(self) : 
     r = "" 
     s = self.value 
     for i in xrange(0, len(s), 2) : 
      r += chr(int(s[i:i+2], 16)) 
     return r 

def p_buffer(p): 
    """buffer : HASH chars""" 
    p[0] = LazyString(p[2]) 

這是我唯一需要重寫的方法嗎?

回答

2

我不知道如何實現一個字符串子類在這裏很有好處。在我看來,如果你正在處理一個包含PB級數據的數據流,每當你創建了一個你不需要的對象時,你就已經失去了遊戲。您的首要任務應該是儘可能多地忽略儘可能多的輸入。

你當然可以建立一個字符串類是這樣做:

class mystr(str): 
    def __init__(self, value): 
     self.value = value 
     self._decoded = None 
    @property 
    def decoded(self): 
     if self._decoded == None: 
      self._decoded = self.value.decode("hex") 
      return self._decoded 
    def __repr__(self): 
     return self.decoded 
    def __len__(self): 
     return len(self.decoded) 
    def __getitem__(self, i): 
     return self.decoded.__getitem__(i) 
    def __getslice__(self, i, j): 
     return self.decoded.__getslice__(i, j) 

等。關於這樣一個奇怪的事情是,如果你繼承str,每一個你沒有明確的實現方法將在則傳遞給構造函數的值稱爲:

>>> s = mystr('a0a1a2') 
>>> s 
 ¡¢ 
>>> len(s) 
3 
>>> s.capitalize() 
'A0a1a2' 
+0

我正在寫一個記錄閱讀器,一旦記錄完成處理後記錄超出範圍,所以我不認爲創建該對象會是一個問題,只是解碼很慢。你有其他想法嗎? – 2009-11-02 03:52:28

+0

當你處理數萬億字節時,一切都是問題。從BOF讀到EOF是一個問題。我希望能夠從中找出基準。就像,你可能可以通過使用'array.fromfile()'將大塊數據讀入數組,然後使用指針處理數組來避免創建對象,並且只有當你將數據從數組中複製出來已經確定了你感興趣的內容。如果不知道更多關於你的應用的信息,很難理解。 – 2009-11-02 05:35:17

+0

我實際上是通過HDFS使用CSV記錄格式讀取Hadoop上的所有內容。由於我在數千臺計算機上運行,​​因此單個文件沒有太多數據。我實際上爲這種格式編寫了一個lex和yacc解析器:http://github.com/ptarjan/hadoop_record。我認爲'ply'在解析時在內存中使用了一個字符串緩衝區,但我不確定。 – 2009-11-02 20:03:56

1

我在代碼中看不到任何一種惰性評估。您使用xrange的事實僅意味着將按需生成從0len(s)的整數列表。無論如何,整個字符串r將在字符串轉換期間被解碼。

在Python中實現惰性序列的最佳方式是使用generators。你可以嘗試這樣的事:

def lazy(v): 
    for i in xrange(0, len(v), 2): 
     yield int(v[i:i+2], 16) 

list(lazy("0a0a0f")) 
Out: [10, 10, 15] 
+0

嗯,我想製作LazyString對象,但不要做大的for循環,直到人們真正使用它。 – 2009-11-01 01:08:00

+0

這取決於你的需求,但從你的問題我明白,你想解碼只是需要的字符串的一部分 - 這可以使用生成器完成。 – 2009-11-01 01:16:03

+0

我實際上只想解碼字符串,如果客戶端代碼使用它。也許我的問題不是很清楚,對不起。 – 2009-11-01 02:11:23

0

你在做什麼是建立在已經:

s = "i am a string!".encode('hex') 
# what you do 
r = "" 
for i in xrange(0, len(s), 2) : 
    r += chr(int(s[i:i+2], 16)) 
# but decoding is builtin 
print r==s.decode('hex') # => True 

正如你可以看到你的整個解碼s.decode('hex')

但「懶惰」的解碼聽起來像是對我不成熟的優化。你需要千兆字節的數據才能注意到它。嘗試分析,.decode比舊代碼快50倍。

也許你想的財產以後這樣的:

class DB(object): # dunno what data it is ;) 
    def __init__(self, data): 
     self.data = data 
     self.decoded = {} # maybe cache if the field data is long 
    def __getitem__(self, name): 
     try: 
      return self.decoded[name] 
     except KeyError: 
      # this copies the fields data 
      self.decoded[name] = ret = self.data[ self._get_field_slice(name) ].decode('hex') 
      return ret 
    def _get_field_slice(self, name): 
     # find out what part to decode, return the index in the data 
     return slice(...) 

db = DB(encoded_data)  
print db["some_field"] # find out where the field is, get its data and decode it 
+0

不錯!我不知道.decode(十六進制)。我會用它。但事實上,我在Hadoop上的數據量是PB,所以我不想解碼所有數據,只是特定程序要求的部分。我分析了我的代碼,99%的時間花在解碼循環中,1%在解析器中,所以這就是爲什麼我在這部分工作:) – 2009-11-01 02:10:33

+0

感謝您的編輯。它接近,但我想模仿一個字符串,而不是一個完整的數據庫。所以我想要一個類似於被訪問時看起來像字符串的類,但是在使用它之前並沒有實際解碼它的數據。那可能嗎?我的示例代碼是爲那個工作嗎? – 2009-11-01 08:12:02

0

你需要重寫的方法真的取決於如何計劃使用您新的字符串類型。

但是你基於str的類型對我來說看起來有點可疑,你看看str的實現來檢查它是否有value屬性,你在__init__()中設置?執行dir(str)並不表示在str上有任何此類屬性。這是正常的str方法將不會在您的數據上運行的情況下,我懷疑這是否是你想要的效果,否則子分類的優點是什麼。

無論如何,子類別基礎數據類型有點奇怪,除非您有非常特殊的要求。對於懶惰的評估,你希望你可能更好地創建包含字符串而不是子類str的類,並編寫客戶端代碼來處理該類。然後,您可以通過多種方式自由添加所需的即時評估:在此演示文稿中可找到使用描述符協議的示例:Python's Object Model(搜索「class Jit(object)」以訪問相關部分)

+0

'價值'是我自己的咒語。我寧願這對我的最終用戶來說是一個真正的字符串,而不必使用另一個類。我應該在'dir(str)'中實現每個方法嗎?他們都自己依靠'__str__'嗎? – 2009-11-01 21:16:44

+0

我不確定'str'是如何在內部工作的,因爲它是一個在C中實現的內置類型,我沒有看過它的C源代碼。但是它不太可能在內部使用'__str__'。這是Python在各種情況下調用以獲取任何對象的字符串表示形式的特殊方法。我懷疑在'str'類型中這個方法只返回'self'。我不明白爲什麼'str'會使用它來獲取自己的數據,但我可能是錯的。 – 2009-11-01 21:54:40

0

的問題是不完整的,因爲答案將取決於你使用的編碼細節。假設你將一個字符串列表編碼爲pascal字符串(即以字符串長度作爲固定長度整數編碼的前綴),並且假設你想從列表中讀取第100個字符串,你可以seek()forward對於前99個字符串中的每一個並且根本不讀取它們的內容。如果字符串很大,這會帶來一些性能提升。

如果OTOH,你編碼字符串作爲串接0終結stirngs的列表,你會閱讀所有字節,直到第100 0

此外,你談到一些「場」,但你例子看起來完全不同。

+0

我的例子是一個層片解析器代碼。我的輸入是一個字符串,我將它解析爲記錄。我的記錄將包含一些地圖,矢量,字符串和數字。在我的輸入字符串中,這些字符串是十六進制編碼的,我不想解碼它們,除非我的庫的用戶明確要求它們。這有幫助嗎? – 2009-11-02 03:55:34