2009-09-06 41 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