2013-07-26 117 views
1

的解析我試圖解析與嵌套結構的二進制文件格式。在程序的僞代碼,該過程將如下這樣:嵌套結構和對象模型

// A structure contains: 
// tag | oneof(a, b, c) | oneof(oneof(aa, ab, ac), oneof(ba, bb, bc), oneof(ca, cb, cc)) 
PROCEDURE parse() { 
    RECORD read_type; 

    read_tag(read_type); 

    if (read_type == TYPE_A) { 
     read_a(read_type); 
     if (read_type == TYPE_AA) { 
      read_aa(); 
     } else if (read_type == TYPE_AB) { 
      read_ab(); 
     } else if (read_type == TYPE_AC) { 
      read_ac(); 
     } 
    } else if (read_type == TYPE_B) { 
     // see above 
    } else if (read_type == TYPE_C) { 
     // see above 
    } 
} 

的外部結構例如AA不能在不脫離它的父對象A,而這又需要它的標籤/標頭解釋上下文來解釋。使用這些結構時,處理包含A的結構是有意義的,這些結構包含AA等,但不能是結構的A或AA部分。

我的問題是那麼如何創建此過程的類模型。如果該結構是:

class Base; 
class A: Base; 
class B: Base; 
class C: Base; 
class AA: A; 
class AB: A; 
class AC: A; 
// ... 

在這種情況下,AA可能被理解爲這樣的:

AA::AA(): A() { 
    read_aa(); 
} 

A::A(): Base() { 
    read_a(); 
} 

Base::Base() { 
    read_tag(); 
} 

然而,問題是,這是不可能知道的派生類對象,而不構建首先構造基礎對象。這可以通過使用構造函數AA :: AA(A *)來複制構造其父項,但這看起來像是一種不必要的低效率。此外,這將需要一個外部工廠功能,諸如:

Base *read_object() { 
    Base *base = new Base(); 
    if (b->tag_type == TYPE_A) { 
     A *a = new A(base); 
     if (a->tag_type == TYPE_AA) { 
      return new AA(a); 
     } else if (a->tag_type == TYPE_AB) { 
      // ... 
     } else if (a->tag_type == TYPE_AC) { 
      // ... 
     } 
    } else if (b->tag_type == TYPE_B) { 
     // ... 
    } else if (b->tag_type == TYPE_C) { 
     // ... 
    } 
} 

另一種選擇是爲具有指結構的子區域,如類:

class CompleteStructure; 
class StructureA; 
class StructureB; 
class StructureC; 
class StructureAA; 
class StructureAB; 
class StructureAC; 
// ... 

class CompleteStructure { 
    union {StructureA a, StructureB b, StructureC c} sub; 
} 

class StructureA { 
    CompleteStructure *parent; 
    union {StructureAA aa, StructureAB ab, StructureAC ac} sub; 
} 

class StructureAA { 
    StructureA *parent; 
} 

在這種情況下,該構造函數CompleteStructure :: CompleteStructure()會讀取標記,然後構造StructureA,StructureB或StructureC中的一個,然後構造它自己的子結構。與此相關的問題是,每個子結構都需要明確引用其父項,以便「投射」層次並實現其方法/功能。

是這些方法比其他的更好的空間/時間效率和「清潔」的名詞之一?有沒有優越的第三種方法?

編輯: 要到下面的兩個答案迴應,問題是雙方關於分析和對象行爲。我最初的目標僅僅是從文件中讀取結構,打印出它們的字段,然後按照相同的順序將它們寫回到磁盤。稍後,還會有額外的目標,例如查找A派生結構的所有實例並按特定字段對其進行排序或檢查非法結構組合(例如,同時具有BA和BB)。

EDIT2: 這是我參考的一個結構(帶有通用字段名稱)的示例模式。 u8/16/32是指整數類型,sz是一個C字符串,大寫名稱是需要讀取的字段,常量前綴爲下劃線。

DEF AA { 
    // Identifies and deliminates complete records. 
    TAG { 
     u32 SYNC_CODE = 0xFFFFFFFF; 
    } 

    // Metadata for high level identification of data. 
    A { 
     u32 TYPE = __TYPE_A; 
     u16 CATEGORY = __CATEGORY_1; // A defines the "category" of the following file data 
     u32 NUM_OF_KV_PAIRS; 
     for (int i = 0; i < NUM_OF_KV_PAIRS; ++i) { // unspecified metadata 
      sz KEY; 
      sz VALUE; 
     } 
     u8 HAS_EXTENSION_FLAG = true; // indicates presence of next record 
     if (!HAS_EXTENSION_FLAG) { 
      DEFAULT_PARAMS; // legacy 
     } 
    } 

    // Indicates a specific data layout and version. 
    AA { 
     u32 TYPE = __TYPE_AA; 
     u8[16] ACCESS_KEY; 
     u32 NUM_OFFSETS; 
     for (int i = 0; i < NUM_OFFSETS; ++i) { 
      // stuff 
     } 
    } 
} 
+0

我沒有得到_「與此相關的問題是,每個子結構都需要明確引用其父級以便」投射「層次結構」_您能詳細說明一點嗎?爲什麼一個子結構讓我們說AA需要知道任何關於它的超級結構A的例子? –

+0

AA中的字段指的是先前結構中的初始化結構來定義它們的語義。此外,爲了類型安全,人們可能希望通過其特定類型來引用完整的結構,例如函數'process(AA a){do_something(a.complete_structure()); }'。 – user2363399

+0

_HAS_EXTENSION_FLAG = true; //表示下一個記錄的存在_這是關於AA結構的存在還是其他意義? –

回答

0

這是很難回答的,如果一些做法是在效益分析方面沒有更好的更具體的問題說明。在下面你可以找到一些思考的食物。

要點1:在考慮班級設計時,還需要考察所需的行爲而不僅僅是數據。用於存儲的二進制格式不一定意味着層次結構,當然應該考慮到這一點,但它不應該成爲主要關注點。

舉個例子,假設我們有一個Person類具有height場和Rectangle類,也有height場。他們都分享一些數據,但只有這些信息使他們彼此無關。如果我們定義了上下文,並且我們說我們想要在屏幕上繪製它們,那麼突然height字段具有更具體的含義。現在繼承Drawable也許更有意義。

您的情況中的問題是我們將如何使用它們?如果我們有{A, B}{AA, BB}甚至{A, BB}的列表,我們可以執行哪些常見操作?我們能以某種方式一起管理它們嗎?這是你應該考慮的重要一點。

點2:你說「是有意義的操作包含一個包含AA等結構,但絕不只有A或結構的AA部分」。所以我明白AAis-aA,但反過來也是如此。如果是這樣的話,那麼它很有意義的Base, A, B, C抽象類,只能夠直接instatiate最後一級AA, BB

3點:在另一方面,它可能會更好超過可用的組合物如果不同的結構只是定義了一些數據而不是一些行爲,就是繼承。例如。我們會調用一個像process()這樣的方法來處理數據嗎?或者我們是否想將這些結構本身用作數據?

class X { 
    Base base; 
    A a; 
    AA aa; 
    process() { 
     // this is different than calling base.process() + a.process() + aa.process() 
     // do we need one over the other? both? 
     process(base) + process(a) + process(aa); 
    } 
} 

點4:關於初始化順序一邊讀書,這不應該是一個問題。也許你可以在臨時存儲它時讀取這些信息,並且只有在知道它的完整類型(即達到最後一級)後才能實例化一個類。

我希望幫助

+0

我已經更新了問題以澄清第1點。關於第4點,這不需要額外複製/讀取數據嗎? – user2363399

+0

@ user2363399它需要一個額外的副本是(所有實例不是每個實例)。另一種閱讀我不這麼認爲。我正在考慮一個例子:'A value1 = readValue(); value2 = readValue(); ...最後'新的AA(value1,value2,...);'對於下一個實例值可以重用。我不知道你的結構有多大,如果這是可能的。我只是提供想法 –

+0

這些將是可以從幾個字節到幾個kB/MB(或者甚至可能是GB,但是由於它們包含原始陣列而將我實現爲句柄的範圍)的批量結構。一般來說,我想保持數據連續以避免間接指向,但也許這是錯誤的? – user2363399

0

的問題沒有明確說明你認爲你正在做的,或者實際的問題是什麼(即你應該做的事情)。

你需要非常清楚地界定其A,AA型,AB型是實體用自己獨特的生存 - 而哪裏有子關係這你理應解析。你說嵌套結構,但沒有詳細說明。

作爲另一個提到的答案 - OO約爲行爲,而不是約數據建模

嚴重依賴繼承,特別是因爲你不知道你在構建什麼,聽起來像是一個完全的錯誤。當你需要行爲時(通過計算或做事的方法),一般的繼承問題只會有用,它可以有效地將行爲空間劃分爲一些類層次,並從中受益。

上面提到的問題只是一個解析問題。你可以使用堆棧和一些內部狀態(比如說一個StringBuilder,最簡單的方法)來讀取&建立解析狀態,同時使用堆棧來推動彈出嵌套層次的&。

實際上,以上是實現大多數解析器的好方法。

一個更復雜的選擇(在解析器中也很常見)是構建一個AST。這些都是非常高效的&輕量級構建&遍歷。

class AstNode { 
    protected AstNode down;  // first child. 
    protected AstNode across; // next sibling. 

    public void addChild (AstNode child) { 
     if (getDown() == null) { 
      // First Child; 
      this.down = child; 
      return; 
     } 
     // Sibling to existing Children. 
     AstNode last = down; 
     while (last.getAcross() != null) 
      last = last.getAcross(); 
     last.across = child; 
     // done. 
    } 
} 

隨着AST,你也可以把屬性/成員的節點類型,數據類型(詞彙)等,並有效地建立在自己的權利強大的數據結構。

希望這會有所幫助。

+0

是的,你說得對,解析是這個的重要組成部分。我認爲AST會過度殺傷,因爲這是一種二進制格式,沒有複雜的優先規則,或者在例如編程語言。我認爲某種樹可能是合適的。結構的每個部分都可以是樹中的一個節點,儘管這並不像我所希望的那樣真正利用語言類型系統。 – user2363399

+0

AST沒有體現優先規則或者支持它們的任何開銷,它只是存儲輸出的輕量和可靠的方式(有或沒有優先級)。基於棧的解析和解析器規則是實際應用語法規則和優先級的。 –

+0

利用類型系統?如果您將數據成員放入您的AST或(有時)數據和類型成員中,則可以在解析進一步發展後附加結構化對象,並在這些對象上使用語言的類型系統。效果很好。 –