2016-11-09 67 views
1

我有一個Go程序與輸入流一起工​​作,即os.Stdin:非常大的XML文件,所以我無法處理這一切都一次。檢索元素的「outerxml」(如innerxml,但包括元素本身)

我想提取所有特定屬性的XML元素進行後期處理。

我很難確定提取元素,並獲取相關的開始和結束元素。不過,我不知道如何將整個元素作爲字符串來轉儲,而不是僅將內部XML轉儲。

例如,假設我有以下XML:

<a> 
    <b somethingUseful="1"> 
    <c>Hello</c> 
    <d>world</d> 
    </b> 
    <e> 
    <foo/> 
    </e> 
    <!-- Imagine there were 1 billion lines in between - 
     I need to stream this! --> 
    <b somethingUseful="321"> 
    <c>Hello again</c> 
    </b> 
</a> 

在這個例子中,我想每個<b>元素的輸出,從開始到結束。

使用innerxmlDecodeElement,我能走這麼遠,以流方式:

Here comes a B: 

    <c>Hello</c> 
    <d>world</d> 

Here comes a B: 

    <c>Hello again</c> 

如此接近,但它缺少<b>標記(和屬性)本身。我一直沒能弄清楚如何在不犧牲解碼的流式性質的情況下做出最後一步。

要清楚,是我想要的輸出是一樣的東西:

Here comes a B: 
    <b somethingUseful="1"> 
    <c>Hello</c> 
    <d>world</d> 
    </b> 
Here comes a B: 
    <b somethingUseful="321"> 
    <c>Hello again</c> 
    </b> 

下面是所宣佈的那樣這個例子中操場,什麼我在得到這個地步完成:

https://play.golang.org/p/XqJY_1pa9j

回答

2

通過@ nothingmuch年代decoder.InputOffset使用的啓發,我用一個TeeReader分割輸入Reader分爲兩個:它獲取通過解碼器解析的標準,那我們將用它來輸出確切元素(它位於之間的緩衝遇到元素前後的decoder.InputOffset)。

爲了最大限度地減少內存使用量,緩衝區不斷被清除只能達到我們所知的不可能匹配的點。我們維持抵消以跟蹤這一點。這增加了複雜性是必要的,因爲解碼器可以在讀取器前面抓取讀取器中的字節,所以我們需要小心不要清除我們實際需要的東西。

因此額外的內存使用率只有不亞於:

  1. 可能之前它清除回到一個可以同時存儲在緩衝區中的最大的兩個標記。
  2. 正在輸出的實際元素的大小。

這裏有一個更新的操場與解決方案:

https://play.golang.org/p/H8WVDWI57r

1

一個相當粗糙的方法是通過在解碼器開始元素之前和結束元素之後詢問解碼器the offset來保存偏移量並只讀取這些字節。

請參閱this playground example,它將讀者分爲兩個管道,其中一個管道進入XML解碼器,另一個管道被緩衝,然後用於提取對應於XML元素的字節範圍。

然後,XML解碼例程在通道上寫入偏移對,而另一個線程用來跳過或輸出來自讀取器流副本的感興趣區域。這應該比我做的黑客工作更嚴肅,比如使用堆棧和匹配過濾標準。

這個解決方案假設Seek/ReadAt不可行,回想起來我可能會在那裏超過它,如果你只是打開文件兩次,假設它是一個文件,這會更簡單。

+1

噢噢噢,這是一個很好的開始!我會尋找任何方法讓它變得更漂亮並回復你:D非常感謝! –

+0

假設我們在這裏使用'os.Stdin' - 所以你不能將它作爲兩個文件打開(除非有辦法做到這一點?:O),但如果你很棘手,你可以做類似的事情。看起來這就是多功能打印機正在做的事情。 –

+0

啊,那麼是的,我想你會需要複製到'ioutil.Discard'愚蠢 – nothingmuch