2009-09-06 73 views
5

例如,假設我想從陣列中刪除0的所有連續段超過3個字節的Java:從字節數組中刪除零continious段

byte a[] = {1,2,3,0,1,2,3,0,0,0,0,4}; 
byte r[] = magic(a); 
System.out.println(r); 

結果

{1,2,3,0,1,2,3,4} 

我不再想要在Java中執行類似於正則表達式的操作,但要使用字節數組而不是字符串。

有什麼可以幫助我內置(或有一個很好的第三方工具),還是我需要從頭開始工作?

字符串是UTF-16,所以來回轉換不是一個好主意嗎?至少它有很多浪費的開銷......對吧?

+0

如何關鍵的是性能和內存使用爲您的使用情況?一般來說,RAM很便宜,而且CPU速度很快。你真的發現了一個瓶頸,還是擔心效率?您可以通過使用8位編碼將字節[]轉換爲字符串來輕鬆地進行測試,執行重新編程並檢查性能。畢竟,我們並不擔心在ANSI環境中正常使用效率低下的16位字符串Java字符串,對嗎? – 2009-09-07 00:00:59

+1

這是一個高性能的應用程序,我比ram的使用更擔心週期。 – Mike 2009-09-07 00:02:41

+1

這仍然值得標杆; Hotspot虛擬機會將熱點中的代碼轉換爲機器代碼,它將以與8位數據相同的速度處理16位數據,因爲它全部適合32位機器字。即使你發現它太慢,你也不會花太多時間找出它。 – 2009-09-07 00:08:05

回答

1

正則表達式是不適合工作的工具,你反而需要實現從無到有

-1

Java正則表達式在CharSequences上運行 - 你可以用CharBuffer來包裝你現有的字節數組(你可能需要將它轉換爲char []?)並解釋它,然後對它執行regex?

+0

語法錯誤,沒有代碼,不是Unicode替換問號就是反問題。很難理解詢問這些[X/Y問題]的人(http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem)。 Downvoted直到改進。 – 2017-07-30 10:49:34

1

我不明白正則表達式對於做你想做的事很有用。你可以做的一件事是使用Run Length Encoding來編碼該字節數組,用空字符串替換「30」(讀取三個0)的每一次出現,並且解碼最後的字符串。維基百科有一個簡單的Java實現。

+1

我以爲3 0就是一個例子。 – 2009-09-06 23:57:31

1

雖然有一個合理的ByteString庫左右浮動,我已經看到了實現他們的通用正則表達式庫人。

我建議解決您的問題,而不是直接實現正則表達式庫:)

如果轉換爲字符串和背部,你可能不會發現任何現有的編碼,讓您往返您的0字節。如果是這樣的話,你必須編寫自己的字節數組< - >字符串轉換器;不值得麻煩。

24
byte[] a = {1,2,3,0,1,2,3,0,0,0,0,4}; 
String s0 = new String(a, "ISO-8859-1"); 
String s1 = s0.replaceAll("\\x00{4,}", ""); 
byte[] r = s1.getBytes("ISO-8859-1"); 

System.out.println(Arrays.toString(r)); // [1, 2, 3, 0, 1, 2, 3, 4] 

我用ISO-8859-1(latin1的),因爲,不同於任何其他編碼,

  • 在範圍內的每個字節0x00..0xFF映射到一個有效的字符,和

  • 每個這些字符與其latin1編碼具有相同的數值。

這意味着該字符串是相同的長度與原始字節數組,可以通過它的數字值與所述\xFF構建體相匹配的任何字節,並且可以將得到的字符串轉換回一個字節數組,而不會丟失信息。

我不會試圖顯示數據,雖然它是字符串形式 - 雖然所有的字符都是有效的,但其中許多是不可打印的。另外,避免在數據處於字符串形式時操作數據;您可能會意外地執行一些轉義序列替換或其他編碼轉換而不會意識到它。事實上,我不會推薦做這種事情,但這不是你問的。:)

此外,請注意,此技術不一定適用於其他編程語言或正則表達式。你將不得不逐一測試每一個。

+3

這真的很聰明。 – 2014-08-28 07:42:18

+1

哈克。我喜歡它:) – 2015-02-12 15:03:04

0

我建議將字節數組轉換爲字符串,執行正則表達式,然後將其轉換回來。這是一個工作示例:

public void testRegex() throws Exception { 
    byte a[] = { 1, 2, 3, 0, 1, 2, 3, 0, 0, 0, 0, 4 }; 
    String s = btoa(a); 
    String t = s.replaceAll("\u0000{4,}", ""); 
    byte b[] = atob(t); 
    System.out.println(Arrays.toString(b)); 
} 

private byte[] atob(String t) { 
    char[] array = t.toCharArray(); 
    byte[] b = new byte[array.length]; 
    for (int i = 0; i < array.length; i++) { 
     b[i] = (byte) Character.toCodePoint('\u0000', array[i]); 
    } 
    return b; 
} 

private String btoa(byte[] a) { 
    StringBuilder sb = new StringBuilder(); 
    for (byte b : a) { 
     sb.append(Character.toChars(b)); 
    } 
    return sb.toString(); 
} 

對於更復雜的轉換,我建議使用Lexer。 JavaCC和ANTLR都支持解析/轉換二進制文件。

8

雖然我質疑reg-ex是否是正確的工具,但如果您確實想使用它,我建議您只在字節數組上實現CharSequence包裝。就像這樣(我只是直接寫了這個,沒有編譯...但你明白了)。

public class ByteChars 
implements CharSequence 

... 

ByteChars(byte[] arr) { 
    this(arr,0,arr.length); 
    } 

ByteChars(byte[] arr, int str, int end) { 
    //check str and end are within range here 
    strOfs=str; 
    endOfs=end; 
    bytes=arr; 
    } 

public char charAt(int idx) { 
    //check idx is within range here 
    return (char)(bytes[strOfs+idx]&0xFF); 
    } 

public int length() { 
    return (endOfs-strOfs); 
    } 

public CharSequence subSequence(int str, int end) { 
    //check str and end are within range here 
    return new ByteChars(arr,(strOfs+str,strOfs+end); 
    } 

public String toString() { 
    return new String(bytes,strOfs,(endOfs-strOfs),"ISO8859_1"); 
    } 
+0

我實施了這種方法,它工作得很好!顯然,你必須小心,因爲你沒有執行任何字符集解碼,但是對於doctype檢測等,它是完美的。 – sigpwned 2015-03-08 15:35:11

0

利用正則表達式,通過其他的答案提出的實施方式中,比使用一個循環,複製從輸入陣列到輸出陣列字節的幼稚實現較慢的高達8倍。

該實現複製一個字節的輸入數組。如果檢測到零序列,則會減少輸出數組索引(倒回)。處理完輸入數組後,輸出數組甚至會被複制一次以將其長度修剪爲實際的字節數,因爲中間輸出數組是用輸入數組的長度初始化的。

/** 
* Remove four or more zero byte sequences from the input array. 
* 
* @param inBytes the input array 
* @return a new array with four or more zero bytes removed form the input array 
*/ 
private static byte[] removeDuplicates(byte[] inBytes) { 
    int size = inBytes.length; 
    // Use an array with the same size in the first place 
    byte[] newBytes = new byte[size]; 
    byte value; 
    int newIdx = 0; 
    int zeroCounter = 0; 

    for (int i = 0; i < size; i++) { 
     value = inBytes[i]; 

     if (value == 0) { 
      zeroCounter++; 
     } else { 
      if (zeroCounter >= 4) { 
       // Rewind output buffer index 
       newIdx -= zeroCounter; 
      } 

      zeroCounter = 0; 
     } 

     newBytes[newIdx] = value; 
     newIdx++; 
    } 

    if (zeroCounter >= 4) { 
     // Rewind output buffer index for four zero bytes at the end too 
     newIdx -= zeroCounter; 
    } 

    // Copy data into an array that has the correct length 
    byte[] finalOut = new byte[newIdx]; 
    System.arraycopy(newBytes, 0, finalOut, 0, newIdx); 

    return finalOut; 
} 

將防止不必要的副本通過倒帶至(三個或更少)的第一零字節和複製的那些元件是有趣的是比第一種方法更慢一點的第二種方法。

在Pentium N3700處理器上對所有三種實現進行了測試,在一個8 x 32KB輸入陣列上進行1,000次迭代,其中有幾個量和長度爲零的序列。與正則表達式方法相比,性能最差的改進是速度更快的爲012x。

完整的試驗檯可以在這裏找到:https://pastebin.com/83q9EzDc