如何在Java中围绕字节序列拆分字节数组?

时间:2014-03-19 22:17:49

标签: java

如何在Java中的字节序列周围分割byte[]?类似于String#split(regex)byte[]版本。

实施例

让我们取这个字节数组:
[11 11 FF FF 22 22 22 FF FF 33 33 33 33]

让我们选择分隔符为
[FF FF]

然后分裂将导致这三个部分:
[11 11]
[22 22 22]
[33 33 33 33]

编辑:

请注意,由于编码问题,您无法将byte[]转换为String,然后将其拆分,然后返回。在字节数组上执行此类转换时,生成的byte[]将不同。请参考: Conversion of byte[] into a String and then back to a byte[]

7 个答案:

答案 0 :(得分:10)

这是一个简单的解决方案。

与avgvstvs方法不同,它处理任意长度分隔符。最佳答案也很好,但作者没有解决Eitan Perkal指出的问题。使用Perkal建议的方法可以避免这个问题。

public static List<byte[]> tokens(byte[] array, byte[] delimiter) {
        List<byte[]> byteArrays = new LinkedList<>();
        if (delimiter.length == 0) {
            return byteArrays;
        }
        int begin = 0;

        outer:
        for (int i = 0; i < array.length - delimiter.length + 1; i++) {
            for (int j = 0; j < delimiter.length; j++) {
                if (array[i + j] != delimiter[j]) {
                    continue outer;
                }
            }
            byteArrays.add(Arrays.copyOfRange(array, begin, i));
            begin = i + delimiter.length;
        }
        byteArrays.add(Arrays.copyOfRange(array, begin, array.length));
        return byteArrays;
    }

答案 1 :(得分:7)

请注意,如果使用编码&#34; iso8859-1&#34;

但是,它仍然是一个丑陋的解决方案。

我认为你需要自己动手。

我建议分两个阶段来解决它:

  1. 找出如何查找每个分隔符的索引。 Google for&#34; Knuth-Morris-Pratt&#34;对于一个有效的算法 - 虽然一个更天真的算法将适用于短分隔符。
  2. 每次找到索引时,请使用Arrays.copyOfRange()获取所需的部分并将其添加到输出列表中。
  3. 这里使用的是朴素的模式发现算法。如果分隔符很长,KMP将变得有价值(因为它可以节省回溯,但如果它们按顺序嵌入到最后不匹配的顺序中,则不会错过分隔符。)

    public static boolean isMatch(byte[] pattern, byte[] input, int pos) {
        for(int i=0; i< pattern.length; i++) {
            if(pattern[i] != input[pos+i]) {
                return false;
            }
        }
        return true;
    }
    
    public static List<byte[]> split(byte[] pattern, byte[] input) {
        List<byte[]> l = new LinkedList<byte[]>();
        int blockStart = 0;
        for(int i=0; i<input.length; i++) {
           if(isMatch(pattern,input,i)) {
              l.add(Arrays.copyOfRange(input, blockStart, i));
              blockStart = i+pattern.length;
              i = blockStart;
           }
        }
        l.add(Arrays.copyOfRange(input, blockStart, input.length ));
        return l;
    }
    

答案 2 :(得分:2)

我修改了'L. Blanc的答案是在开始和结束时处理分隔符。另外,我将其重命名为“拆分”。

private List<byte[]> split(byte[] array, byte[] delimiter)
{
   List<byte[]> byteArrays = new LinkedList<byte[]>();
   if (delimiter.length == 0)
   {
      return byteArrays;
   }
   int begin = 0;

   outer: for (int i = 0; i < array.length - delimiter.length + 1; i++)
   {
      for (int j = 0; j < delimiter.length; j++)
      {
         if (array[i + j] != delimiter[j])
         {
            continue outer;
         }
      }

      // If delimiter is at the beginning then there will not be any data.
      if (begin != i)
         byteArrays.add(Arrays.copyOfRange(array, begin, i));
      begin = i + delimiter.length;
   }

   // delimiter at the very end with no data following?
   if (begin != array.length)
      byteArrays.add(Arrays.copyOfRange(array, begin, array.length));

   return byteArrays;
}

答案 3 :(得分:0)

滚动自己是唯一的方式去这里。如果您对非标准库开放,我可以提供的最好的想法是来自Apache的这个类:

http://commons.apache.org/proper/commons-primitives/apidocs/org/apache/commons/collections/primitives/ArrayByteList.html

Knuth的解决方案可能是最好的,但我会将数组视为堆栈并执行以下操作:

List<ArrayByteList> targetList = new ArrayList<ArrayByteList>();
while(!stack.empty()){
  byte top = stack.pop();
  ArrayByteList tmp = new ArrayByteList();

  if( top == 0xff && stack.peek() == 0xff){
    stack.pop();
    continue;
  }else{
    while( top != 0xff ){
      tmp.add(stack.pop());
    }
    targetList.add(tmp);
  }
}

我知道这很快且很脏,但在所有情况下都应该提供O(n)。

答案 4 :(得分:0)

https://stackoverflow.com/a/44468124/1291605的答案Roger有一些改进: 假设我们有这样的数组||||aaa||bbb和定界符||。在这种情况下,我们得到

java.lang.IllegalArgumentException: 2 > 1
    at java.util.Arrays.copyOfRange(Arrays.java:3519)

因此,最终的改进方案是:

public static List<byte[]> split(byte[] array, byte[] delimiter) {
        List<byte[]> byteArrays = new LinkedList<>();
        if (delimiter.length == 0) {
            return byteArrays;
        }
        int begin = 0;

        outer:
        for (int i = 0; i < array.length - delimiter.length + 1; i++) {
            for (int j = 0; j < delimiter.length; j++) {
                if (array[i + j] != delimiter[j]) {
                    continue outer;
                }
            }

            // This condition was changed
            if (begin != i)
                byteArrays.add(Arrays.copyOfRange(array, begin, i));
            begin = i + delimiter.length;
        }

        // Also here we may change condition to 'less'
        if (begin < array.length)
            byteArrays.add(Arrays.copyOfRange(array, begin, array.length));

        return byteArrays;
    }

答案 5 :(得分:-2)

您可以使用Arrays.copyOfRange()

答案 6 :(得分:-4)

请参阅Java Doc for String

您可以从String数组构造一个byte对象。猜猜你知道其余的。

public static byte[][] splitByteArray(byte[] bytes, byte[] regex, Charset charset) {
    String str = new String(bytes, charset);
    String[] split = str.split(new String(regex, charset));
    byte[][] byteSplit = new byte[split.length][];
    for (int i = 0; i < split.length; i++) {
        byteSplit[i] = split[i].getBytes(charset);
    }
    return byteSplit;
}

public static void main(String[] args) {
    Charset charset = Charset.forName("UTF-8");
    byte[] bytes = {
        '1', '1', ' ', '1', '1',
        'F', 'F', ' ', 'F', 'F',
        '2', '2', ' ', '2', '2', ' ', '2', '2',
        'F', 'F', ' ', 'F', 'F',
        '3', '3', ' ', '3', '3', ' ', '3', '3', ' ', '3', '3'
    };
    byte[] regex = {'F', 'F', ' ', 'F', 'F'};
    byte[][] splitted = splitByteArray(bytes, regex, charset);
    for (byte[] arr : splitted) {
        System.out.print("[");
        for (byte b : arr) {
            System.out.print((char) b);
        }
        System.out.println("]");
    }
}