用JAVA處理文本與位元據混合大檔案__JAVA
來源:互聯網
上載者:User
我們一般常見的檔案主要有三種形式:文字檔、位元據檔案、混合檔案。作為混合文檔的處理,特別是大型混合文檔的處理,開發人員面臨著特殊的挑戰: 其一,需要對位元據進行定位,並取得位元據內容而進行下一步處理; 其二,要根據位元據的上下文,取得位元據的相關背景資訊,才可能進行下一步處理; 其三,面對大型檔案,一次將所有資料統統讀入記憶體進行處理往往不現實。而在分次讀入檔案時,如何控制位元據的連續性和正確性,是一個不能忽視的問題。 本文即對如上三個問題進行一些探討,而提出用JAVA對大型位元據和文本混合檔案進行有效處理的一種可行方法。 關於XML混合檔案的處理方法,已有文章進行論述。而且,針對XML文檔處理,我們可以使用JAXP等等進行處理,本文對這些問題不再贅述。有興趣讀者請參閱 http://www-128.ibm.com/developerworks/cn/xml/x-wxxm33/。這裡,我們主要以PDF文檔為例,討論一下大型混合文檔的處理。 1、問題描述: 在pdf-1.3以後,pdf文檔中可以嵌入附件注釋,如下圖所示。 在對pdf文檔進行全文檢索索引的時候,如果不能對附件注釋的內容進行提取,則有可能不能檢索出帶有某個關鍵詞的pdf文檔。基於這種情況,我們有必要提取出pdf文檔中的附件注釋內容,根據實際需求,以決定對附件注釋內容如何進行全文檢索索引。 pdf-1.3的源檔案的一個案例如下圖所示。 2、關於JAVA類的選擇。 對於混合文檔處理,我們要考慮到兩個方面:其一為對常值內容的資訊提取,其二為位元據的資料提取。針對這兩個方面,我們則需要使用不同的JAVA類進行處理。 2.1 處理常值內容。 處理常值內容我們可以使用JAVA的BufferedReader類,使用案例如下: File f = new File(); BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(f))); 針對附件存在的位置,要結合pdf文檔的文檔定義結構進行分析。通過分析pdf文檔的每一行,找出含有附件的位置,以及附件名稱等等。從而為下一步位元據的提取做好準備。 *關於pdf文檔結構,請參見Acrobat首頁。 2.2 處理位元據 處理位元據不可以使用BufferedReader,否則提取的資料將不會保持原樣。所以,我們此時需要FileInputStream類。該類的使用案例如下: File f = new File(); FileInputStream fin = new FileInputStream(f); 然而,我們仍然需要對要提取的位元據進行定位,否則將錯誤的提取資料。此時,則需要對獲得的檔案輸入資料流進行字元比較,而找到要提取的位元據的開始和結束部分。進行字元比較,我們需要構造一個類似於下面的方法: int findStringInBuffer (byte[] buffer, char[] search, int buffersize, int offset)
{
int len = search.length;
int pos = 0, i;
boolean fnd = false;
while (!fnd)
{
fnd = true;
for (i=0; i<len; i++)
{
if ((char)buffer[offset + pos + i]!=search[i])
{
fnd = false;
break;
}
}
if (fnd) {
return pos;
}
pos++;
if (pos >= buffersize - len - offset) return -1;
}
return -1;
} 3. 在處理大型混合檔案時的挑戰和對策。 3.1 大型混合檔案的挑戰 如果該混合檔案較小,則可以通過把整個文檔讀入記憶體的方式,來迅速定位附件注釋位置並抽取出位元據。但是,面對大型混合檔案,我們則會有新的挑戰。挑戰主要包括如下幾個方面: 其一,如何選擇buffer的大小。如果buffer過小,可能會因為指示文本資料過長無法放入buffer中,而使得資料提取演算法失效;如果buffer過大,則可能會使得記憶體溢出; 其二,如何對目的文件進行分段,而正確的確定二進位檔案的位置,從而進行資料提取,也是一個不能忽略的問題。指示文本資料可能分屬不同的分段中,這樣如何恢複指示文本資料便成為關鍵; 其三,一個位元據流可能屬於連續的文檔段中,如何將位元據流合適的拼接成原來的資料,也是一個關鍵的問題。 針對這三個問題,我們可以有如下選擇對策。 3.2 合理選擇buffer大小。 在選擇buffer的時候,我們要確定對每一個位元據流的指示文本資料的長度。理論上講,只要buffer的長度大於指示文本資料的長度,則是適合的buffer資料長度。將輸入檔案流的資料讀入buffer中的一個案例如下: byte[] buffer = new byte[buffer_size]; int size = fin.read(buffer); size中存放的是buffer中實際讀取的字元數。 3.3 指示文本資料的拼接。 按照指示文本資料的長度,我們有必要設計首尾重疊的buffer來讀取文檔中的資料,並且每次唯寫入固定長度的位元據到輸出檔案中。首尾重疊的buffer的設計目的是正確實現指示文本資料的正確拼接,因為每一次讀入的輸入檔案流可能將指示文本資料截為兩半。首尾重疊的buffer的一個使用案例如下: for(int j = tmp_start; j < size; j++){
buffer[j - tmp_start] = tmp[j];
}
size = 0;
int bt;
for(int j = 0; (j < tmp_start) && ((bt = fin.read()) != -1); j++){
size++;
buffer[buffer_size - tmp_start + j] = (byte)bt;
}
size = size + buffer_size - tmp_start; 3.4 將連續buffer中的位元據拼接成完全的位元據。首先確定位元據的起始位置。由於位元據長度可能遠大於選擇的buffer長度,所以在讀取位元據時候,有必要確定位元據的結束位置。這涉及到二級制資料結束位置的指示文本資料位元置確定,其中可能需要事先指示文本資料的拼接。另外還要確定不要讀入錯誤的結束資料。這就需要確定結束指示文本資料的長度,在每一次將位元據寫入到輸出檔案中的時候,都要預留出這個結束指示文本資料的長度,而留待下次讀取。一個位元據讀取的案例如下: for(int j = 0; j < endstream_length; j++){
tmp[j] = buffer[size - endstream_length + j];
}
for(int j = 0; j < endstream_length; j++){
buffer[j] = tmp[j];
}
size = fin.read(ass_buf_4_endstream);
for(int j = 0; j < size; j++){
buffer[endstream_length + j] = ass_buf_4_endstream[j];
}
size = size + endstream_length; 4. 結語 綜合使用JAVA的FileInputStream和BufferedReader類,並對大型二進位和文本混合檔案進行合理的結構分析和進行演算法設計,則可以完成對大型二進位和文本混合檔案中的位元據進行處理的任務。本文提出了處理大型二進位和文本混合檔案的一種可行性方案,具有重要的實踐價值。 聯絡作者:wtcforever (a) 163.com