web-dev-qa-db-fra.com

Rechercher l'index d'un tableau d'octets dans un autre tableau d'octets

Étant donné un tableau d'octets, comment puis-je trouver à l'intérieur, la position d'un (plus petit) tableau d'octets?

Cette documentation semblait prometteuse, en utilisant ArrayUtils, mais si je me trompe, cela me permettrait seulement de trouver un octet individuel dans le tableau à rechercher.

(Je ne vois pas que cela importe, mais juste au cas où: parfois le tableau d'octets de recherche sera régulier ASCII caractères, d'autres fois ce sera des caractères de contrôle ou étendu ASCII caractères. L'utilisation des opérations String ne serait donc pas toujours appropriée)

Le grand tableau peut être compris entre 10 et environ 10000 octets, et le plus petit autour de 10. Dans certains cas, j'ai plusieurs tableaux plus petits que je veux trouver dans le plus grand tableau lors d'une seule recherche. Et je vais parfois vouloir trouver le dernier index d'une instance plutôt que le premier.

28
CL22

Les chaînes Java sont composées de chars 16 bits, et non de bytes 8 bits. Un char peut contenir un byte, vous pouvez donc toujours transformer vos tableaux d'octets en chaînes et utiliser indexOf: ASCII caractères, contrôle caractères, et même aucun caractère fonctionnera correctement.

Voici une démo:

byte[] big = new byte[] {1,2,3,0,4,5,6,7,0,8,9,0,0,1,2,3,4};
byte[] small = new byte[] {7,0,8,9,0,0,1};
String bigStr = new String(big, StandardCharsets.UTF_8);
String smallStr = new String(small, StandardCharsets.UTF_8);
System.out.println(bigStr.indexOf(smallStr));

Ceci imprime 7.

Cependant, étant donné que votre grand tableau peut atteindre 10 000 octets et que le petit tableau ne fait que dix octets, cette solution n'est peut-être pas la plus efficace, pour deux raisons:

  • Cela nécessite de copier votre grand tableau dans un tableau deux fois plus grand (même capacité, mais avec char au lieu de byte). Cela triple vos besoins en mémoire.
  • Algorithme de recherche de chaînes de Java n'est pas le plus rapide disponible. Vous pouvez être suffisamment rapide si vous implémentez l'un des algorithmes avancés, par exemple, le Knuth – Morris – Pratt = un. Cela pourrait potentiellement réduire la vitesse d'exécution d'un facteur pouvant aller jusqu'à dix (la longueur de la petite chaîne), et nécessitera une mémoire supplémentaire proportionnelle à la longueur de la petite chaîne, pas de la grande chaîne.
1
dasblinkenlight

La manière la plus simple serait de comparer chaque élément:

public int indexOf(byte[] outerArray, byte[] smallerArray) {
    for(int i = 0; i < outerArray.length - smallerArray.length+1; ++i) {
        boolean found = true;
        for(int j = 0; j < smallerArray.length; ++j) {
           if (outerArray[i+j] != smallerArray[j]) {
               found = false;
               break;
           }
        }
        if (found) return i;
     }
   return -1;  
}  

Quelques tests:

@Test
public void testIndexOf() {
  byte[] outer = {1, 2, 3, 4};
  assertEquals(0, indexOf(outer, new byte[]{1, 2}));
  assertEquals(1, indexOf(outer, new byte[]{2, 3}));
  assertEquals(2, indexOf(outer, new byte[]{3, 4}));
  assertEquals(-1, indexOf(outer, new byte[]{4, 4}));
  assertEquals(-1, indexOf(outer, new byte[]{4, 5}));
  assertEquals(-1, indexOf(outer, new byte[]{4, 5, 6, 7, 8}));
}

Lorsque vous avez mis à jour votre question: Java Les chaînes sont des chaînes UTF-16, elles ne se soucient pas du jeu étendu ASCII, vous pouvez donc utiliser string.indexOf ( )

36
morpheus05

La goyave de Google fournit un Bytes.indexOf (tableau d'octets [], cible d'octets []).

22
ant-depalma

Est-ce ce que vous recherchez?

public class KPM {
    /**
     * Search the data byte array for the first occurrence of the byte array pattern within given boundaries.
     * @param data
     * @param start First index in data
     * @param stop Last index in data so that stop-start = length
     * @param pattern What is being searched. '*' can be used as wildcard for "ANY character"
     * @return
     */
    public static int indexOf( byte[] data, int start, int stop, byte[] pattern) {
        if( data == null || pattern == null) return -1;

        int[] failure = computeFailure(pattern);

        int j = 0;

        for( int i = start; i < stop; i++) {
            while (j > 0 && ( pattern[j] != '*' && pattern[j] != data[i])) {
                j = failure[j - 1];
            }
            if (pattern[j] == '*' || pattern[j] == data[i]) {
                j++;
            }
            if (j == pattern.length) {
                return i - pattern.length + 1;
            }
        }
        return -1;
    }

    /**
     * Computes the failure function using a boot-strapping process,
     * where the pattern is matched against itself.
     */
    private static int[] computeFailure(byte[] pattern) {
        int[] failure = new int[pattern.length];

        int j = 0;
        for (int i = 1; i < pattern.length; i++) {
            while (j>0 && pattern[j] != pattern[i]) {
                j = failure[j - 1];
            }
            if (pattern[j] == pattern[i]) {
                j++;
            }
            failure[i] = j;
        }

        return failure;
    }
}
6
MonoThreaded

Pour gagner du temps lors des tests:

http://helpdesk.objects.com.au/Java/search-a-byte-array-for-a-byte-sequence

vous donne du code qui fonctionne si vous rendez computeFailure () statique:

public class KPM {
    /**
     * Search the data byte array for the first occurrence 
     * of the byte array pattern.
     */
    public static int indexOf(byte[] data, byte[] pattern) {
    int[] failure = computeFailure(pattern);

    int j = 0;

    for (int i = 0; i < data.length; i++) {
        while (j > 0 && pattern[j] != data[i]) {
            j = failure[j - 1];
        }
        if (pattern[j] == data[i]) { 
            j++; 
        }
        if (j == pattern.length) {
            return i - pattern.length + 1;
        }
    }
    return -1;
    }

    /**
     * Computes the failure function using a boot-strapping process,
     * where the pattern is matched against itself.
     */
    private static int[] computeFailure(byte[] pattern) {
    int[] failure = new int[pattern.length];

    int j = 0;
    for (int i = 1; i < pattern.length; i++) {
        while (j>0 && pattern[j] != pattern[i]) {
            j = failure[j - 1];
        }
        if (pattern[j] == pattern[i]) {
            j++;
        }
        failure[i] = j;
    }

    return failure;
    }
}

Puisqu'il est toujours sage de tester le code que vous empruntez, vous pouvez commencer par:

public class Test {
    public static void main(String[] args) {
        do_test1();
    }
    static void do_test1() {
      String[] ss = { "",
                    "\r\n\r\n",
                    "\n\n",
                    "\r\n\r\nthis is a test",
                    "this is a test\r\n\r\n",
                    "this is a test\r\n\r\nthis si a test",
                    "this is a test\r\n\r\nthis si a test\r\n\r\n",
                    "this is a test\n\r\nthis si a test",
                    "this is a test\r\nthis si a test\r\n\r\n",
                    "this is a test"
                };
      for (String s: ss) {
        System.out.println(""+KPM.indexOf(s.getBytes(), "\r\n\r\n".getBytes())+"in ["+s+"]");
      }

    }
}
5

En utilisant le Knuth–Morris–Pratt algorithm est le moyen le plus efficace.

StreamSearcher.Java en est une implémentation et fait partie de Twitter's elephant-bird projet.

Il n'est pas recommandé d'inclure cette bibliothèque car elle est assez importante pour n'utiliser qu'une seule classe.

import Java.io.IOException;
import Java.io.InputStream;
import Java.util.Arrays;

/**
 * An efficient stream searching class based on the Knuth-Morris-Pratt algorithm.
 * For more on the algorithm works see: http://www.inf.fh-flensburg.de/lang/algorithmen/pattern/kmpen.htm.
 */
public class StreamSearcher
{
    private byte[] pattern_;
    private int[] borders_;

    // An upper bound on pattern length for searching. Results are undefined for longer patterns.
    @SuppressWarnings("unused")
    public static final int MAX_PATTERN_LENGTH = 1024;

    StreamSearcher(byte[] pattern)
    {
        setPattern(pattern);
    }

    /**
     * Sets a new pattern for this StreamSearcher to use.
     *
     * @param pattern the pattern the StreamSearcher will look for in future calls to search(...)
     */
    public void setPattern(byte[] pattern)
    {
        pattern_ = Arrays.copyOf(pattern, pattern.length);
        borders_ = new int[pattern_.length + 1];
        preProcess();
    }

    /**
     * Searches for the next occurrence of the pattern in the stream, starting from the current stream position. Note
     * that the position of the stream is changed. If a match is found, the stream points to the end of the match -- i.e. the
     * byte AFTER the pattern. Else, the stream is entirely consumed. The latter is because InputStream semantics make it difficult to have
     * another reasonable default, i.e. leave the stream unchanged.
     *
     * @return bytes consumed if found, -1 otherwise.
     */
    long search(InputStream stream) throws IOException
    {
        long bytesRead = 0;

        int b;
        int j = 0;

        while ((b = stream.read()) != -1)
        {
            bytesRead++;

            while (j >= 0 && (byte) b != pattern_[j])
            {
                j = borders_[j];
            }
            // Move to the next character in the pattern.
            ++j;

            // If we've matched up to the full pattern length, we found it.  Return,
            // which will automatically save our position in the InputStream at the point immediately
            // following the pattern match.
            if (j == pattern_.length)
            {
                return bytesRead;
            }
        }

        // No dice, Note that the stream is now completely consumed.
        return -1;
    }

    /**
     * Builds up a table of longest "borders" for each prefix of the pattern to find. This table is stored internally
     * and aids in implementation of the Knuth-Moore-Pratt string search.
     * <p>
     * For more information, see: http://www.inf.fh-flensburg.de/lang/algorithmen/pattern/kmpen.htm.
     */
    private void preProcess()
    {
        int i = 0;
        int j = -1;
        borders_[i] = j;
        while (i < pattern_.length)
        {
            while (j >= 0 && pattern_[i] != pattern_[j])
            {
                j = borders_[j];
            }
            borders_[++i] = ++j;
        }
    }
}
2
BullyWiiPlaza
package org.example;

import Java.util.List;

import org.riversun.finbin.BinarySearcher;

public class Sample2 {

    public static void main(String[] args) throws Exception {

        BinarySearcher bs = new BinarySearcher();

        // UTF-8 without BOM
        byte[] srcBytes = "Hello world.It's a small world.".getBytes("utf-8");

        byte[] searchBytes = "world".getBytes("utf-8");

        List<Integer> indexList = bs.searchBytes(srcBytes, searchBytes);

        System.out.println("indexList=" + indexList);
    }
 }

il en résulte

indexList=[6, 25]

Donc, vous pouvez trouver l'index de l'octet [] dans l'octet []

Exemple ici sur Github à: https://github.com/riversun/finbin

1
riversun

Copié presque identique à partir de Java.lang.String.

indexOf(char[],int,int,char[]int,int,int)

static int indexOf(byte[] source, int sourceOffset, int sourceCount, byte[] target, int targetOffset, int targetCount, int fromIndex) {
    if (fromIndex >= sourceCount) {
        return (targetCount == 0 ? sourceCount : -1);
    }
    if (fromIndex < 0) {
        fromIndex = 0;
    }
    if (targetCount == 0) {
        return fromIndex;
    }

    byte first = target[targetOffset];
    int max = sourceOffset + (sourceCount - targetCount);

    for (int i = sourceOffset + fromIndex; i <= max; i++) {
        /* Look for first character. */
        if (source[i] != first) {
            while (++i <= max && source[i] != first)
                ;
        }

        /* Found first character, now look at the rest of v2 */
        if (i <= max) {
            int j = i + 1;
            int end = j + targetCount - 1;
            for (int k = targetOffset + 1; j < end && source[j] == target[k]; j++, k++)
                ;

            if (j == end) {
                /* Found whole string. */
                return i - sourceOffset;
            }
        }
    }
    return -1;
}
1
Gustavo Mendoza

Pour un petit serveur HTTP sur lequel je travaille actuellement, j'ai trouvé le code suivant pour trouver des limites dans une requête multipart/form-data. J'espérais trouver une meilleure solution ici, mais je suppose que je m'en tiendrai. Je pense qu'il est aussi efficace que possible (assez rapide et utilise peu de RAM). Il utilise les octets d'entrée comme tampon en anneau, lit l'octet suivant dès qu'il ne correspond pas à la limite et écrit les données après le premier cycle complet dans le flux de sortie. Bien sûr, il peut être modifié pour les tableaux d'octets au lieu des flux, comme demandé dans la question.

    private boolean multipartUploadParseOutput(InputStream is, OutputStream os, String boundary)
    {
        try
        {
            String n = "--"+boundary;
            byte[] bc = n.getBytes("UTF-8");
            int s = bc.length;
            byte[] b = new byte[s];
            int p = 0;
            long l = 0;
            int c;
            boolean r;
            while ((c = is.read()) != -1)
            {
                b[p] = (byte) c;
                l += 1;
                p = (int) (l % s);
                if (l>p)
                {
                    r = true;
                    for (int i = 0; i < s; i++)
                    {
                        if (b[(p + i) % s] != bc[i])
                        {
                            r = false;
                            break;
                        }
                    }
                    if (r)
                        break;
                    os.write(b[p]);
                }
            }
            os.flush();
            return true;
        } catch(IOException e) {e.printStackTrace();}
        return false;
    }
0
Benjamin Gillhofer