2009-09-06 41 views
5

Por ejemplo, digamos que desea eliminar de la matriz todos los segmentos continuos de 0 de más de 3 bytesJava: quitar segmento Continious de ceros del conjunto de bytes

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

resultado

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

I desea hacer algo como una expresión regular en Java, pero en una matriz de bytes en lugar de una cadena.

¿Hay algo que pueda ayudarme a incorporar (o hay una buena herramienta de terceros), o tengo que trabajar desde cero?

Las cadenas son UTF-16, por lo que la conversión de ida y vuelta no es una buena idea. Al menos es un montón de desperdicio sobrecarga ... ¿verdad?

+0

¿Qué tan importante es el rendimiento y uso de memoria para su caso de uso? En general, la RAM es barata y las CPU son rápidas. ¿Ha encontrado realmente un cuello de botella o se trata de preocuparse por la eficiencia? Puedes probarlo fácilmente convirtiendo byte [] en String usando una codificación de 8 bits, haz tu regexing y verifica el rendimiento. Después de todo, no nos preocupamos por cuán ineficientes son las cadenas de Java con sus caracteres de 16 bits para el uso normal en entornos ANSI, ¿no? –

+1

Es para una aplicación de alto rendimiento, estoy más preocupado por los ciclos que por el uso de ram. – Mike

+1

Todavía vale la evaluación comparativa; un Hotspot VM convertirá el código en zonas activas al código de máquina, que manejará los datos de 16 bits a la misma velocidad que los datos de 8 bits, ya que todo encaja en una palabra de máquina de 32 bits. Incluso si te parece demasiado lento, no habrás pasado mucho tiempo averiguándolo. –

Respuesta

1

expresión regular no es la herramienta para el trabajo, tendrá lugar necesario implementar que a partir de cero

-1

Java Regex funciona en CharSequences: puede CharBuffer para envolver su matriz de bytes existente (es posible que necesite convertirlo a char []?) E interpretarlo como tal, y luego realizar expresiones regulares sobre eso?

+0

Mala gramática, sin código, ni signos de interrogación de reemplazo Unicode ni preguntas de contador. Es difícil de entender para las personas que hacen tales [preguntas X/Y] (http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). Downvoted hasta que se mejore. –

1

No veo cómo la expresión regular sería útil para hacer lo que quiera. Una cosa que puedes hacer es usar Run Length Encoding para codificar esa matriz de bytes, reemplazar cada ocurrencia de "30" (leer tres 0) con la cadena vacía, y decodificar la cadena final. Wikipedia tiene una implementación simple de Java.

+1

Pensé que los 3 0s solo eran un ejemplo. –

1

Aunque hay una razonable ByteString biblioteca flotando alrededor, nadie que he visto ha puesto en marcha una biblioteca de expresión regular general sobre ellos.

Recomiendo la solución de su problema directamente en lugar de implementar una biblioteca de expresiones regulares :)

Si convertir a la cadena y la espalda, probablemente no encontrará ninguna codificación existente que le da una ida y vuelta para sus 0 bytes . Si ese es el caso, tendría que escribir su propia matriz de bytes < -> convertidores de cadenas; no vale la pena el problema.

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] 

I utilizarse ISO-8859-1 (latin1) porque, a diferencia de cualquier otra codificación,

  • cada byte en el intervalo 0x00..0xFF mapas a un carácter válido, y

  • cada de esos caracteres tiene el mismo valor numérico que su codificación latin1.

Eso significa que la cadena es la misma longitud que la matriz de bytes original, puede adaptarse a cualquier byte por su valor numérico con el \xFF construcción, y se puede convertir la cadena resultante de nuevo a una matriz de bytes sin perder información .

No trataría de mostrar datos mientras está en forma de cadena - aunque todos los caracteres son válidos, muchos de ellos no son imprimibles. Además, evite manipular los datos mientras está en forma de cadena; Es posible que accidentalmente realice sustituciones de secuencias de escape u otra conversión de codificación sin darse cuenta. De hecho, no quisiera recomendar haciendo este tipo de cosas, pero eso no es lo que preguntaste.:)

Además, tenga en cuenta que esta técnica no funcionará necesariamente en otros lenguajes de programación o sabores regex. Deberías probar cada uno individualmente.

+3

Eso es realmente inteligente. –

+1

Hacky. Me encanta :) –

0

Sugiero convertir la matriz de bytes en una cadena, realizar la expresión regular y luego convertirla. Aquí hay un ejemplo de trabajo:

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

Para transformaciones más complicadas, sugiero usar un Lexer. Tanto JavaCC como ANTLR tienen soporte para analizar/transformar archivos binarios.

8

Aunque me pregunto si reg-ex es la herramienta adecuada para el trabajo, si desea utilizar uno, le sugiero que implemente un contenedor CharSequence en una matriz de bytes. Algo como esto (acabo de escribir esto directamente en, no compilado ... pero entiendes la idea).

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

¡Implementé este enfoque y funcionó! Obviamente tienes que tener cuidado ya que no estás realizando ninguna decodificación de charset, pero para cosas como la detección de tipo de letra es perfecto. – sigpwned

0

La implementación utilizando una expresión regular, propuesto por otras respuestas, es hasta 8 veces más lento que una aplicación ingenua usando un bucle que las copias bytes desde la matriz de entrada a una matriz de salida.

La implementación copia un byte de matriz de entrada por byte. Si se detectó una secuencia cero, el índice de la matriz de salida se reduce (rebobinado). Después de procesar la matriz de entrada, la matriz de salida incluso se copia una vez más para recortar su longitud a la cantidad real de bytes, ya que la matriz de salida intermedia se inicializa con la longitud de la matriz de entrada.

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

Un segundo enfoque que impida copias innecesarias rebobinando al primer byte cero (de tres o menos) y la copia de esos elementos fue curiosamente un poco más lento que el primer enfoque.

Las tres implementaciones se probaron en un procesador Pentium N3700 con 1,000 iteraciones en una matriz de entrada de 8 x 32KB con varias cantidades y longitudes de cero secuencias. La peor mejora del rendimiento en comparación con el enfoque de expresión regular fue 1.5x más rápido.

El banco de pruebas completo se puede encontrar aquí: https://pastebin.com/83q9EzDc

Cuestiones relacionadas