2009-09-06 19 views
5

Ví dụ, giả sử tôi muốn xóa từ mảng tất cả các phân khúc liên tục từ 0 của dài hơn 3 byteJava: loại bỏ phân khúc continious các zeros từ mảng byte

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

kết quả

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

tôi muốn làm một cái gì đó giống như một biểu thức chính quy trong Java, nhưng trên một mảng byte thay vì một String.

Có điều gì đó có thể giúp tôi tích hợp (hoặc có công cụ bên thứ ba tốt) hay tôi cần làm việc từ đầu?

Chuỗi là UTF-16, do đó, chuyển đổi qua lại không phải là một ý tưởng hay? Ít nhất đó là rất nhiều chi phí lãng phí ... phải không?

+0

Làm thế nào quan trọng là hiệu suất và sử dụng bộ nhớ đối với trường hợp sử dụng của bạn? Nói chung, RAM rẻ và CPU nhanh. Bạn đã thực sự tìm thấy một nút cổ chai hay nó là một trường hợp đáng lo ngại về hiệu quả? Bạn có thể dễ dàng thử nó bằng cách chuyển đổi byte [] thành String bằng cách sử dụng mã hóa 8 bit, thực hiện regexing của bạn và kiểm tra hiệu suất. Sau khi tất cả, chúng tôi không lo lắng về cách các chuỗi Java không hiệu quả với các ký tự 16 bit của chúng là để sử dụng bình thường trong các môi trường ANSI, phải không? –

+1

Đó là một ứng dụng hiệu suất cao, tôi lo lắng nhiều hơn về chu kỳ so với sử dụng ram. – Mike

+1

Nó vẫn có giá trị điểm chuẩn; một Hotspot VM sẽ chuyển đổi mã trong các điểm nóng thành mã máy, sẽ xử lý dữ liệu 16 bit ở cùng tốc độ với dữ liệu 8 bit vì tất cả đều khớp với một từ máy 32 bit. Ngay cả khi bạn tìm thấy nó quá chậm, bạn sẽ không phải mất nhiều thời gian để tìm ra nó. –

Trả lời

1

regex không phải là công cụ cho công việc, bạn sẽ thay cần phải thực hiện điều đó từ đầu

-1

Java Regex hoạt động trên CharSequences - bạn có thể CharBuffer để bọc mảng byte hiện tại của mình (bạn có thể cần truyền nó thành char []?) Và diễn giải nó như vậy, sau đó thực hiện regex?

+0

ngữ pháp xấu, không có mã, hoặc là dấu hỏi thay thế Unicode hoặc câu hỏi truy cập. Khó hiểu đối với những người hỏi [câu hỏi X/Y] (http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). Downvoted cho đến khi được cải thiện. –

1

Tôi không thấy làm thế nào regex sẽ hữu ích để làm những gì bạn muốn. Một điều bạn có thể làm là sử dụng Run Length Encoding để mã hóa mảng byte đó, thay thế mọi ocurrence của "30" (đọc ba 0) bằng chuỗi rỗng và giải mã chuỗi cuối cùng. Wikipedia có một thực thi Java đơn giản.

+1

Tôi nghĩ rằng 3 0 chỉ là một ví dụ. –

1

Mặc dù có một lý ByteString thư viện nổi xung quanh, không ai mà tôi đã nhìn thấy đã thực hiện một thư viện regexp chung trên chúng.

Tôi khuyên bạn nên giải quyết vấn đề của bạn trực tiếp chứ không phải là thực hiện một thư viện regexp :)

Nếu bạn chuyển sang chuỗi và ngược lại, có thể bạn sẽ không tìm thấy bất kỳ mã hóa hiện có cung cấp cho bạn một chuyến đi vòng 0 byte của bạn . Nếu đó là trường hợp, bạn sẽ phải viết mảng byte của riêng bạn < -> trình biến đổi chuỗi; không đáng để gặp rắc rối.

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] 

tôi sử dụng tiêu chuẩn ISO-8859-1 (latin1) bởi vì, không giống như bất kỳ mã hóa khác,

  • mỗi byte trong phạm vi 0x00..0xFF bản đồ để một nhân vật có giá trị, và

  • mỗi các ký tự đó có cùng giá trị số như mã hóa latin1 của nó.

đó có nghĩa là chuỗi là chiều dài tương tự như mảng byte gốc, bạn có thể phù hợp với bất kỳ byte bởi giá trị số của nó với \xFF xây dựng, và bạn có thể chuyển đổi chuỗi kết quả trở lại một mảng byte mà không làm mất thông tin .

Tôi sẽ không cố hiển thị hiển thị dữ liệu khi ở dạng chuỗi - mặc dù tất cả các ký tự hợp lệ, nhiều ký tự không thể in được. Ngoài ra, tránh thao tác dữ liệu trong khi nó ở dạng chuỗi; bạn có thể vô tình thực hiện một số thay thế chuỗi thoát hoặc một chuyển đổi mã hóa khác mà không nhận ra nó. Trong thực tế, tôi sẽ không khuyên bạn nên làm điều này ở tất cả, nhưng đó không phải là những gì bạn yêu cầu.:)

Ngoài ra, hãy lưu ý rằng kỹ thuật này không nhất thiết phải hoạt động ở các ngôn ngữ lập trình khác hoặc hương vị regex. Bạn sẽ phải kiểm tra từng cá nhân.

+3

Điều đó thực sự thông minh. –

+1

Hacky. Tôi thích nó :) –

0

Tôi khuyên bạn nên chuyển đổi mảng byte thành chuỗi, thực hiện regex và sau đó chuyển đổi lại. Dưới đây là ví dụ hoạt động:

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(); 
} 

Để chuyển đổi phức tạp hơn, tôi khuyên bạn nên sử dụng Lexer. Cả JavaCC và ANTLR đều hỗ trợ phân tích cú pháp/chuyển đổi các tệp nhị phân.

8

Mặc dù tôi đặt câu hỏi liệu reg-ex có phải là công cụ thích hợp cho công việc không, nếu bạn muốn sử dụng công cụ này, tôi khuyên bạn chỉ cần triển khai trình bao bọc CharSequence trên một mảng byte. Một cái gì đó như thế này (tôi chỉ viết này trực tiếp trong, không biên dịch ... nhưng bạn có được ý tưởng).

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

Tôi thực hiện phương pháp này và nó đã làm việc một điều trị! Rõ ràng bạn phải cẩn thận vì bạn không thực hiện bất kỳ giải mã ký tự nào, nhưng đối với những thứ như phát hiện doctype thì nó hoàn hảo. – sigpwned

0

Việc thực hiện sử dụng một Regular Expression, bởi câu trả lời khác đề xuất, là chậm hơn so với một thực hiện ngây thơ sử dụng một vòng lặp cho phép sao chép byte từ mảng đầu vào cho một mảng sản lượng lên đến 8 lần.

Việc triển khai sao chép byte mảng đầu vào theo byte. Nếu một chuỗi số không được phát hiện, chỉ số mảng đầu ra sẽ bị giảm (tua lại). Sau khi xử lý mảng đầu vào, mảng đầu ra thậm chí được sao chép một lần nữa để cắt chiều dài của nó thành số byte thực tế vì mảng đầu ra trung gian được khởi tạo với độ dài của mảng đầu vào.

/** 
* 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; 
} 

Cách tiếp cận thứ hai có thể ngăn chặn các bản sao không cần thiết bằng cách tua lại byte zero thứ nhất (ba hoặc ít hơn) và sao chép các yếu tố đó thú vị hơn một chút so với phương pháp đầu tiên.

Tất cả ba triển khai đã được thử nghiệm trên bộ vi xử lý Pentium N3700 với 1.000 lần lặp trên một mảng đầu vào 8 x 32KB với một số lượng và độ dài của chuỗi không. Cải thiện hiệu suất kém nhất so với phương pháp Express Expression là 1.5x nhanh hơn.

Các thử nghiệm giàn đầy đủ có thể được tìm thấy ở đây: https://pastebin.com/83q9EzDc

Các vấn đề liên quan